Skip to content
This repository was archived by the owner on Sep 27, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions src/custom-functions/bcode-v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { HttpError } from '~/api/http-error'
import { CachingBuffettCodeApiClientV2 } from '~/api/v2/caching-client'
import { CachingIndicatorProperty } from '~/api/v2/caching-indicator-property'
import { QuarterProperty } from '~/api/v2/quarter-property'
import { bcodeIndicator } from '~/custom-functions/bcode-indicator'
import { bcodeQuarter } from '~/custom-functions/bcode-quarter'
import { BcodeResult } from '~/custom-functions/bcode-result'
import {
ApiResponseError,
OndemandApiNotEnabledError,
UnsupportedTickerError
} from '~/custom-functions/error'
import {
InvalidLYLQError,
InvalidYearError,
InvalidQuarterError
} from '~/fiscal-periods/error'
import { Setting } from '~/setting'

function validate(
ticker: string,
fiscalYear: string,
fiscalQuarter: string,
propertyName: string
): void {
if (!ticker) {
throw new Error('<<tickerが有効ではありません>>')
}

if (!propertyName) {
throw new Error('<<propertyNameが有効ではありません>>')
}

const isQuarterProperty = QuarterProperty.isQuarterProperty(propertyName)
const isIndicatorProperty = CachingIndicatorProperty.isIndicatorProperty(
propertyName
)

if (!isQuarterProperty && !isIndicatorProperty) {
throw new Error(`<<指定された項目が見つかりません: ${propertyName}>>`)
}
}

// TODO: エラーハンドリングの改善
// TODO: fiscalYearとfiscalQuarterの型をstringではなく'LY'と'LQ'に変更する
export function bcodeV2(
ticker: string,
fiscalYear: string | number,
fiscalQuarter: string | number,
propertyName: string,
isRawValue = false,
isWithUnits = false
): number | string {
validate(
ticker,
fiscalYear.toString(),
fiscalQuarter.toString(),
propertyName
)

const setting = Setting.load()
if (!setting.token) {
throw new Error('<<APIキーが有効ではありません>>')
}

const client = new CachingBuffettCodeApiClientV2(setting.token)

try {
let result: BcodeResult
if (fiscalYear && fiscalQuarter) {
result = bcodeQuarter(
client,
ticker,
fiscalYear === 'LY' ? fiscalYear : parseInt(fiscalYear.toString(), 10),
fiscalQuarter === 'LQ'
? fiscalQuarter
: parseInt(fiscalQuarter.toString(), 10),
propertyName,
setting.ondemandApiEnabled
)
} else {
result = bcodeIndicator(client, ticker, propertyName)
}

return result.format(isRawValue, isWithUnits)
} catch (e) {
if (e instanceof ApiResponseError) {
throw new Error('<<指定されたデータを取得できませんでした>>')
} else if (e instanceof OndemandApiNotEnabledError) {
throw new Error('<<従量課金APIが有効になっていません>>')
} else if (e instanceof UnsupportedTickerError) {
throw new Error('<<サポートされていないtickerです>>')
} else if (e instanceof HttpError) {
const code = e.response.getResponseCode()

if (e.isInvalidTestingRequest()) {
throw new Error('<<テスト用のAPIキーでは取得できないデータです>>')
} else if (code === 403) {
throw new Error('<<APIキーが有効ではありません>>')
} else if (code === 429) {
throw new Error('<<APIの実行回数が上限に達しました>>')
} else if (Math.floor(code / 100) === 4) {
throw new Error(`<<無効なリクエストです。${e.name}: ${e.message}>>`)
} else {
console.error('システムエラー', e.name, e.message)
throw new Error(
`<<システムエラーが発生しました。${e.name}: ${e.message}>>`
)
}
} else if (e instanceof InvalidLYLQError) {
throw new Error('<<LYとLQは同時に指定する必要があります>>')
} else if (e instanceof InvalidYearError) {
throw new Error(`<<無効な決算年度が指定されています: ${fiscalYear}>>`)
} else if (e instanceof InvalidQuarterError) {
throw new Error(`<<無効な四半期が指定されています: ${fiscalQuarter}>>`)
} else {
console.error('未定義のエラー', e.name, e.message)
throw new Error(
`<<未定義のエラーが発生しました。${e.name}: ${e.message}>>`
)
}
}
}
28 changes: 28 additions & 0 deletions src/custom-functions/bcode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { castStringAsBoolean, isV3Call } from '~/custom-functions/bcode'

test('castStringAsBoolean', () => {
expect(castStringAsBoolean(true)).toBeTruthy()
expect(castStringAsBoolean('true')).toBeTruthy()
expect(castStringAsBoolean('TRUE')).toBeTruthy()
expect(castStringAsBoolean('True')).toBeTruthy()
expect(castStringAsBoolean(false)).toBeFalsy()
expect(castStringAsBoolean('false')).toBeFalsy()
expect(castStringAsBoolean('FALSE')).toBeFalsy()
expect(castStringAsBoolean('False')).toBeFalsy()
expect(castStringAsBoolean('t')).toBeFalsy()
expect(castStringAsBoolean('f')).toBeFalsy()
expect(castStringAsBoolean('')).toBeFalsy()
})

