From 97b7b3a4dedcb2afc2a38fd7c6137127a869c126 Mon Sep 17 00:00:00 2001 From: Shu Suzuki Date: Sun, 17 Jul 2022 12:21:43 +0900 Subject: [PATCH] Mod error handlings and refactoring --- BuffettCode.sln | 6 ++ BuffettCodeAPIClient/ApiClientCore.cs | 14 +--- .../BuffettCodeAPIClient.csproj | 2 + .../GetRequestErrorHandler.cs | 76 +++++++++++++++++++ .../BuffettCodeAPIClientTests.csproj | 1 + .../GetRequestErrorHandlerTests.cs | 68 +++++++++++++++++ .../CsvDownloadExceptionHandler.cs | 8 +- .../CsvDownload/TabularWriterBuilder.cs | 2 +- .../Config/BuffettCodeApiConfig.cs | 1 + .../BuffettCodeApiClientException.cs | 18 ++++- .../BCodeFunctionErrorHandler.cs | 8 +- .../HeatGeneratedAddinRibbon.wxs | 4 + .../HeatGeneratedExcelFunctions.wxs | 16 ++++ 13 files changed, 202 insertions(+), 22 deletions(-) create mode 100644 BuffettCodeAPIClient/GetRequestErrorHandler.cs create mode 100644 BuffettCodeAPIClientTests/GetRequestErrorHandlerTests.cs diff --git a/BuffettCode.sln b/BuffettCode.sln index fd0545e..c2045be 100644 --- a/BuffettCode.sln +++ b/BuffettCode.sln @@ -43,8 +43,14 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuffettCodeAddinRibbonTests", "BuffettCodeAddinRibbonTests\BuffettCodeAddinRibbonTests.csproj", "{00A6A593-A87D-4DD2-8B06-8A36CFC6C3AA}" EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "SetupAddinRibbon32", "SetupAddinRibbon32\SetupAddinRibbon32.wixproj", "{E1016C59-0448-4BFA-B89D-BB3C960F1F16}" + ProjectSection(ProjectDependencies) = postProject + {00A6A593-A87D-4DD2-8B06-8A36CFC6C3AA} = {00A6A593-A87D-4DD2-8B06-8A36CFC6C3AA} + EndProjectSection EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "SetupExcelFunctions32", "SetupExcelFunctions32\SetupExcelFunctions32.wixproj", "{9D5AFF0E-0EA3-4318-8BE4-941FC1DAF261}" + ProjectSection(ProjectDependencies) = postProject + {AD8A3F1E-584E-46C1-ACEE-A749433B99B2} = {AD8A3F1E-584E-46C1-ACEE-A749433B99B2} + EndProjectSection EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "BuffettCodeExcelAddin32Installer", "BuffettCodeExcelAddin32Installer\BuffettCodeExcelAddin32Installer.wixproj", "{4D7EB0F3-E548-42EF-A99E-6C059800C654}" EndProject diff --git a/BuffettCodeAPIClient/ApiClientCore.cs b/BuffettCodeAPIClient/ApiClientCore.cs index fa5c387..a065a97 100644 --- a/BuffettCodeAPIClient/ApiClientCore.cs +++ b/BuffettCodeAPIClient/ApiClientCore.cs @@ -1,6 +1,4 @@ -using BuffettCodeCommon.Exception; using System; -using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; @@ -48,17 +46,7 @@ public async Task Get(ApiGetRequest request, bool isConfigureAwait) { if (!response.IsSuccessStatusCode) { - switch ((int)response.StatusCode) - { - case (int)HttpStatusCode.Forbidden: - throw new InvalidAPIKeyException($"request={request}"); - case (int)HttpStatusCode.NotFound: - throw new ResourceNotFoundException($"request={request}"); - case 429: // Quota Error - throw new QuotaException($"request={request}"); - default: - throw new BuffettCodeApiClientException($"request={request}"); - } + GetRequestErrorHandler.Handle(request, response); } var content = response.Content.ReadAsStringAsync().Result; return content; diff --git a/BuffettCodeAPIClient/BuffettCodeAPIClient.csproj b/BuffettCodeAPIClient/BuffettCodeAPIClient.csproj index 92d42ac..fb25436 100644 --- a/BuffettCodeAPIClient/BuffettCodeAPIClient.csproj +++ b/BuffettCodeAPIClient/BuffettCodeAPIClient.csproj @@ -36,6 +36,7 @@ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + @@ -61,6 +62,7 @@ + diff --git a/BuffettCodeAPIClient/GetRequestErrorHandler.cs b/BuffettCodeAPIClient/GetRequestErrorHandler.cs new file mode 100644 index 0000000..b419ccd --- /dev/null +++ b/BuffettCodeAPIClient/GetRequestErrorHandler.cs @@ -0,0 +1,76 @@ +using BuffettCodeCommon.Exception; +using System.Collections.Immutable; +using System.Net; +using System.Net.Http; + +namespace BuffettCodeAPIClient +{ + enum GetHttpStatusErrorCode + { + Forbidden = HttpStatusCode.Forbidden, + NotFound = HttpStatusCode.NotFound, + BadRequest = HttpStatusCode.BadRequest, + TooManyRequests = 429, + } + + static class TestApiTokenErrorMessage + { + const string ExceedingTestingApiTickerLimist = @"{""message"":""Testing Apikey is only allowed to ticker ending with \""01\""""}"; + const string ExceedingTraialApiTickerLimit = "{\"message\":\"Trial request only supports ticker ending '01'\"}"; + const string TestingApiKeyIsNotAllowed = "{\"message\":\"Testing apikey is not allowed\"}"; + public static ImmutableHashSet KnownErrorMessages = ImmutableHashSet.Create(new string[] { ExceedingTestingApiTickerLimist, ExceedingTraialApiTickerLimit, TestingApiKeyIsNotAllowed }); + } + + static class InvalidAPIKeyErrorMessage + { + public const string ApiGatewayDefault = "{\"message\":\"Forbidden\"}"; + + } + + + public class GetRequestErrorHandler + { + public static void Handle(ApiGetRequest request, HttpResponseMessage errorResponse) + { + switch ((int)errorResponse.StatusCode) + { + case (int)GetHttpStatusErrorCode.Forbidden: + throw CreateExceptionForForbidden(request, errorResponse); + case (int)GetHttpStatusErrorCode.NotFound: + throw new ResourceNotFoundException($"request={request}"); + case (int)GetHttpStatusErrorCode.TooManyRequests: + throw new DailyQuotaException($"request={request}"); + case (int)GetHttpStatusErrorCode.BadRequest: + throw CreateExceptionforBadRequest(request, + errorResponse); + default: + throw new BuffettCodeApiClientException($"request={request}"); + } + } + + private static BuffettCodeApiClientException CreateExceptionForForbidden(ApiGetRequest request, HttpResponseMessage errorResponse) + { + switch (errorResponse.Content.ReadAsStringAsync().Result) + { + case InvalidAPIKeyErrorMessage.ApiGatewayDefault: + return new InvalidAPIKeyException($"request={request}"); + default: + return new ApiMonthlyLimitExceededException($"request={request}"); + } + } + + private static BuffettCodeApiClientException CreateExceptionforBadRequest(ApiGetRequest request, HttpResponseMessage errorResponse) + { + if (TestApiTokenErrorMessage.KnownErrorMessages.Contains(errorResponse.Content.ReadAsStringAsync().Result)) + { + return new TestAPIConstraintException($"request={request}"); + } + else + { + return new BuffettCodeApiClientException($"request={request}"); + + } + + } + } +} \ No newline at end of file diff --git a/BuffettCodeAPIClientTests/BuffettCodeAPIClientTests.csproj b/BuffettCodeAPIClientTests/BuffettCodeAPIClientTests.csproj index 87e0d53..115d3f8 100644 --- a/BuffettCodeAPIClientTests/BuffettCodeAPIClientTests.csproj +++ b/BuffettCodeAPIClientTests/BuffettCodeAPIClientTests.csproj @@ -74,6 +74,7 @@ + diff --git a/BuffettCodeAPIClientTests/GetRequestErrorHandlerTests.cs b/BuffettCodeAPIClientTests/GetRequestErrorHandlerTests.cs new file mode 100644 index 0000000..2605708 --- /dev/null +++ b/BuffettCodeAPIClientTests/GetRequestErrorHandlerTests.cs @@ -0,0 +1,68 @@ +using BuffettCodeCommon.Exception; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; + +namespace BuffettCodeAPIClient.Tests +{ + [TestClass()] + public class GetRequestErrorHandlerTests + { + + private readonly ApiGetRequest Request = new ApiGetRequest("dummy", new Dictionary()); + + private static HttpResponseMessage CreateResponseMessage(HttpStatusCode statusCode, string content) + { + var response = new HttpResponseMessage(statusCode); + response.Content = new StringContent(content); + return response; + } + + [TestMethod()] + public void HandleInvalidApiKeyError() + { + var response = CreateResponseMessage(HttpStatusCode.Forbidden, "{\"message\":\"Forbidden\"}"); + Assert.ThrowsException(() => GetRequestErrorHandler.Handle(Request, response)); + } + + [TestMethod()] + public void HandleInvalidApiMonthlyLimitExceededException() + { + var response = CreateResponseMessage(HttpStatusCode.Forbidden, "dummy"); + Assert.ThrowsException(() => GetRequestErrorHandler.Handle(Request, response)); + } + + [TestMethod()] + public void HandleResourceNotFound() + { + var response = CreateResponseMessage(HttpStatusCode.NotFound, ""); + Assert.ThrowsException(() => GetRequestErrorHandler.Handle(Request, response)); + } + + + [TestMethod()] + public void HandleTestAPIConstraintException() + { + var response = CreateResponseMessage(HttpStatusCode.BadRequest, "{\"message\":\"Testing apikey is not allowed\"}"); + Assert.ThrowsException(() => GetRequestErrorHandler.Handle(Request, response)); + } + + [TestMethod()] + public void HandleTestBadRequest() + { + var response = CreateResponseMessage(HttpStatusCode.BadRequest, "unknown error"); + Assert.ThrowsException(() => GetRequestErrorHandler.Handle(Request, response)); + } + + [TestMethod()] + public void HandleTestDefaultException() + { + var response = CreateResponseMessage(HttpStatusCode.InternalServerError, "unknown error"); + Assert.ThrowsException(() => GetRequestErrorHandler.Handle(Request, response)); + } + + + + } +} \ No newline at end of file diff --git a/BuffettCodeAddinRibbon/CsvDownload/CsvDownloadExceptionHandler.cs b/BuffettCodeAddinRibbon/CsvDownload/CsvDownloadExceptionHandler.cs index d522d63..e924b08 100644 --- a/BuffettCodeAddinRibbon/CsvDownload/CsvDownloadExceptionHandler.cs +++ b/BuffettCodeAddinRibbon/CsvDownload/CsvDownloadExceptionHandler.cs @@ -20,9 +20,9 @@ public static string ToMessageBoxString(BaseBuffettCodeException exception) { return "テスト用のAPIキーでは末尾が01の銘柄コードのみ使用できます。"; } - else if (exception is QuotaException) + else if (exception is DailyQuotaException) { - return "APIの実行回数が上限に達しました。"; + return "一日当たりのAPIの実行回数が上限に達しました。"; } else if (exception is InvalidAPIKeyException) { @@ -40,6 +40,10 @@ public static string ToMessageBoxString(BaseBuffettCodeException exception) { return $"存在しないデータにアクセスしようとしています。"; } + else if (exception is ApiMonthlyLimitExceededException) + { + return "今月のAPIの実行回数が上限に達しました。"; + } else { return $"データの取得中にエラーが発生しました。"; diff --git a/BuffettCodeAddinRibbon/CsvDownload/TabularWriterBuilder.cs b/BuffettCodeAddinRibbon/CsvDownload/TabularWriterBuilder.cs index f4b2d22..cd5f2b1 100644 --- a/BuffettCodeAddinRibbon/CsvDownload/TabularWriterBuilder.cs +++ b/BuffettCodeAddinRibbon/CsvDownload/TabularWriterBuilder.cs @@ -30,7 +30,7 @@ public ITabularWriter Build() case TabularOutputDestination.NewCsvFile: return BuildCsvFileTabluarWriter(true); case TabularOutputDestination.NewWorksheet: - Worksheet worksheet = Globals.ThisAddIn.Application.Worksheets.Add(); + Worksheet worksheet = Globals.ThisAddIn.Application.Worksheets.Add(); return BuildWorksheetTabularWriter(worksheet); // for unit testing diff --git a/BuffettCodeCommon/Config/BuffettCodeApiConfig.cs b/BuffettCodeCommon/Config/BuffettCodeApiConfig.cs index 6ca924f..53a58aa 100644 --- a/BuffettCodeCommon/Config/BuffettCodeApiConfig.cs +++ b/BuffettCodeCommon/Config/BuffettCodeApiConfig.cs @@ -42,4 +42,5 @@ public static class ApiRelatedUrlConfig public static readonly string API_SPECIAL_NOTES = "https://www.buffett-code.com/legal/web_api/special_notes"; public static readonly string ONDEMAND_API_USAGE_ENTRY = "https://blog.buffett-code.com/entry/ondemand_api_usage"; } + } \ No newline at end of file diff --git a/BuffettCodeCommon/Exception/BuffettCodeApiClientException.cs b/BuffettCodeCommon/Exception/BuffettCodeApiClientException.cs index 2bd8357..a300402 100644 --- a/BuffettCodeCommon/Exception/BuffettCodeApiClientException.cs +++ b/BuffettCodeCommon/Exception/BuffettCodeApiClientException.cs @@ -20,13 +20,13 @@ public InvalidAPIKeyException(string message) : base(message) { } public InvalidAPIKeyException(string message, Exception inner) : base(message, inner) { } } - public class QuotaException : BuffettCodeApiClientException + public class DailyQuotaException : BuffettCodeApiClientException { - public QuotaException() : base() { } + public DailyQuotaException() : base() { } - public QuotaException(string message) : base(message) { } + public DailyQuotaException(string message) : base(message) { } - public QuotaException(string message, Exception inner) : base(message, inner) { } + public DailyQuotaException(string message, Exception inner) : base(message, inner) { } } @@ -50,4 +50,14 @@ public ResourceNotFoundException(string message, Exception inner) : base(message } + public class ApiMonthlyLimitExceededException : BuffettCodeApiClientException + { + public ApiMonthlyLimitExceededException() : base() { } + + public ApiMonthlyLimitExceededException(string message) : base(message) { } + + public ApiMonthlyLimitExceededException(string message, Exception inner) : base(message, inner) { } + + } + } \ No newline at end of file diff --git a/BuffettCodeExcelFunctions/BCodeFunctionErrorHandler.cs b/BuffettCodeExcelFunctions/BCodeFunctionErrorHandler.cs index 98b9780..2a70081 100644 --- a/BuffettCodeExcelFunctions/BCodeFunctionErrorHandler.cs +++ b/BuffettCodeExcelFunctions/BCodeFunctionErrorHandler.cs @@ -23,9 +23,9 @@ public static string ToErrorMessage(Exception e, string propertyName, bool isDeb { message = $"指定された項目が見つかりません:{propertyName}"; } - else if (bce is QuotaException) + else if (bce is DailyQuotaException) { - message = "APIの実行回数が上限に達しました"; + message = "一日当たりのAPIの実行回数が上限に達しました"; } else if (bce is InvalidAPIKeyException) { @@ -51,6 +51,10 @@ public static string ToErrorMessage(Exception e, string propertyName, bool isDeb { message = $"存在しないデータにアクセスしようとしています。::{bce.Message}"; } + else if (bce is ApiMonthlyLimitExceededException) + { + message = $"今月のAPIの実行回数が上限に達しました::{bce.Message}"; + } else if (bce is BuffettCodeApiClientException) { message = "APIの呼び出しでエラーが発生しました"; diff --git a/SetupAddinRibbon32/HeatGeneratedAddinRibbon.wxs b/SetupAddinRibbon32/HeatGeneratedAddinRibbon.wxs index 2dee335..d40e3fb 100644 --- a/SetupAddinRibbon32/HeatGeneratedAddinRibbon.wxs +++ b/SetupAddinRibbon32/HeatGeneratedAddinRibbon.wxs @@ -62,6 +62,9 @@ + + + @@ -110,6 +113,7 @@ + diff --git a/SetupExcelFunctions32/HeatGeneratedExcelFunctions.wxs b/SetupExcelFunctions32/HeatGeneratedExcelFunctions.wxs index 8894dbb..98f6101 100644 --- a/SetupExcelFunctions32/HeatGeneratedExcelFunctions.wxs +++ b/SetupExcelFunctions32/HeatGeneratedExcelFunctions.wxs @@ -77,6 +77,18 @@ + + + + + + + + + + + + @@ -112,6 +124,10 @@ + + + +