From e6b68706db25d1ff146947ac449151f53e8fed4b Mon Sep 17 00:00:00 2001 From: Akiomi Kamakura Date: Wed, 19 Jan 2022 18:45:39 +0900 Subject: [PATCH] Improve DateParam --- src/__mocks__/services/daily-cache.ts | 2 +- src/api/company-service.test.ts | 10 ++--- src/api/v3/caching-client.test.ts | 6 +-- src/api/v3/client.test.ts | 4 +- src/api/v3/client.ts | 4 +- src/custom-functions/v3/bcode-daily.test.ts | 2 +- src/custom-functions/v3/bcode.ts | 3 +- src/fiscal-periods/date-param.test.ts | 26 ++++++----- src/fiscal-periods/date-param.ts | 49 ++++++++++++++------- src/fiscal-periods/period-parser.test.ts | 17 +++++-- src/fiscal-periods/period-parser.ts | 6 ++- src/services/daily-cache.test.ts | 4 +- src/services/daily-cache.ts | 4 +- 13 files changed, 84 insertions(+), 53 deletions(-) diff --git a/src/__mocks__/services/daily-cache.ts b/src/__mocks__/services/daily-cache.ts index 2b5b0c2..2a2847c 100644 --- a/src/__mocks__/services/daily-cache.ts +++ b/src/__mocks__/services/daily-cache.ts @@ -16,7 +16,7 @@ export class DailyCache { static getData(ticker: string, date: Date | DateParam): object | null { if (date instanceof Date) { - date = new DateParam(date) + date = DateParam.from(date) } const cached = DailyCache.cache[`${ticker}-${date}`] diff --git a/src/api/company-service.test.ts b/src/api/company-service.test.ts index 99f6478..e5248d8 100644 --- a/src/api/company-service.test.ts +++ b/src/api/company-service.test.ts @@ -72,9 +72,9 @@ test('isOndemandDailyApiPeriod', () => { const client = new CachingBuffettCodeApiClientV3('token') const service = new CompanyService('2371', client) - expect(service.isOndemandDailyApiPeriod(new DateParam(new Date('2003-10-10')))).toBe(true) - expect(service.isOndemandDailyApiPeriod(new DateParam(new Date('2016-11-12')))).toBe(true) - expect(service.isOndemandDailyApiPeriod(new DateParam(new Date('2016-11-13')))).toBe(false) - expect(service.isOndemandDailyApiPeriod(new DateParam(new Date()))).toBe(false) - expect(service.isOndemandDailyApiPeriod(new DateParam('latest'))).toBe(false) + expect(service.isOndemandDailyApiPeriod(DateParam.from(new Date('2003-10-10')))).toBe(true) + expect(service.isOndemandDailyApiPeriod(DateParam.from(new Date('2016-11-12')))).toBe(true) + expect(service.isOndemandDailyApiPeriod(DateParam.from(new Date('2016-11-13')))).toBe(false) + expect(service.isOndemandDailyApiPeriod(DateParam.from(new Date()))).toBe(false) + expect(service.isOndemandDailyApiPeriod(DateParam.from('latest'))).toBe(false) }) diff --git a/src/api/v3/caching-client.test.ts b/src/api/v3/caching-client.test.ts index c70082e..9006d70 100644 --- a/src/api/v3/caching-client.test.ts +++ b/src/api/v3/caching-client.test.ts @@ -45,7 +45,7 @@ describe('daily', () => { const ticker = '2371' test('(uncached)', () => { - const date = new DateParam(new Date('2020-09-06')) + const date = DateParam.from(new Date('2020-09-06')) expect(DailyCache.get(ticker, date)).toBeNull() const client = new CachingBuffettCodeApiClientV3('token') @@ -56,7 +56,7 @@ describe('daily', () => { }) test('(cached)', () => { - const date = new DateParam(new Date('2020-09-06')) + const date = DateParam.from(new Date('2020-09-06')) const cached = DailyCache.get(ticker, date) expect(cached).not.toBeNull() @@ -185,7 +185,7 @@ describe('ondemandDaily', () => { DailyCache.clearAll() }) - const period = new DateParam(new Date('2020-09-06')) + const period = DateParam.from(new Date('2020-09-06')) test('(uncached)', () => { expect(DailyCache.get(ticker, period)).toBeNull() diff --git a/src/api/v3/client.test.ts b/src/api/v3/client.test.ts index 52b4cc3..d1398b2 100644 --- a/src/api/v3/client.test.ts +++ b/src/api/v3/client.test.ts @@ -167,7 +167,7 @@ describe('BuffettCodeApiClientV3', () => { const client = new BuffettCodeApiClientV3('foo') const ticker = '2371' - const date = new DateParam(new Date('2021-08-11')) + const date = DateParam.from(new Date('2021-08-11')) expect(client.daily(ticker, date)).toEqual(Daily.fromResponse(daily)) expect(mockFetch.mock.calls.length).toBe(1) expect(mockFetch.mock.calls[0].length).toBe(2) @@ -207,7 +207,7 @@ describe('BuffettCodeApiClientV3', () => { const client = new BuffettCodeApiClientV3('foo') const ticker = '2371' - const date = new DateParam(new Date('2021-08-11')) + const date = DateParam.from(new Date('2021-08-11')) expect(client.ondemandDaily(ticker, date)).toEqual(Daily.fromResponse(daily)) expect(mockFetch.mock.calls.length).toBe(1) expect(mockFetch.mock.calls[0].length).toBe(2) diff --git a/src/api/v3/client.ts b/src/api/v3/client.ts index 00a1dce..2cee063 100644 --- a/src/api/v3/client.ts +++ b/src/api/v3/client.ts @@ -114,8 +114,8 @@ export class BuffettCodeApiClientV3 { const endpoint = BuffettCodeApiClientV3.baseUrl + '/bulk/daily' const builder = new UrlBuilder(endpoint, { ticker, - from: new DateParam(range.from).toString(), - to: new DateParam(range.to).toString() + from: DateParam.from(range.from).toString(), + to: DateParam.from(range.to).toString() }) const url = builder.toString() const options = this.defaultOptions() diff --git a/src/custom-functions/v3/bcode-daily.test.ts b/src/custom-functions/v3/bcode-daily.test.ts index ba4a6b3..da1a74b 100644 --- a/src/custom-functions/v3/bcode-daily.test.ts +++ b/src/custom-functions/v3/bcode-daily.test.ts @@ -10,7 +10,7 @@ jest.mock('~/services/daily-cache', () => jest.requireActual('~/__mocks__/servic test('bcodeDaily', () => { const ticker = '2371' - const date = new DateParam(new Date('2020-09-06')) + const date = DateParam.from(new Date('2020-09-06')) const propertyName = 'market_capital' expect(DailyCache.get(ticker, date)).toBeNull() diff --git a/src/custom-functions/v3/bcode.ts b/src/custom-functions/v3/bcode.ts index 759f1cb..76051cc 100644 --- a/src/custom-functions/v3/bcode.ts +++ b/src/custom-functions/v3/bcode.ts @@ -4,7 +4,6 @@ import { ApiResponseError, OndemandApiNotEnabledError, UnsupportedTickerError } import { bcodeDaily } from '~/custom-functions/v3/bcode-daily' import { bcodeQuarter } from '~/custom-functions/v3/bcode-quarter' import { BcodeResult } from '~/custom-functions/v3/bcode-result' -import { DateParam } from '~/fiscal-periods/date-param' import { InvalidLYLQError, InvalidYearError, InvalidQuarterError } from '~/fiscal-periods/error' import { PeriodParser } from '~/fiscal-periods/period-parser' import { Setting } from '~/setting' @@ -77,7 +76,7 @@ export function bcode( const client = new CachingBuffettCodeApiClientV3(setting.token) const parsedPeriod = PeriodParser.parse(period) let result: BcodeResult - if (parsedPeriod instanceof DateParam) { + if (PeriodParser.isDateParam(parsedPeriod)) { result = bcodeDaily( client, ticker, diff --git a/src/fiscal-periods/date-param.test.ts b/src/fiscal-periods/date-param.test.ts index 314e873..eacef3a 100644 --- a/src/fiscal-periods/date-param.test.ts +++ b/src/fiscal-periods/date-param.test.ts @@ -1,25 +1,29 @@ -import { DateParam } from '~/fiscal-periods/date-param' +import { DateParam, DateParamDate, DateParamLatest } from '~/fiscal-periods/date-param' import { ParseError } from '~/fiscal-periods/error' test('toString', () => { - expect(new DateParam('latest').toString()).toEqual('latest') - expect(new DateParam(new Date('2020-09-06')).toString()).toEqual('2020-09-06') + expect(DateParam.from('latest').toString()).toEqual('latest') + expect(DateParam.from(new Date('2020-09-06')).toString()).toEqual('2020-09-06') }) test('toDate', () => { - expect(() => new DateParam('latest').toDate()).toThrow(Error) - expect(new DateParam(new Date('2020-09-06')).toDate()).toEqual(new Date('2020-09-06')) + expect(new DateParamDate(new Date('2020-09-06')).toDate()).toEqual(new Date('2020-09-06')) }) test('isLatest', () => { - expect(new DateParam('latest').isLatest()).toBeTruthy() - expect(new DateParam(new Date('2020-09-06')).isLatest()).toBeFalsy() + expect(DateParam.from('latest').isLatest()).toBeTruthy() + expect(DateParam.from(new Date('2020-09-06')).isLatest()).toBeFalsy() }) -test('parse', () => { - expect(DateParam.parse('2020-09-06')).toEqual(new DateParam(new Date('2020-09-06'))) - expect(DateParam.parse('latest')).toEqual(new DateParam('latest')) - expect(DateParam.parse('Latest')).toEqual(new DateParam('latest')) +test('DateParam.from', () => { + expect(DateParam.from(new Date('2020-09-06'))).toEqual(new DateParamDate(new Date('2020-09-06'))) + expect(DateParam.from('latest')).toEqual(new DateParamLatest('latest')) +}) + +test('DateParam.parse', () => { + expect(DateParam.parse('2020-09-06')).toEqual(new DateParamDate(new Date('2020-09-06'))) + expect(DateParam.parse('latest')).toEqual(new DateParamLatest('latest')) + expect(DateParam.parse('Latest')).toEqual(new DateParamLatest('latest')) expect(() => DateParam.parse('foo')).toThrow(ParseError) expect(() => DateParam.parse('2020-9-6')).toThrow(ParseError) expect(() => DateParam.parse('2020/09/06')).toThrow(ParseError) diff --git a/src/fiscal-periods/date-param.ts b/src/fiscal-periods/date-param.ts index 0ff0792..ae328e9 100644 --- a/src/fiscal-periods/date-param.ts +++ b/src/fiscal-periods/date-param.ts @@ -1,32 +1,47 @@ import { ParseError } from '~/fiscal-periods/error' -export class DateParam { - constructor(public date: Date | 'latest') {} +export class DateParamDate { + constructor(readonly date: Date) {} public toString(): string { - if (this.date instanceof Date) { - return this.date.toISOString().substring(0, 10) - } else { - return this.date - } + return this.date.toISOString().substring(0, 10) + } + + public isLatest(): this is DateParamLatest { + return false } public toDate(): Date { - if (this.date === 'latest') { - throw new Error('This cannot convert to Date') - } else { - return this.date - } + return this.date } +} + +export class DateParamLatest { + constructor(readonly date: 'latest') {} - public isLatest(): boolean { - return this.date === 'latest' + public toString(): string { + return this.date + } + + public isLatest(): this is DateParamLatest { + return true } +} - static parse(str: string): DateParam { +export type DateParam = DateParamDate | DateParamLatest + +export const DateParam = { + from(date: Date | 'latest'): DateParam { + if (date === 'latest') { + return new DateParamLatest(date) + } else { + return new DateParamDate(date) + } + }, + parse(str: string): DateParam { str = str.toLowerCase() if (str == 'latest') { - return new DateParam(str) + return new DateParamLatest(str) } const matches = str.match(/^(\d{4})-(\d{2})-(\d{2})$/) @@ -34,6 +49,6 @@ export class DateParam { throw new ParseError(`Invalid date format: ${str}`) } - return new DateParam(new Date(str)) + return new DateParamDate(new Date(str)) } } diff --git a/src/fiscal-periods/period-parser.test.ts b/src/fiscal-periods/period-parser.test.ts index e79bf6c..66875ef 100644 --- a/src/fiscal-periods/period-parser.test.ts +++ b/src/fiscal-periods/period-parser.test.ts @@ -8,7 +8,7 @@ import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' const LY = new LyWithOffset() const LQ = new LqWithOffset() -test('parse', () => { +test('PeriodParser.parse', () => { expect(PeriodParser.parse('2020Q3')).toEqual(new YearQuarterParam(2020, 3)) expect(PeriodParser.parse('2020LQ')).toEqual(new YearQuarterParam(2020, LQ)) expect(PeriodParser.parse('LYQ3')).toEqual(new YearQuarterParam(LY, 3)) @@ -18,10 +18,19 @@ test('parse', () => { expect(PeriodParser.parse('2020LQ-1')).toEqual(new YearQuarterParam(2020, new LqWithOffset(-1))) expect(PeriodParser.parse('LY-1LQ-1')).toEqual(new YearQuarterParam(new LyWithOffset(-1), new LqWithOffset(-1))) expect(PeriodParser.parse('ly-1lq-1')).toEqual(new YearQuarterParam(new LyWithOffset(-1), new LqWithOffset(-1))) - expect(PeriodParser.parse('2020-09-06')).toEqual(new DateParam(new Date('2020-09-06'))) - expect(PeriodParser.parse('latest')).toEqual(new DateParam('latest')) - expect(PeriodParser.parse('Latest')).toEqual(new DateParam('latest')) + expect(PeriodParser.parse('2020-09-06')).toEqual(DateParam.from(new Date('2020-09-06'))) + expect(PeriodParser.parse('latest')).toEqual(DateParam.from('latest')) + expect(PeriodParser.parse('Latest')).toEqual(DateParam.from('latest')) expect(() => PeriodParser.parse('foo')).toThrow(ParseError) expect(() => PeriodParser.parse('2020/09/06')).toThrow(ParseError) expect(() => PeriodParser.parse('0Q1')).toThrow(ParseError) }) + +test('PeriodParser.isDateParam', () => { + expect(PeriodParser.isDateParam(DateParam.from('latest'))).toBeTruthy() + expect(PeriodParser.isDateParam(DateParam.from(new Date()))).toBeTruthy() + expect(PeriodParser.isDateParam(new YearQuarterParam(2020, 3))).toBeFalsy() + expect(PeriodParser.isDateParam(new YearQuarterParam(2020, LQ))).toBeFalsy() + expect(PeriodParser.isDateParam(new YearQuarterParam(LY, 3))).toBeFalsy() + expect(PeriodParser.isDateParam(new YearQuarterParam(LY, LQ))).toBeFalsy() +}) diff --git a/src/fiscal-periods/period-parser.ts b/src/fiscal-periods/period-parser.ts index 6c2553d..98e63e9 100644 --- a/src/fiscal-periods/period-parser.ts +++ b/src/fiscal-periods/period-parser.ts @@ -1,4 +1,4 @@ -import { DateParam } from '~/fiscal-periods/date-param' +import { DateParam, DateParamDate, DateParamLatest } from '~/fiscal-periods/date-param' import { ParseError } from '~/fiscal-periods/error' import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' @@ -22,4 +22,8 @@ export class PeriodParser { throw new ParseError(`Invalid period format: ${str}`) } + + static isDateParam(period: DateParam | YearQuarterParam): period is DateParam { + return period instanceof DateParamDate || period instanceof DateParamLatest + } } diff --git a/src/services/daily-cache.test.ts b/src/services/daily-cache.test.ts index 57de350..4bcb385 100644 --- a/src/services/daily-cache.test.ts +++ b/src/services/daily-cache.test.ts @@ -4,11 +4,11 @@ import { getMock, putMock } from '~/services/cache-test-helper' import { DailyCache } from '~/services/daily-cache' test('key', () => { - expect(DailyCache.key('6501', new DateParam('latest'))).toBe( + expect(DailyCache.key('6501', DateParam.from('latest'))).toBe( `daily-6501-${new Date().toISOString().substring(0, 10)}` ) expect(DailyCache.key('6501', new Date())).toBe(`daily-6501-${new Date().toISOString().substring(0, 10)}`) - expect(DailyCache.key('6501', new DateParam(new Date('2020-09-06')))).toBe(`daily-6501-2020-09-06`) + expect(DailyCache.key('6501', DateParam.from(new Date('2020-09-06')))).toBe(`daily-6501-2020-09-06`) expect(DailyCache.key('6501', new Date('2020-09-06'))).toBe(`daily-6501-2020-09-06`) }) diff --git a/src/services/daily-cache.ts b/src/services/daily-cache.ts index ee40c45..a03dded 100644 --- a/src/services/daily-cache.ts +++ b/src/services/daily-cache.ts @@ -10,9 +10,9 @@ export class DailyCache { static key(ticker: string, date: Date | DateParam): string { if (date instanceof Date) { - date = new DateParam(date) + date = DateParam.from(date) } else if (date.isLatest()) { - date = new DateParam(new Date()) + date = DateParam.from(new Date()) } return `${this.prefix}-${ticker}-${date}`