-
Notifications
You must be signed in to change notification settings - Fork 1
Faster csv download #44
Changes from all commits
dbf2ece
36e3693
d76f549
42e15e7
90c39ac
c0f272a
b1717a3
1f6ce5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,15 +11,15 @@ namespace BuffettCodeAPIClient | |
| /// BuffettCode API と Http でやり取りする Client のコアクラス | ||
| /// </summary> | ||
|
|
||
| public class ApiClientCore | ||
| public class ApiClientCore : IDisposable, IApiClientCore | ||
| { | ||
| public string ApiKey { set; get; } | ||
| private string apiKey; | ||
| private readonly Uri baseUri; | ||
| private static readonly long TimeoutMilliseconds = 5000; | ||
| private readonly HttpClient httpClient; | ||
| public ApiClientCore(string apiKey, Uri baseUri) | ||
| { | ||
| this.ApiKey = apiKey; | ||
| this.apiKey = apiKey; | ||
| this.baseUri = baseUri; | ||
| this.httpClient = NewHttpClient(); | ||
| } | ||
|
|
@@ -32,11 +32,11 @@ private HttpClient NewHttpClient() | |
| httpClient.DefaultRequestHeaders.Accept.Clear(); | ||
| httpClient.DefaultRequestHeaders.Accept.Add( | ||
| new MediaTypeWithQualityHeaderValue("application/json")); | ||
| httpClient.DefaultRequestHeaders.Add("x-api-key", ApiKey); | ||
| httpClient.DefaultRequestHeaders.Add("x-api-key", apiKey); | ||
| return httpClient; | ||
| } | ||
|
|
||
| public static string BuildGetPath(ApiGetRequest request) | ||
| private static string BuildGetPath(ApiGetRequest request) | ||
| { | ||
| return $"{request.EndPoint}?{new FormUrlEncodedContent(request.Parameters).ReadAsStringAsync().Result}"; | ||
| } | ||
|
|
@@ -58,10 +58,23 @@ public async Task<string> Get(ApiGetRequest request, bool isConfigureAwait) | |
| throw new BuffettCodeApiClientException(); | ||
| } | ||
| } | ||
| // to do : waiting too long to read as str in csv download | ||
| return await response.Content.ReadAsStringAsync().ConfigureAwait(isConfigureAwait); | ||
| var content = response.Content.ReadAsStringAsync().Result; | ||
|
Comment on lines
-62
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task-1.result?view=net-5.0#-- |
||
| return content; | ||
| } | ||
| } | ||
|
|
||
| public void Dispose() | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. constructor で |
||
| { | ||
| httpClient.Dispose(); | ||
| } | ||
|
|
||
| public IApiClientCore SetApiKey(string apiKey) | ||
| { | ||
| this.apiKey = apiKey; | ||
| return this; | ||
| } | ||
|
|
||
| public string GetApiKey() => this.apiKey; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,45 +1,48 @@ | ||
| using System; | ||
| using System.Runtime.Caching; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace BuffettCodeAPIClient | ||
| { | ||
| public class ApiClientCoreWithCache | ||
| { | ||
| private readonly ApiClientCore apiClientCore; | ||
| private readonly IApiClientCore apiClientCore; | ||
| private readonly ApiRequestCacheHelper cacheHelper; | ||
|
|
||
| private ApiClientCoreWithCache(string apiKey, Uri baseUri, MemoryCache cache) | ||
| private ApiClientCoreWithCache(IApiClientCore apiClientCore | ||
| , ApiRequestCacheHelper cacheHelper) | ||
| { | ||
| this.apiClientCore = new ApiClientCore(apiKey, baseUri); | ||
| this.cacheHelper = new ApiRequestCacheHelper(cache, baseUri); | ||
| this.apiClientCore = apiClientCore; | ||
| this.cacheHelper = cacheHelper; | ||
| } | ||
|
|
||
| public static ApiClientCoreWithCache Create(string apiKey, string baseUrl, MemoryCache cache) | ||
| { | ||
| return new ApiClientCoreWithCache(apiKey, new Uri(baseUrl), cache); | ||
| var baseUri = new Uri(baseUrl); | ||
| var apiClientCore = new ApiClientCore(apiKey, baseUri); | ||
| var cacheHelper = new ApiRequestCacheHelper(cache, baseUri); | ||
| return new ApiClientCoreWithCache(apiClientCore, cacheHelper); | ||
| } | ||
|
|
||
| public static ApiClientCoreWithCache Create(string apiKey, Uri baseUrl, MemoryCache cache) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この |
||
| public static ApiClientCoreWithCache Create(IApiClientCore apiClientCore, ApiRequestCacheHelper cacheHelper) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 代わりにこっちを作りました |
||
| { | ||
| return new ApiClientCoreWithCache(apiKey, baseUrl, cache); | ||
| return new ApiClientCoreWithCache(apiClientCore, cacheHelper); | ||
| } | ||
|
|
||
| public void UpdateApiKey(string apikey) | ||
| { | ||
| this.apiClientCore.ApiKey = apikey; | ||
| } | ||
| public void UpdateApiKey(string apiKey) => this.apiClientCore.SetApiKey(apiKey); | ||
|
|
||
| public string GetApiKey() => this.apiClientCore.GetApiKey(); | ||
|
|
||
| public string GetApiKey() => this.apiClientCore.ApiKey; | ||
| public async Task<string> Get(ApiGetRequest request, bool isConfigureAwait, bool useCache) | ||
| public string Get(ApiGetRequest request, bool isConfigureAwait, bool useCache) | ||
| { | ||
| if (useCache && cacheHelper.HasCache(request)) | ||
| { | ||
| return (string)cacheHelper.Get(request); | ||
| } | ||
| var response = await apiClientCore.Get(request, isConfigureAwait); | ||
| cacheHelper.Set(request, response); | ||
| return response; | ||
| else | ||
| { | ||
| return apiClientCore.Get(request, isConfigureAwait).Result; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この時点で取得を待って動くようにします。当然ながら cache からは常に |
||
| } | ||
| } | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,6 @@ | |
| using Newtonsoft.Json.Linq; | ||
| using System; | ||
| using System.Runtime.Caching; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace BuffettCodeAPIClient | ||
| { | ||
|
|
@@ -25,31 +24,31 @@ private BuffettCodeApiV2Client() | |
| ); | ||
| } | ||
|
|
||
| public async Task<JObject> GetQuarter(string ticker, FiscalQuarterPeriod period, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
| public JObject GetQuarter(string ticker, FiscalQuarterPeriod period, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| { | ||
| var request = BuffettCodeApiV2RequestCreator.CreateGetQuarterRequest(ticker, period, useOndemand); | ||
| var response = await apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| var response = apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| return ApiGetResponseBodyParser.Parse(response); | ||
| } | ||
|
|
||
| public async Task<JObject> GetIndicator(string ticker, bool isConfigureAwait = true, bool useCache = true) | ||
| public JObject GetIndicator(string ticker, bool isConfigureAwait = true, bool useCache = true) | ||
| { | ||
| var request = BuffettCodeApiV2RequestCreator.CreateGetIndicatorRequest | ||
| (ticker); | ||
| var response = await apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| var response = apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| return ApiGetResponseBodyParser.Parse(response); | ||
| } | ||
|
|
||
| public async Task<JObject> GetQuarterRange(string ticker, FiscalQuarterPeriod from, FiscalQuarterPeriod to, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
| public JObject GetQuarterRange(string ticker, FiscalQuarterPeriod from, FiscalQuarterPeriod to, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
| { | ||
| var request = BuffettCodeApiV2RequestCreator.CreateGetQuarterRangeRequest(ticker, from, to); | ||
| var response = await apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| var response = apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| return ApiGetResponseBodyParser.Parse(response); | ||
| } | ||
| public async Task<JObject> GetCompany(string ticker, bool isConfigureAwait = true, bool useCache = true) | ||
| public JObject GetCompany(string ticker, bool isConfigureAwait = true, bool useCache = true) | ||
| { | ||
| var request = BuffettCodeApiV2RequestCreator.CreateGetCompanyRequest(ticker); | ||
| var response = await apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| var response = apiClientCore.Get(request, isConfigureAwait, useCache); | ||
| return ApiGetResponseBodyParser.Parse(response); | ||
| } | ||
|
|
||
|
|
@@ -64,7 +63,7 @@ public static BuffettCodeApiV2Client GetInstance(string apiKey) | |
| } | ||
|
|
||
|
|
||
| public Task<JObject> Get(DataTypeConfig dataType, string ticker, IPeriod period, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
| public JObject Get(DataTypeConfig dataType, string ticker, IPeriod period, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
| { | ||
| switch (dataType) | ||
| { | ||
|
|
@@ -79,7 +78,7 @@ public Task<JObject> Get(DataTypeConfig dataType, string ticker, IPeriod period, | |
| } | ||
| } | ||
|
|
||
| public Task<JObject> GetRange(DataTypeConfig dataType, string ticker, IPeriod from, IPeriod to, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
| public JObject GetRange(DataTypeConfig dataType, string ticker, IPeriod from, IPeriod to, bool useOndemand, bool isConfigureAwait = true, bool useCache = true) | ||
| { | ||
| switch (dataType) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace BuffettCodeAPIClient | ||
| { | ||
| public interface IApiClientCore | ||
| { | ||
| Task<string> Get(ApiGetRequest request, bool isConfigureAwait); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これどうするかすごい迷ったけど、この
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 将来的には非同期クライアントになってるほうがいいと思うので、内部的には半同期みたいな感じでもシグニチャはこのままでいいと思う |
||
|
|
||
| IApiClientCore SetApiKey(string apiKey); | ||
| string GetApiKey(); | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,101 @@ | ||
| using BuffettCodeCommon.Exception; | ||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Runtime.Caching; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace BuffettCodeAPIClient.Tests | ||
| { | ||
| class MockApiclientCore : IApiClientCore | ||
| { | ||
| private string apiKey; | ||
| public MockApiclientCore(string apiKey) | ||
| { | ||
| this.apiKey = apiKey; | ||
| } | ||
|
|
||
|
|
||
| public Task<string> Get(ApiGetRequest request, bool isConfigureAwait) | ||
| { | ||
| return Task<string>.FromResult(request.ToString()); | ||
| } | ||
|
|
||
| public string GetApiKey() => this.apiKey; | ||
| public IApiClientCore SetApiKey(string apiKey) | ||
| { | ||
| this.apiKey = apiKey; | ||
| return this; | ||
| } | ||
| } | ||
|
|
||
| class ErrorMockApiClientCore : IApiClientCore | ||
| { | ||
| private string apiKey; | ||
| public ErrorMockApiClientCore(string apiKey) | ||
| { | ||
| this.apiKey = apiKey; | ||
| } | ||
|
|
||
|
|
||
| public Task<string> Get(ApiGetRequest request, bool isConfigureAwait) | ||
| { | ||
| throw new BuffettCodeApiClientException(); | ||
| } | ||
|
|
||
| public string GetApiKey() => this.apiKey; | ||
| public IApiClientCore SetApiKey(string apikey) | ||
| { | ||
| apiKey = apikey; | ||
| return this; | ||
| } | ||
| } | ||
|
|
||
| [TestClass()] | ||
| public class ApiClientCoreWithCacheTests | ||
| { | ||
| private static ApiRequestCacheHelper CreateCheHelperForTest() | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test ごとにcache helper を作ります。 |
||
| { | ||
| var cacheKey = new Random().Next().GetHashCode().ToString(); | ||
| return new ApiRequestCacheHelper(new MemoryCache(cacheKey), new Uri("http://example.com")); | ||
| } | ||
|
|
||
| [TestMethod()] | ||
| public void GetAndUpdateApiKeyTest() | ||
| { | ||
| var initApiKey = @"init-key"; | ||
| var newApiKey = @"new-key"; | ||
| var client = ApiClientCoreWithCache.Create(initApiKey, @"http://example.com", new MemoryCache(@"GetAndUpdateApiKeyTest")); | ||
| var mockApiClientCore = new MockApiclientCore(initApiKey); | ||
| var client = ApiClientCoreWithCache.Create(mockApiClientCore, CreateCheHelperForTest()); | ||
|
|
||
| // test init value | ||
| Assert.AreEqual(initApiKey, client.GetApiKey()); | ||
|
|
||
| client.UpdateApiKey(newApiKey); | ||
| Assert.AreEqual(newApiKey, client.GetApiKey()); | ||
| } | ||
|
|
||
| [TestMethod()] | ||
| public void GetTest() | ||
| { | ||
| var mockApiClientCore = new MockApiclientCore("dummy"); | ||
| var client = ApiClientCoreWithCache.Create(mockApiClientCore, CreateCheHelperForTest()); | ||
| ApiGetRequest request = new ApiGetRequest("dummy endpoint", new Dictionary<string, string>()); | ||
| Assert.AreEqual(request.ToString(), client.Get(request, false, true)); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1回目のリクエスト |
||
| // use cache | ||
| Assert.AreEqual(request.ToString(), client.Get(request, false, true)); | ||
|
|
||
| // don't use cache | ||
| Assert.AreEqual(request.ToString(), client.Get(request, false, false)); | ||
|
Comment on lines
+86
to
+89
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cahceを使っても使わなくても、同じ結果が返ることを確認しています |
||
| } | ||
|
|
||
| [TestMethod()] | ||
| public void GetExceptionTest() | ||
| { | ||
| var mockApiClientCore = new ErrorMockApiClientCore("dummy"); | ||
| var client = ApiClientCoreWithCache.Create(mockApiClientCore, CreateCheHelperForTest()); | ||
| ApiGetRequest request = new ApiGetRequest("dummy endpoint", new Dictionary<string, string>()); | ||
| Assert.ThrowsException<BuffettCodeApiClientException>(() => client.Get(request, false, false)); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
実際にhttp通信を行うこいつを差し替えられるようにします