Skip to content

Commit

Permalink
feat: GoogleAPIsから書影を取得 (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
SnO₂WMaN authored Feb 13, 2021
1 parent 0f54d8f commit 540956e
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 31 deletions.
2 changes: 2 additions & 0 deletions src/books/books.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Module} from '@nestjs/common';
import {ConfigModule} from '@nestjs/config';
import {ExcludeModule} from '../exclude/exclude.module';
import {GoogleAPIsModule} from '../googleapis/googleapis.module';
import {OpenBDModule} from '../openbd/openbd.module';
import {RakutenModule} from '../rakuten/rakuten.module';
import {RedisCacheModule} from '../redis-cache/redis-cache.module';
Expand All @@ -15,6 +16,7 @@ import {BooksService} from './books.service';
ExcludeModule,
OpenBDModule,
RakutenModule,
GoogleAPIsModule,
],
providers: [BooksService, BooksResolver],
exports: [BooksService],
Expand Down
11 changes: 11 additions & 0 deletions src/books/books.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Inject, Injectable} from '@nestjs/common';
import {ConfigType} from '@nestjs/config';
import {ExcludeService} from '../exclude/exclude.service';
import {GoogleAPIsService} from '../googleapis/googleapis.service';
import {OpenBDService} from '../openbd/openbd.service';
import {RakutenService} from '../rakuten/rakuten.service';
import {RedisCacheService} from '../redis-cache/redis-cache.service';
Expand All @@ -17,6 +18,7 @@ export class BooksService {

private readonly oepnBDService: OpenBDService,
private readonly rakutenService: RakutenService,
private readonly googleAPIsService: GoogleAPIsService,
) {}

async getFromISBN(isbn: string): Promise<string | null> {
Expand All @@ -38,6 +40,15 @@ export class BooksService {
return rakuten;
}

const googleapis = await this.googleAPIsService.getBookCover(dehyphenized);
if (
googleapis &&
!(await this.excludeService.canExcludeFromUrl(googleapis))
) {
await this.cache.set(cachedKey, googleapis);
return googleapis;
}

return null;
}

Expand Down
77 changes: 46 additions & 31 deletions src/books/test/small/books.service.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {Test, TestingModule} from '@nestjs/testing';
import {ExcludeService} from '../../../exclude/exclude.service';
import {GoogleAPIsService} from '../../../googleapis/googleapis.service';
import {OpenBDService} from '../../../openbd/openbd.service';
import {RakutenService} from '../../../rakuten/rakuten.service';
import {RedisCacheService} from '../../../redis-cache/redis-cache.service';
Expand All @@ -8,6 +9,8 @@ import {BooksService} from '../../books.service';

jest.mock('../../../openbd/openbd.service');
jest.mock('../../../rakuten/rakuten.service');
jest.mock('../../../googleapis/googleapis.service');

jest.mock('../../../exclude/exclude.service');
jest.mock('../../../redis-cache/redis-cache.service');

