From d60d0f9bef8d34cfb15a1831900144fac824b307 Mon Sep 17 00:00:00 2001 From: Akiomi Kamakura Date: Wed, 10 Nov 2021 18:43:57 +0900 Subject: [PATCH] Add Daily --- src/api/v3/caching-client.test.ts | 43 ++++++++++++++++++++---- src/api/v3/caching-client.ts | 13 ++------ src/api/v3/client.test.ts | 19 ++++++++--- src/api/v3/client.ts | 13 ++++---- src/entities/v3/daily.ts | 15 +++++++++ src/services/daily-cache.test.ts | 43 +++++++++++++++++++----- src/services/daily-cache.ts | 54 ++++++++++++++++++++++++++++--- 7 files changed, 162 insertions(+), 38 deletions(-) create mode 100644 src/entities/v3/daily.ts diff --git a/src/api/v3/caching-client.test.ts b/src/api/v3/caching-client.test.ts index a832b72..9fd106c 100644 --- a/src/api/v3/caching-client.test.ts +++ b/src/api/v3/caching-client.test.ts @@ -1,10 +1,10 @@ import { CompanyCache } from '~/__mocks__/services/company-cache' +import { DailyCache } from '~/__mocks__/services/daily-cache' import { QuarterCache } from '~/__mocks__/services/quarter-cache' import { CachingBuffettCodeApiClientV3 } from '~/api/v3/caching-client' import { DateParam } from '~/fiscal-periods/date-param' import { YearQuarter } from '~/fiscal-periods/year-quarter' import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' -import { DailyCache } from '~/services/daily-cache' jest.mock('~/api/v3/client', () => jest.requireActual('~/__mocks__/api/v3/client') @@ -52,10 +52,10 @@ describe('daily', () => { expect(DailyCache.get(ticker, date)).toBeNull() const client = new CachingBuffettCodeApiClientV3('token') - const res = client.daily(ticker, date) - expect(res).not.toBeNull() + const daily = client.daily(ticker, date) + expect(daily).not.toBeNull() - expect(DailyCache.get(ticker, date)).toEqual(res) + expect(DailyCache.get(ticker, date)).toEqual(daily) }) test('(cached)', () => { @@ -64,8 +64,8 @@ describe('daily', () => { expect(cached).not.toBeNull() const client = new CachingBuffettCodeApiClientV3('token') - const res = client.daily(ticker, date) - expect(res).toEqual(cached) + const daily = client.daily(ticker, date) + expect(daily).toEqual(cached) expect(DailyCache.get(ticker, date)).toEqual(cached) }) @@ -180,3 +180,34 @@ describe('ondemandQuarter', () => { }) }) }) + +describe('ondemandDaily', () => { + const ticker = '2371' + + beforeAll(() => { + DailyCache.clearAll() + }) + + const period = new DateParam(new Date('2020-09-06')) + + test('(uncached)', () => { + expect(DailyCache.get(ticker, period)).toBeNull() + + const client = new CachingBuffettCodeApiClientV3('token') + const daily = client.ondemandDaily(ticker, period) + expect(daily).not.toBeNull() + + expect(DailyCache.get(ticker, period)).toEqual(daily) + }) + + test('(cached)', () => { + const cached = DailyCache.get(ticker, period) + expect(cached).not.toBeNull() + + const client = new CachingBuffettCodeApiClientV3('token') + const daily = client.ondemandDaily(ticker, period) + expect(daily).toEqual(cached) + + expect(DailyCache.get(ticker, period)).toEqual(cached) + }) +}) diff --git a/src/api/v3/caching-client.ts b/src/api/v3/caching-client.ts index 362a526..367d656 100644 --- a/src/api/v3/caching-client.ts +++ b/src/api/v3/caching-client.ts @@ -1,4 +1,5 @@ import { BuffettCodeApiClientV3 } from '~/api/v3/client' +import { Daily } from '~/entities/v3/daily' import { DateParam } from '~/fiscal-periods/date-param' import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' import { CompanyCache } from '~/services/company-cache' @@ -22,17 +23,13 @@ export class CachingBuffettCodeApiClientV3 extends BuffettCodeApiClientV3 { return company } - daily(ticker: string, date: DateParam): object | null { + daily(ticker: string, date: DateParam): Daily { const cached = DailyCache.get(ticker, date) if (cached) { return cached } const daily = super.daily(ticker, date) - if (!daily) { - return null - } - DailyCache.put(ticker, daily) return daily @@ -56,17 +53,13 @@ export class CachingBuffettCodeApiClientV3 extends BuffettCodeApiClientV3 { return quarter } - ondemandDaily(ticker: string, date: DateParam): object | null { + ondemandDaily(ticker: string, date: DateParam): Daily { const cached = DailyCache.get(ticker, date) if (cached) { return cached } const daily = super.ondemandDaily(ticker, date) - if (!daily) { - return null - } - DailyCache.put(ticker, daily) return daily diff --git a/src/api/v3/client.test.ts b/src/api/v3/client.test.ts index 8f65f96..139d74e 100644 --- a/src/api/v3/client.test.ts +++ b/src/api/v3/client.test.ts @@ -4,6 +4,7 @@ import * as quarter from '~/__mocks__/fixtures/v3/quarter' import { HttpError } from '~/api/http-error' import { useMockedUrlFetchApp } from '~/api/test-helper' import { BuffettCodeApiClientV3 } from '~/api/v3/client' +import { Daily } from '~/entities/v3/daily' import { DateParam } from '~/fiscal-periods/date-param' import { DateRange } from '~/fiscal-periods/date-range' import { YearQuarter } from '~/fiscal-periods/year-quarter' @@ -158,7 +159,7 @@ describe('BuffettCodeApiClientV3', () => { const client = new BuffettCodeApiClientV3('foo') const ticker = '2371' const date = new DateParam(new Date('2021-08-11')) - expect(client.daily(ticker, date)).toEqual(daily['data']) + expect(client.daily(ticker, date)).toEqual(Daily.fromResponse(daily)) expect(mockFetch.mock.calls.length).toBe(1) expect(mockFetch.mock.calls[0].length).toBe(2) expect(mockFetch.mock.calls[0][0]).toBe( @@ -171,12 +172,20 @@ describe('BuffettCodeApiClientV3', () => { }) test('bulkDaily', () => { - const mockFetch = useMockedUrlFetchApp(200, JSON.stringify(daily)) + const bulkDaily = { + data: { + '2020-09-06': daily['data'] + }, + column_description: daily['column_description'] // eslint-disable-line @typescript-eslint/camelcase + } + const mockFetch = useMockedUrlFetchApp(200, JSON.stringify(bulkDaily)) const client = new BuffettCodeApiClientV3('foo') const ticker = '2371' const range = new DateRange(new Date('2021-08-11'), new Date('2021-08-17')) - expect(client.bulkDaily(ticker, range)).toEqual(daily['data']) + expect(client.bulkDaily(ticker, range)).toEqual( + Daily.fromBulkResponse(bulkDaily) + ) expect(mockFetch.mock.calls.length).toBe(1) expect(mockFetch.mock.calls[0].length).toBe(2) expect(mockFetch.mock.calls[0][0]).toBe( @@ -194,7 +203,9 @@ describe('BuffettCodeApiClientV3', () => { const client = new BuffettCodeApiClientV3('foo') const ticker = '2371' const date = new DateParam(new Date('2021-08-11')) - expect(client.ondemandDaily(ticker, date)).toEqual(daily['data']) + expect(client.ondemandDaily(ticker, date)).toEqual( + Daily.fromResponse(daily) + ) expect(mockFetch.mock.calls.length).toBe(1) expect(mockFetch.mock.calls[0].length).toBe(2) expect(mockFetch.mock.calls[0][0]).toBe( diff --git a/src/api/v3/client.ts b/src/api/v3/client.ts index 765a587..3adea13 100644 --- a/src/api/v3/client.ts +++ b/src/api/v3/client.ts @@ -1,5 +1,6 @@ import { HttpError } from '~/api/http-error' import { UrlBuilder } from '~/api/url-builder' +import { Daily } from '~/entities/v3/daily' import { DateParam } from '~/fiscal-periods/date-param' import { DateRange } from '~/fiscal-periods/date-range' import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' @@ -99,7 +100,7 @@ export class BuffettCodeApiClientV3 { return res['data'] } - public daily(ticker: string, date: DateParam): object { + public daily(ticker: string, date: DateParam): Daily { const endpoint = BuffettCodeApiClientV3.baseUrl + '/daily' const builder = new UrlBuilder(endpoint, { ticker, @@ -109,10 +110,10 @@ export class BuffettCodeApiClientV3 { const options = this.defaultOptions() const res = BuffettCodeApiClientV3.request(url, options) - return res['data'] + return Daily.fromResponse(res) } - public bulkDaily(ticker: string, range: DateRange): object[] { + public bulkDaily(ticker: string, range: DateRange): Daily[] { const endpoint = BuffettCodeApiClientV3.baseUrl + '/bulk/daily' const builder = new UrlBuilder(endpoint, { ticker, @@ -123,10 +124,10 @@ export class BuffettCodeApiClientV3 { const options = this.defaultOptions() const res = BuffettCodeApiClientV3.request(url, options) - return res['data'] + return Daily.fromBulkResponse(res) } - public ondemandDaily(ticker: string, date: DateParam): object { + public ondemandDaily(ticker: string, date: DateParam): Daily { const endpoint = BuffettCodeApiClientV3.baseUrl + '/ondemand/daily' const builder = new UrlBuilder(endpoint, { ticker, @@ -136,6 +137,6 @@ export class BuffettCodeApiClientV3 { const options = this.defaultOptions() const res = BuffettCodeApiClientV3.request(url, options) - return res['data'] + return Daily.fromResponse(res) } } diff --git a/src/entities/v3/daily.ts b/src/entities/v3/daily.ts new file mode 100644 index 0000000..0f71817 --- /dev/null +++ b/src/entities/v3/daily.ts @@ -0,0 +1,15 @@ +export class Daily { + constructor(readonly data: object, readonly columnDescription: object) { + // noop + } + + static fromResponse(response: object): Daily { + return new Daily(response['data'], response['column_description']) + } + + static fromBulkResponse(response: object): Daily[] { + return Object.keys(response['data']).map(key => { + return new Daily(response['data'][key], response['column_description']) + }) + } +} diff --git a/src/services/daily-cache.test.ts b/src/services/daily-cache.test.ts index 6adf9cc..4b0d8c6 100644 --- a/src/services/daily-cache.test.ts +++ b/src/services/daily-cache.test.ts @@ -18,30 +18,57 @@ test('key', () => { ) }) -const daily = dailyFixture['data'] +test('columnDescriptionKey', () => { + expect(DailyCache.columnDescriptionKey()).toBe('daily-column-description') +}) + const date = new Date('2020-09-06') beforeEach(() => { jest.clearAllMocks() }) -test('get', () => { - getMock.mockReturnValueOnce(JSON.stringify(daily)) - expect(DailyCache.get('2371', date)).toEqual(daily) - expect(DailyCache.get('9999', date)).toBeNull() +test('getData', () => { + getMock.mockReturnValueOnce(JSON.stringify(dailyFixture['data'])) + expect(DailyCache.getData('2371', date)).toEqual(dailyFixture['data']) + expect(DailyCache.getData('9999', date)).toBeNull() expect(getMock).toBeCalledTimes(2) expect(getMock).nthCalledWith(1, 'daily-2371-2020-09-06') expect(getMock).nthCalledWith(2, 'daily-9999-2020-09-06') }) -test('put', () => { - DailyCache.put('2371', daily) +test('getColumnDescription', () => { + getMock.mockReturnValueOnce( + JSON.stringify(dailyFixture['column_description']) + ) + + expect(DailyCache.getColumnDescription()).toEqual( + dailyFixture['column_description'] + ) + + expect(getMock).toBeCalledTimes(1) + expect(getMock).toBeCalledWith('daily-column-description') +}) + +test('putData', () => { + DailyCache.putData('2371', dailyFixture['data']) expect(putMock).toBeCalledTimes(1) expect(putMock).toBeCalledWith( 'daily-2371-2020-09-06', - JSON.stringify(daily), + JSON.stringify(dailyFixture['data']), + 21600 + ) +}) + +test('putColumnDescription', () => { + DailyCache.putColumnDescription(dailyFixture['column_description']) + + expect(putMock).toBeCalledTimes(1) + expect(putMock).toBeCalledWith( + 'daily-column-description', + JSON.stringify(dailyFixture['column_description']), 21600 ) }) diff --git a/src/services/daily-cache.ts b/src/services/daily-cache.ts index 36cce4d..51660c3 100644 --- a/src/services/daily-cache.ts +++ b/src/services/daily-cache.ts @@ -1,3 +1,4 @@ +import { Daily } from '~/entities/v3/daily' import { DateParam } from '~/fiscal-periods/date-param' export class DailyCache { @@ -17,7 +18,11 @@ export class DailyCache { return `${this.prefix}-${ticker}-${date}` } - static get(ticker: string, date: Date | DateParam): object | null { + static columnDescriptionKey(): string { + return `${this.prefix}-column-description` + } + + static getData(ticker: string, date: Date | DateParam): object | null { const cache = CacheService.getUserCache() const key = this.key(ticker, date) const cached = cache.get(key) @@ -28,10 +33,51 @@ export class DailyCache { return JSON.parse(cached) } - static put(ticker: string, daily: object, expirationInSeconds = 21600): void { + static getColumnDescription(): object | null { + const cache = CacheService.getUserCache() + const cached = cache.get(this.columnDescriptionKey()) + if (!cached) { + return null + } + + return JSON.parse(cached) + } + + static get(ticker: string, date: Date | DateParam): Daily | null { + const cachedData = this.getData(ticker, date) + const cachedColumnDescription = this.getColumnDescription() + if (!cachedData || !cachedColumnDescription) { + return null + } + + return new Daily(cachedData, cachedColumnDescription) + } + + static putData( + ticker: string, + data: object, + expirationInSeconds = 21600 + ): void { const cache = CacheService.getUserCache() - const date = new Date(daily['day']) + const date = new Date(data['day']) const key = this.key(ticker, date) - cache.put(key, JSON.stringify(daily), expirationInSeconds) + cache.put(key, JSON.stringify(data), expirationInSeconds) + } + + static putColumnDescription( + columnDescription: object, + expirationInSeconds = 21600 + ): void { + const cache = CacheService.getUserCache() + cache.put( + this.columnDescriptionKey(), + JSON.stringify(columnDescription), + expirationInSeconds + ) + } + + static put(ticker: string, daily: Daily, expirationInSeconds = 21600): void { + this.putData(ticker, daily.data, expirationInSeconds) + this.putColumnDescription(daily.columnDescription, expirationInSeconds) } }