From 6bdedf6627e816101c4ca6cee1c7320c44ac5621 Mon Sep 17 00:00:00 2001 From: anpigon Date: Mon, 15 Aug 2022 16:17:45 +0900 Subject: [PATCH] refactor: google book response --- src/apis/base_api.ts | 49 ++++++++++++++++------------------ src/apis/google_books_api.ts | 47 ++++++++++++++------------------ src/apis/naver_books_api.ts | 32 +++++++++------------- src/views/book_search_modal.ts | 17 ++++++------ 4 files changed, 63 insertions(+), 82 deletions(-) diff --git a/src/apis/base_api.ts b/src/apis/base_api.ts index 085f157..1f71f42 100644 --- a/src/apis/base_api.ts +++ b/src/apis/base_api.ts @@ -9,38 +9,35 @@ export interface BaseBooksApiImpl { getByQuery(query: string): Promise; } -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 { - throw new Error('Method not implemented.'); - } - - async apiGet( - url: string, - params: Record = {}, - headers?: Record, - ): Promise { - 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( + url: string, + params: Record = {}, + headers?: Record, +): Promise { + 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; } diff --git a/src/apis/google_books_api.ts b/src/apis/google_books_api.ts index 92f8163..a8ac456 100644 --- a/src/apis/google_books_api.ts +++ b/src/apis/google_books_api.ts @@ -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 = { @@ -15,35 +14,30 @@ export class GoogleBooksApi extends BaseBooksApi { if (langRestrict) { params['langRestrict'] = langRestrict; } - const searchResults = await super.apiGet(this.API_URL, params); + const searchResults = await apiGet('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), @@ -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', '') ?? ''; } } diff --git a/src/apis/naver_books_api.ts b/src/apis/naver_books_api.ts index efde058..1500d57 100644 --- a/src/apis/naver_books_api.ts +++ b/src/apis/naver_books_api.ts @@ -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 { @@ -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(this.API_URL, params, header); + const searchResults = await apiGet( + '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, @@ -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; } } diff --git a/src/views/book_search_modal.ts b/src/views/book_search_modal.ts index 9f1d6e2..fedefd7 100644 --- a/src/views/book_search_modal.ts +++ b/src/views/book_search_modal.ts @@ -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() { @@ -73,7 +73,6 @@ export class BookSearchModal extends Modal { } onClose() { - const { contentEl } = this; - contentEl.empty(); + this.contentEl.empty(); } }