test('isV3Call', () => {
expect(isV3Call(2020, 1)).toBeFalsy()
expect(isV3Call(2020, '1')).toBeFalsy()
expect(isV3Call('2020', 1)).toBeFalsy()
expect(isV3Call('2020', '1')).toBeFalsy()
expect(isV3Call('LY', '4')).toBeFalsy()
expect(isV3Call('ly', '4')).toBeFalsy()
expect(isV3Call('2020', 'LQ')).toBeFalsy()
expect(isV3Call('2020', 'lq')).toBeFalsy()
expect(isV3Call('', '')).toBeFalsy()
expect(isV3Call('2020Q1', 'net_sales')).toBeTruthy()
})
137 changes: 36 additions & 101 deletions src/custom-functions/bcode.ts
Original file line number Diff line number Diff line change
@@ -1,111 +1,46 @@
import { HttpError } from '~/api/http-error'
import { CachingBuffettCodeApiClientV2 } from '~/api/v2/caching-client'
import { CachingIndicatorProperty } from '~/api/v2/caching-indicator-property'
import { QuarterProperty } from '~/api/v2/quarter-property'
import { bcodeIndicator } from '~/custom-functions/bcode-indicator'
import { bcodeQuarter } from '~/custom-functions/bcode-quarter'
import { BcodeResult } from '~/custom-functions/bcode-result'
import {
ApiResponseError,
OndemandApiNotEnabledError,
UnsupportedTickerError
} from '~/custom-functions/error'
import {
InvalidLYLQError,
InvalidYearError,
InvalidQuarterError
} from '~/fiscal-periods/error'
import { Setting } from '~/setting'
import { bcodeV2 } from '~/custom-functions/bcode-v2'

function validate(
ticker: string,
fiscalYear: string,
fiscalQuarter: string,
propertyName: string
): void {
if (!ticker) {
throw new Error('<<tickerが有効ではありません>>')
}

if (!propertyName) {
throw new Error('<<propertyNameが有効ではありません>>')
}

const isQuarterProperty = QuarterProperty.isQuarterProperty(propertyName)
const isIndicatorProperty = CachingIndicatorProperty.isIndicatorProperty(
propertyName
)
export function castStringAsBoolean(bool: string | boolean): boolean {
return typeof bool === 'string' ? bool.toLowerCase() === 'true' : bool
}

if (!isQuarterProperty && !isIndicatorProperty) {
throw new Error(`<<指定された項目が見つかりません: ${propertyName}>>`)
export function isV3Call(
param1: string | number,
param2: string | number
): boolean {
if (typeof param1 === 'number' || typeof param2 === 'number') {
return false
} else if (param1 === '' || param2 === '') {
return false
} else if (param1.toUpperCase() === 'LY' || param2.toUpperCase() === 'LQ') {
return false
} else if (param1.match(/^\d+$/) || param1.match(/^\d+$/)) {
return false
} else {
return true
}
}

// TODO: エラーハンドリングの改善
export function bcode(
ticker: string,
fiscalYear: string,
fiscalQuarter: string,
propertyName: string,
isRawValue = false,
isWithUnits = false
param1: string | number | Date,
param2: string | number,
param3: string | boolean = false,
param4: string | boolean = false,
Comment on lines +26 to +29
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど

param5: string | boolean = false
): number | string {
validate(ticker, fiscalYear, fiscalQuarter, propertyName)

const setting = Setting.load()
if (!setting.token) {
throw new Error('<<APIキーが有効ではありません>>')
}

const client = new CachingBuffettCodeApiClientV2(setting.token)

try {
let result: BcodeResult
if (fiscalYear && fiscalQuarter) {
result = bcodeQuarter(
client,
ticker,
fiscalYear === 'LY' ? fiscalYear : parseInt(fiscalYear, 10),
fiscalQuarter === 'LQ' ? fiscalQuarter : parseInt(fiscalQuarter, 10),
propertyName,
setting.ondemandApiEnabled
)
} else {
result = bcodeIndicator(client, ticker, propertyName)
}

return result.format(isRawValue, isWithUnits)
} catch (e) {
if (e instanceof ApiResponseError) {
throw new Error('<<指定されたデータを取得できませんでした>>')
} else if (e instanceof OndemandApiNotEnabledError) {
throw new Error('<<従量課金APIが有効になっていません>>')
} else if (e instanceof UnsupportedTickerError) {
throw new Error('<<サポートされていないtickerです>>')
} else if (e instanceof HttpError) {
const code = e.response.getResponseCode()

if (e.isInvalidTestingRequest()) {
throw new Error('<<テスト用のAPIキーでは取得できないデータです>>')
} else if (code === 403) {
throw new Error('<<APIキーが有効ではありません>>')
} else if (code === 429) {
throw new Error('<<APIの実行回数が上限に達しました>>')
} else if (Math.floor(code / 100) === 4) {
throw new Error('<<無効なリクエストです>>')
} else {
console.error('システムエラー', e.name, e.message)
throw new Error('<<システムエラーが発生しました>>')
}
} else if (e instanceof InvalidLYLQError) {
throw new Error('<<LYとLQは同時に指定する必要があります>>')
} else if (e instanceof InvalidYearError) {
throw new Error(`<<無効な決算年度が指定されています: ${fiscalYear}>>`)
} else if (e instanceof InvalidQuarterError) {
throw new Error(`<<無効な四半期が指定されています: ${fiscalQuarter}>>`)
} else {
console.error('未定義のエラー', e.name, e.message)
throw new Error(`<<未定義のエラー: ${e.name}: ${e.message}>>`)
}
if (param1 instanceof Date || isV3Call(param1, param2)) {
throw new Error(
`<<引数が不正な形式です。param1: ${param1} (${typeof param1}), param2: ${param2} (${typeof param2})>>`
)
Comment on lines +33 to +35
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

将来的にはここで bcodeV3 を呼び出します。

} else {
return bcodeV2(
ticker,
param1,
param2,
param3.toString(),
castStringAsBoolean(param4),
castStringAsBoolean(param5)
)
}
}