Expand All @@ -18,8 +21,10 @@ describe(BooksService.name, () => {

let cacheManager: RedisCacheService;
let excludeService: ExcludeService;

let openbdService: OpenBDService;
let rakutenService: RakutenService;
let googleApisService: GoogleAPIsService;

beforeEach(async () => {
module = await Test.createTestingModule({
Expand All @@ -33,15 +38,18 @@ describe(BooksService.name, () => {
BooksService,
OpenBDService,
RakutenService,
GoogleAPIsService,
],
}).compile();

booksService = module.get<BooksService>(BooksService);

cacheManager = module.get<RedisCacheService>(RedisCacheService);
excludeService = module.get<ExcludeService>(ExcludeService);

openbdService = module.get<OpenBDService>(OpenBDService);
rakutenService = module.get<RakutenService>(RakutenService);
googleApisService = module.get<GoogleAPIsService>(GoogleAPIsService);
});

afterEach(async () => {
Expand All @@ -58,41 +66,48 @@ describe(BooksService.name, () => {

describe('getCover()', () => {
it.each([
[{openBD: 'openBD'}, 'openBD'],
[{rakuten: 'rakuten'}, 'rakuten'],
[{googleAPIs: 'googleAPIs'}, 'googleAPIs'],
[
// openBDを優先
'https://cover.openbd.jp/9784781618968.jpg',
'https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2460/9784832272460.jpg',
'https://cover.openbd.jp/9784781618968.jpg',
],
[
null,
'https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2460/9784832272460.jpg',
'https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2460/9784832272460.jpg',
],
[
'https://cover.openbd.jp/9784781618968.jpg',
null,
'https://cover.openbd.jp/9784781618968.jpg',
{openBD: 'openBD', rakuten: 'rakuten', googleAPIs: 'googleAPIs'},
'openBD',
],
[null, null, null],
])('未キャッシュ(%#)', async (openBD, rakuten, expected) => {
jest.spyOn(cacheManager, 'get').mockResolvedValueOnce(undefined);
jest.spyOn(excludeService, 'canExcludeFromUrl').mockResolvedValue(false);

const set = jest.fn((isbn, result) => result);
jest.spyOn(cacheManager, 'set').mockImplementationOnce(set);

jest.spyOn(openbdService, 'getBookCover').mockResolvedValueOnce(openBD);
jest.spyOn(rakutenService, 'getBookCover').mockResolvedValueOnce(rakuten);

const actual = await booksService.getCover({isbn: '9784781618968'});

expect(actual).toBe(expected);
if (expected) expect(set).toHaveBeenCalled();
});
[{rakuten: 'rakuten', googleAPIs: 'googleAPIs'}, 'rakuten'],
[{}, null],
])(
'未キャッシュ(%#)',
async (
mock: {openBD?: string; rakuten?: string; googleAPIs?: string},
expected,
) => {
jest.spyOn(cacheManager, 'get').mockResolvedValueOnce(undefined);
jest
.spyOn(excludeService, 'canExcludeFromUrl')
.mockResolvedValue(false);

const set = jest.fn((isbn, result) => result);
jest.spyOn(cacheManager, 'set').mockImplementationOnce(set);

jest
.spyOn(openbdService, 'getBookCover')
.mockResolvedValueOnce(mock.openBD || null);
jest
.spyOn(rakutenService, 'getBookCover')
.mockResolvedValueOnce(mock.rakuten || null);
jest
.spyOn(googleApisService, 'getBookCover')
.mockResolvedValueOnce(mock.googleAPIs || null);

const actual = await booksService.getCover({isbn: '9784781618968'});

expect(actual).toBe(expected);
if (expected) expect(set).toHaveBeenCalled();
},
);

it('キャッシュ済み', async () => {
const expected = 'https://cover.openbd.jp/9784781618968.jpg';
const expected = 'openBD';

jest.spyOn(cacheManager, 'get').mockResolvedValueOnce(expected);

Expand Down
5 changes: 5 additions & 0 deletions src/googleapis/googleapis.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {registerAs} from '@nestjs/config';

export const GoogleAPIsConfig = registerAs('googleapis', () => ({
endpoint: `https://www.googleapis.com/books/v1/volumes`,
}));
11 changes: 11 additions & 0 deletions src/googleapis/googleapis.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {HttpModule, Module} from '@nestjs/common';
import {ConfigModule} from '@nestjs/config';
import {GoogleAPIsConfig} from './googleapis.config';
import {GoogleAPIsService} from './googleapis.service';

@Module({
imports: [ConfigModule.forFeature(GoogleAPIsConfig), HttpModule],
providers: [GoogleAPIsService],
exports: [GoogleAPIsService],
})
export class GoogleAPIsModule {}
42 changes: 42 additions & 0 deletions src/googleapis/googleapis.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {HttpService, Inject, Injectable} from '@nestjs/common';
import {ConfigType} from '@nestjs/config';
import {GoogleAPIsConfig} from './googleapis.config';

export interface APIPayload {
items?: {
volumeInfo?: {
imageLinks?: {
smallThumbnail?: string;
thumbnail?: string;
};
};
}[];
}

@Injectable()
export class GoogleAPIsService {
constructor(
@Inject(GoogleAPIsConfig.KEY)
private configService: ConfigType<typeof GoogleAPIsConfig>,
private readonly httpService: HttpService,
) {}

async getBookCover(isbn: string): Promise<string | null> {
return this.httpService
.get<APIPayload>(this.configService.endpoint, {
params: {
// eslint-disable-next-line id-length
q: `isbn:${isbn}`,
},
})
.toPromise()
.then(({data}) => {
return (
data?.items?.[0]?.volumeInfo?.imageLinks?.thumbnail ||
data?.items?.[0]?.volumeInfo?.imageLinks?.smallThumbnail ||
null
);
})
.catch(() => null);
}
}

0 comments on commit 540956e

Please sign in to comment.