From bcdebe269a4cbbaf7a80c99e26f02ecacaa06172 Mon Sep 17 00:00:00 2001 From: Akiomi Kamakura Date: Fri, 17 Jun 2022 12:04:46 +0900 Subject: [PATCH] Improve error handling --- src/api/http-error.ts | 16 +++++++++++ src/custom-functions/v3/bcode.ts | 42 ++-------------------------- src/services/csv-exporter.ts | 40 +++++++++++++++++++-------- src/services/error-handler.ts | 47 ++++++++++++++++++++++++++++++++ src/ui/csv-export-dialog.html | 2 +- 5 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 src/services/error-handler.ts diff --git a/src/api/http-error.ts b/src/api/http-error.ts index 0b4597b..96c4c7c 100644 --- a/src/api/http-error.ts +++ b/src/api/http-error.ts @@ -23,6 +23,22 @@ export class HttpError implements Error { return content === '{"message":"Forbidden"}' } + public isMonthlyLimitExceeded(): boolean { + return !this.isInvalidTokenRequest() && this.response.getResponseCode() === 403 + } + + public isResourceNotFound(): boolean { + return this.response.getResponseCode() === 404 + } + + public isApiQuotaExceeded(): boolean { + return this.response.getResponseCode() === 429 + } + + public isBadRequest(): boolean { + return Math.floor(this.response.getResponseCode() / 100) === 4 + } + public toString(): string { return this.message } diff --git a/src/custom-functions/v3/bcode.ts b/src/custom-functions/v3/bcode.ts index 76051cc..43ff088 100644 --- a/src/custom-functions/v3/bcode.ts +++ b/src/custom-functions/v3/bcode.ts @@ -1,49 +1,11 @@ -import { HttpError } from '~/api/http-error' import { CachingBuffettCodeApiClientV3 } from '~/api/v3/caching-client' -import { ApiResponseError, OndemandApiNotEnabledError, UnsupportedTickerError } from '~/custom-functions/error' 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 { InvalidLYLQError, InvalidYearError, InvalidQuarterError } from '~/fiscal-periods/error' import { PeriodParser } from '~/fiscal-periods/period-parser' +import { ErrorHandler } from '~/services/error-handler' import { Setting } from '~/setting' -function handleError(e): void { - 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 (e.isInvalidTokenRequest()) { - throw new Error('<>') - } else if (code === 403) { - throw new Error('<<月間リクエスト制限に達しています>>') - } else if (code === 429) { - throw new Error('<>') - } 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(`<<無効な決算年度が指定されています>>`) - } else if (e instanceof InvalidQuarterError) { - throw new Error(`<<無効な四半期が指定されています>>`) - } else { - console.error('未定義のエラー', e.name, e.message) - throw new Error(`<<未定義のエラーが発生しました。${e.name}: ${e.message}>>`) - } -} - export function bcode( ticker: string, period: string | Date, @@ -98,6 +60,6 @@ export function bcode( return result.format(isRawValue, isWithUnits) } catch (e) { - handleError(e) + ErrorHandler.handle(e) } } diff --git a/src/services/csv-exporter.ts b/src/services/csv-exporter.ts index acc099a..42f1c25 100644 --- a/src/services/csv-exporter.ts +++ b/src/services/csv-exporter.ts @@ -4,6 +4,7 @@ import { CachingBuffettCodeApiClientV3 } from '~/api/v3/caching-client' import { YearQuarter } from '~/fiscal-periods/year-quarter' import { YearQuarterParam } from '~/fiscal-periods/year-quarter-param' import { YearQuarterRange } from '~/fiscal-periods/year-quarter-range' +import { ErrorHandler } from '~/services/error-handler' import { Setting } from '~/setting' export class CsvExporter { @@ -28,7 +29,14 @@ export class CsvExporter { const setting = Setting.load() const client = new CachingBuffettCodeApiClientV3(setting.token) - const companyService = new CompanyService(ticker, client, today) + + let companyService + try { + companyService = new CompanyService(ticker, client, today) + } catch (e) { + ErrorHandler.handle(e) + } + const ondemandQuarterApiPeriodRange = new OndemandApiPeriodRange(companyService) let ondemandQuarterApiPeriods = [] @@ -48,12 +56,16 @@ export class CsvExporter { } const quarters = [] - quarterApiPeriods.forEach(period => { - quarters.push(client.quarter(ticker, YearQuarterParam.fromYearQuarter(period))) - }) - ondemandQuarterApiPeriods.forEach(period => { - quarters.push(client.ondemandQuarter(ticker, YearQuarterParam.fromYearQuarter(period))) - }) + try { + quarterApiPeriods.forEach(period => { + quarters.push(client.quarter(ticker, YearQuarterParam.fromYearQuarter(period))) + }) + ondemandQuarterApiPeriods.forEach(period => { + quarters.push(client.ondemandQuarter(ticker, YearQuarterParam.fromYearQuarter(period))) + }) + } catch (e) { + ErrorHandler.handle(e) + } if (!quarters.length) { throw new Error('<<指定されたデータを取得できませんでした>>') @@ -96,10 +108,16 @@ export class CsvExporter { const numRows = data.length const numColumns = data[0].length - const ss = SpreadsheetApp.getActiveSpreadsheet() - const sheet = ss.insertSheet() - const range = sheet.getRange(1, 1, numRows, numColumns) // starts from A1 + try { + const ss = SpreadsheetApp.getActiveSpreadsheet() + const sheet = ss.insertSheet() + const range = sheet.getRange(1, 1, numRows, numColumns) // starts from A1 - range.setValues(data) + range.setValues(data) + } catch (e) { + throw new Error( + `<<出力中にエラーが発生しました (${e.name}: ${e.message})。改善しない場合はGoogleアカウントのログアウトおよび再ログインをお試しください>>` + ) + } } } diff --git a/src/services/error-handler.ts b/src/services/error-handler.ts new file mode 100644 index 0000000..952b9d4 --- /dev/null +++ b/src/services/error-handler.ts @@ -0,0 +1,47 @@ +import { HttpError } from '~/api/http-error' +import { ApiResponseError, OndemandApiNotEnabledError, UnsupportedTickerError } from '~/custom-functions/error' +import { InvalidLYLQError, InvalidYearError, InvalidQuarterError } from '~/fiscal-periods/error' + +export class ErrorHandler { + static handle(e: Error): void { + 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) { + ErrorHandler.handleHttpError(e) + } else if (e instanceof InvalidLYLQError) { + throw new Error('<<無効なLY・LQが指定されています>>') + } else if (e instanceof InvalidYearError) { + throw new Error(`<<無効な決算年度が指定されています>>`) + } else if (e instanceof InvalidQuarterError) { + throw new Error(`<<無効な四半期が指定されています>>`) + } else { + console.error('未定義のエラー', e.name, e.message) + throw new Error( + `<<未定義のエラーが発生しました (${e.name}: ${e.message})。改善しない場合はGoogleアカウントのログアウトおよび再ログインをお試しください>>` + ) + } + } + + static handleHttpError(e: HttpError): void { + if (e.isInvalidTestingRequest()) { + throw new Error('<<テスト用のAPIキーでは取得できないデータです>>') + } else if (e.isInvalidTokenRequest()) { + throw new Error('<>') + } else if (e.isMonthlyLimitExceeded()) { + throw new Error('<<月間リクエスト制限に達しています>>') + } else if (e.isResourceNotFound()) { + throw new Error('<<データが見つかりません。tickerや期間に間違いがないかご確認ください>>') + } else if (e.isApiQuotaExceeded()) { + throw new Error('<>') + } else if (e.isBadRequest()) { + throw new Error(`<<無効なリクエストです (${e.name}: ${e.message})>>`) + } else { + console.error('システムエラー', e.name, e.message) + throw new Error(`<<システムエラーが発生しました (${e.name}: ${e.message})>>`) + } + } +} diff --git a/src/ui/csv-export-dialog.html b/src/ui/csv-export-dialog.html index 3758770..aa8c517 100644 --- a/src/ui/csv-export-dialog.html +++ b/src/ui/csv-export-dialog.html @@ -10,7 +10,7 @@ const button = document.getElementById('export-button') button.disabled = "" - alert('CSV出力中にエラーが発生しました: ' + e.message) + alert(`CSV出力中にエラーが発生しました\n${e.message}`) } function exportCsv() {