Skip to content

Commit

Permalink
refactor: google book response
Browse files Browse the repository at this point in the history
  • Loading branch information
anpigon committed Aug 17, 2022
1 parent 02a70f2 commit 6bdedf6
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 82 deletions.
49 changes: 23 additions & 26 deletions src/apis/base_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,35 @@ export interface BaseBooksApiImpl {
getByQuery(query: string): Promise<Book[]>;
}

export function getServiceProvider(settings: BookSearchPluginSettings): BaseBooksApiImpl {
export function factoryServiceProvider(settings: BookSearchPluginSettings): BaseBooksApiImpl {
if (settings.serviceProvider === ServiceProvider.google) {
return new GoogleBooksApi();
}
if (settings.serviceProvider === ServiceProvider.naver) {
if (!settings.naverClientId || !settings.naverClientSecret) {
throw new Error('네이버 개발자센터에서 `Client ID`와 `Client Secret`를 발급받아 설정해주세요.');
}
return new NaverBooksApi(settings.naverClientId, settings.naverClientSecret);
}
}

export class BaseBooksApi implements BaseBooksApiImpl {
getByQuery(_query: string): Promise<Book[]> {
throw new Error('Method not implemented.');
}

async apiGet<T>(
url: string,
params: Record<string, string | number> = {},
headers?: Record<string, string>,
): Promise<T> {
const apiURL = new URL(url);
Object.entries(params).forEach(([key, value]) => {
apiURL.searchParams.append(key, value?.toString());
});
const res = await request({
url: apiURL.href,
method: 'GET',
headers: {
Accept: '*/*',
'Content-Type': 'application/json; charset=utf-8',
...headers,
},
});
return JSON.parse(res) as T;
}
export async function apiGet<T>(
url: string,
params: Record<string, string | number> = {},
headers?: Record<string, string>,
): Promise<T> {
const apiURL = new URL(url);
Object.entries(params).forEach(([key, value]) => {
apiURL.searchParams.append(key, value?.toString());
});
const res = await request({
url: apiURL.href,
method: 'GET',
headers: {
Accept: '*/*',
'Content-Type': 'application/json; charset=utf-8',
...headers,
},
});
return JSON.parse(res) as T;
}
47 changes: 20 additions & 27 deletions src/apis/google_books_api.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Book } from '@models/book.model';
import { apiGet, BaseBooksApiImpl } from '@apis/base_api';
import { GoogleBooksResponse, VolumeInfo } from './models/google_books_response';
import { BaseBooksApi } from './base_api';
export class GoogleBooksApi extends BaseBooksApi {
private readonly API_URL = 'https://www.googleapis.com/books/v1/volumes';

export class GoogleBooksApi implements BaseBooksApiImpl {
async getByQuery(query: string) {
try {
const params = {
Expand All @@ -15,35 +14,30 @@ export class GoogleBooksApi extends BaseBooksApi {
if (langRestrict) {
params['langRestrict'] = langRestrict;
}
const searchResults = await super.apiGet<GoogleBooksResponse>(this.API_URL, params);
const searchResults = await apiGet<GoogleBooksResponse>('https://www.googleapis.com/books/v1/volumes', params);
if (searchResults.totalItems == 0) {
throw new Error('No results found.');
}
return searchResults.items.map(({ volumeInfo }) => this.formatForSuggestion(volumeInfo));
return searchResults.items.map(({ volumeInfo }) => this.createBookItem(volumeInfo));
} catch (error) {
console.warn(error);
throw error;
}
}

getISBN(item: VolumeInfo) {
let ISBN10 = '';
let ISBN13 = '';
let isbn10_data, isbn13_data;

if (item.industryIdentifiers) {
isbn10_data = item.industryIdentifiers.find(element => element.type == 'ISBN_10');
isbn13_data = item.industryIdentifiers.find(element => element.type == 'ISBN_13');
}

if (isbn10_data) ISBN10 = isbn10_data.identifier.trim();
if (isbn13_data) ISBN13 = isbn13_data.identifier.trim();

return { ISBN10, ISBN13 };
getISBN(industryIdentifiers: VolumeInfo['industryIdentifiers']) {
return industryIdentifiers?.reduce((result, item) => {
if (item.type == 'ISBN_10') {
result['isbn10'] = item.identifier.trim();
}
if (item.type == 'ISBN_13') {
result['isbn13'] = item.identifier.trim();
}
return result;
}, {});
}

formatForSuggestion(item: VolumeInfo): Book {
const ISBN = this.getISBN(item);
createBookItem(item: VolumeInfo): Book {
const book: Book = {
title: item.title,
author: this.formatList(item.authors),
Expand All @@ -52,16 +46,15 @@ export class GoogleBooksApi extends BaseBooksApi {
totalPage: item.pageCount,
coverUrl: `${item.imageLinks?.thumbnail ?? ''}`.replace('http:', 'https:'),
publishDate: item.publishedDate ? `${new Date(item.publishedDate).getFullYear()}` : '',
isbn10: ISBN.ISBN10,
isbn13: ISBN.ISBN13,
...this.getISBN(item.industryIdentifiers),
};
return book;
}

formatList(list?: string[]) {
if (!list || list.length === 0 || list[0] == 'N/A') return '';
if (list.length === 1) return list[0] ?? '';

return list.map(item => `${item.trim()}`).join(', ');
if (list?.length > 1) {
return list.map(item => `${item.trim()}`).join(', ');
}
return list?.[0]?.replace('N/A', '') ?? '';
}
}
32 changes: 12 additions & 20 deletions src/apis/naver_books_api.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { Book } from '@models/book.model';
import { BaseBooksApi } from './base_api';
import { apiGet, BaseBooksApiImpl } from './base_api';
import { NaverBookItem, NaverBooksResponse } from './models/naver_books_response';

export class NaverBooksApi extends BaseBooksApi {
private readonly API_URL = 'https://openapi.naver.com/v1/search/book.json';

constructor(private readonly clientId, private readonly clientSecret: string) {
super();
if (!clientId || !clientSecret) {
throw new Error('네이버 개발자센터에서 발급받은 `Client ID`와 `Client Secret`이 설정되지 않았습니다.');
}
}
export class NaverBooksApi implements BaseBooksApiImpl {
constructor(private readonly clientId, private readonly clientSecret: string) {}

async getByQuery(query: string) {
try {
Expand All @@ -19,27 +12,27 @@ export class NaverBooksApi extends BaseBooksApi {
display: 50,
sort: 'sim',
};
const langRestrict = window.moment.locale();
if (langRestrict) {
params['langRestrict'] = langRestrict;
}
const header = {
'X-Naver-Client-Id': this.clientId,
'X-Naver-Client-Secret': this.clientSecret,
};
const searchResults = await super.apiGet<NaverBooksResponse>(this.API_URL, params, header);
const searchResults = await apiGet<NaverBooksResponse>(
'https://openapi.naver.com/v1/search/book.json',
params,
header,
);
if (searchResults.total == 0) {
throw new Error('No results found.');
}
return searchResults.items.map(this.formatForSuggestion);
return searchResults.items.map(this.createBookItem);
} catch (error) {
console.warn(error);
throw error;
}
}

formatForSuggestion(item: NaverBookItem): Book {
const book: Book = {
createBookItem(item: NaverBookItem) {
return {
title: item.title,
author: item.author,
publisher: item.publisher,
Expand All @@ -49,7 +42,6 @@ export class NaverBooksApi extends BaseBooksApi {
description: item.description,
isbn: item.isbn,
...(item.isbn?.length >= 13 ? { isbn13: item.isbn } : { isbn10: item.isbn }),
};
return book;
} as Book;
}
}
17 changes: 8 additions & 9 deletions src/views/book_search_modal.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { ButtonComponent, Modal, Setting, TextComponent } from 'obsidian';
import { Book } from '@models/book.model';
import { BaseBooksApiImpl, getServiceProvider } from '@apis/base_api';
import { BaseBooksApiImpl, factoryServiceProvider } from '@apis/base_api';
import BookSearchPlugin from '@src/main';

export class BookSearchModal extends Modal {
query: string;
isBusy: boolean;
okBtnRef: ButtonComponent;
onSubmit: (err: Error, result?: Book[]) => void;
serviceProvider: BaseBooksApiImpl;
private query: string;
private isBusy: boolean;
private okBtnRef: ButtonComponent;
private onSubmit: (err: Error, result?: Book[]) => void;
private serviceProvider: BaseBooksApiImpl;

constructor(context: BookSearchPlugin, query: string, onSubmit?: (err: Error, result?: Book[]) => void) {
super(context.app);
this.query = query;
this.onSubmit = onSubmit;
this.serviceProvider = getServiceProvider(context.settings);
this.serviceProvider = factoryServiceProvider(context.settings);
}

async searchBook() {
Expand Down Expand Up @@ -73,7 +73,6 @@ export class BookSearchModal extends Modal {
}

onClose() {
const { contentEl } = this;
contentEl.empty();
this.contentEl.empty();
}
}

0 comments on commit 6bdedf6

Please sign in to comment.