OSDN Git Service

TwitterApiクラスからのレートリミットの更新に対応
authorKimura Youichi <kim.upsilon@bucyou.net>
Thu, 28 Apr 2016 17:59:19 +0000 (02:59 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Thu, 28 Apr 2016 18:01:52 +0000 (03:01 +0900)
OpenTween.Tests/Api/TwitterApiStatusTest.cs
OpenTween.Tests/Api/TwitterApiTest.cs
OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
OpenTween/Api/TwitterApi.cs
OpenTween/Api/TwitterApiStatus.cs
OpenTween/Connection/IApiConnection.cs
OpenTween/Connection/TwitterApiConnection.cs

index 8d48e9e..394a604 100644 (file)
@@ -22,6 +22,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Net.Http;
 using System.Text;
 using System.Xml;
 using OpenTween.Api.DataModel;
@@ -168,7 +169,7 @@ namespace OpenTween.Api
         }
 
         [Fact]
-        public void UpdateFromHeaderTest()
+        public void UpdateFromHeader_DictionaryTest()
         {
             var status = new TwitterApiStatus();
 
@@ -203,6 +204,45 @@ namespace OpenTween.Api
             Assert.True(eventCalled);
         }
 
+        [Fact]
+        public void UpdateFromHeader_HttpClientTest()
+        {
+            var status = new TwitterApiStatus();
+
+            var eventCalled = false;
+            status.AccessLimitUpdated += (s, e) => eventCalled = true;
+
+            var response = new HttpResponseMessage
+            {
+                Headers =
+                {
+                    { "X-Rate-Limit-Limit", "150" },
+                    { "X-Rate-Limit-Remaining", "100" },
+                    { "X-Rate-Limit-Reset", "1356998400" },
+                    { "X-MediaRateLimit-Limit", "30" },
+                    { "X-MediaRateLimit-Remaining", "20" },
+                    { "X-MediaRateLimit-Reset", "1357084800" },
+                    { "X-Access-Level", "read-write-directmessages" },
+                },
+            };
+
+            status.UpdateFromHeader(response.Headers, "/statuses/home_timeline");
+
+            var rateLimit = status.AccessLimit["/statuses/home_timeline"];
+            Assert.Equal(150, rateLimit.AccessLimitCount);
+            Assert.Equal(100, rateLimit.AccessLimitRemain);
+            Assert.Equal(new DateTime(2013, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(), rateLimit.AccessLimitResetDate);
+
+            var mediaLimit = status.MediaUploadLimit;
+            Assert.Equal(30, mediaLimit.AccessLimitCount);
+            Assert.Equal(20, mediaLimit.AccessLimitRemain);
+            Assert.Equal(new DateTime(2013, 1, 2, 0, 0, 0, DateTimeKind.Utc).ToLocalTime(), mediaLimit.AccessLimitResetDate);
+
+            Assert.Equal(TwitterApiAccessLevel.ReadWriteAndDirectMessage, status.AccessLevel);
+
+            Assert.True(eventCalled);
+        }
+
         [Fact(Skip = "Mono環境でエラーが発生する")]
         public void UpdateFromJsonTest()
         {
index 785f584..488e6c1 100644 (file)
@@ -93,7 +93,8 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.GetAsync<TwitterStatus>(
                         new Uri("statuses/show.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "id", "100" }, { "include_entities", "true" } })
+                        new Dictionary<string, string> { { "id", "100" }, { "include_entities", "true" } },
+                        "/statuses/show/:id")
                 )
                 .ReturnsAsync(new TwitterStatus { Id = 100L });
 
@@ -161,7 +162,8 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.GetAsync<TwitterUser>(
                         new Uri("users/show.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "screen_name", "twitterapi" }, { "include_entities", "true" } })
+                        new Dictionary<string, string> { { "screen_name", "twitterapi" }, { "include_entities", "true" } },
+                        "/users/show/:id")
                 )
                 .ReturnsAsync(new TwitterUser { ScreenName = "twitterapi" });
 
@@ -252,7 +254,8 @@ namespace OpenTween.Api
                 mock.Setup(x =>
                     x.GetAsync<TwitterFriendship>(
                         new Uri("friendships/show.json", UriKind.Relative),
-                        new Dictionary<string, string> { { "source_screen_name", "twitter" }, { "target_screen_name", "twitterapi" } })
+                        new Dictionary<string, string> { { "source_screen_name", "twitter" }, { "target_screen_name", "twitterapi" } },
+                        "/friendships/show")
                 )
                 .ReturnsAsync(new TwitterFriendship());
 
index 8f34066..741e67d 100644 (file)
@@ -94,6 +94,49 @@ namespace OpenTween.Connection
         }
 
         [Fact]
+        public async Task GetAsync_UpdateRateLimitTest()
+        {
+            using (var mockHandler = new HttpMessageHandlerMock())
+            using (var http = new HttpClient(mockHandler))
+            using (var apiConnection = new TwitterApiConnection("", ""))
+            {
+                apiConnection.http = http;
+
+                mockHandler.Enqueue(x =>
+                {
+                    Assert.Equal(HttpMethod.Get, x.Method);
+                    Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
+                        x.RequestUri.GetLeftPart(UriPartial.Path));
+
+                    return new HttpResponseMessage(HttpStatusCode.OK)
+                    {
+                        Headers =
+                        {
+                            { "X-Rate-Limit-Limit", "150" },
+                            { "X-Rate-Limit-Remaining", "100" },
+                            { "X-Rate-Limit-Reset", "1356998400" },
+                            { "X-Access-Level", "read-write-directmessages" },
+                        },
+                        Content = new StringContent("\"hogehoge\""),
+                    };
+                });
+
+                var apiStatus = new TwitterApiStatus();
+                MyCommon.TwitterApiInfo = apiStatus;
+
+                var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
+
+                await apiConnection.GetAsync<string>(endpoint, null, endpointName: "/hoge/tetete")
+                    .ConfigureAwait(false);
+
+                Assert.Equal(apiStatus.AccessLevel, TwitterApiAccessLevel.ReadWriteAndDirectMessage);
+                Assert.Equal(apiStatus.AccessLimit["/hoge/tetete"], new ApiLimit(150, 100, new DateTime(2013, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()));
+
+                Assert.Equal(0, mockHandler.QueueCount);
+            }
+        }
+
+        [Fact]
         public async Task GetAsync_ErrorStatusTest()
         {
             using (var mockHandler = new HttpMessageHandlerMock())
index 7b42c57..58fe3ae 100644 (file)
@@ -56,7 +56,7 @@ namespace OpenTween.Api
                 ["include_entities"] = "true",
             };
 
-            return this.apiConnection.GetAsync<TwitterStatus>(endpoint, param);
+            return this.apiConnection.GetAsync<TwitterStatus>(endpoint, param, "/statuses/show/:id");
         }
 
         public Task<LazyJson<TwitterStatus>> StatusesDestroy(long statusId)
@@ -90,7 +90,7 @@ namespace OpenTween.Api
                 ["include_entities"] = "true",
             };
 
-            return this.apiConnection.GetAsync<TwitterUser>(endpoint, param);
+            return this.apiConnection.GetAsync<TwitterUser>(endpoint, param, "/users/show/:id");
         }
 
         public Task<LazyJson<TwitterUser>> UsersReportSpam(string screenName)
@@ -135,7 +135,7 @@ namespace OpenTween.Api
                 ["target_screen_name"] = targetScreenName,
             };
 
-            return this.apiConnection.GetAsync<TwitterFriendship>(endpoint, param);
+            return this.apiConnection.GetAsync<TwitterFriendship>(endpoint, param, "/friendships/show");
         }
 
         public Task<LazyJson<TwitterFriendship>> FriendshipsCreate(string screenName)
index 68a0c0e..6190e82 100644 (file)
@@ -30,6 +30,7 @@ using System.Xml;
 using System.Xml.Linq;
 using System.Xml.XPath;
 using OpenTween.Api.DataModel;
+using System.Net.Http.Headers;
 
 namespace OpenTween.Api
 {
@@ -111,7 +112,12 @@ namespace OpenTween.Api
             }
 
             return null;
-        }   
+        }
+
+        public void UpdateFromHeader(HttpResponseHeaders header, string endpointName)
+        {
+            this.UpdateFromHeader(header.ToDictionary(x => x.Key, x => string.Join(",", x.Value)), endpointName);
+        }
 
         public void UpdateFromHeader(IDictionary<string, string> header, string endpointName)
         {
index 2691577..41d93e5 100644 (file)
@@ -30,6 +30,7 @@ namespace OpenTween.Connection
     public interface IApiConnection : IDisposable
     {
         Task<T> GetAsync<T>(Uri uri, IDictionary<string, string> param);
+        Task<T> GetAsync<T>(Uri uri, IDictionary<string, string> param, string endpointName);
 
         Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string> param);
 
index 7cd2d4a..8cc303e 100644 (file)
@@ -52,7 +52,10 @@ namespace OpenTween.Connection
             Networking.WebProxyChanged += this.Networking_WebProxyChanged;
         }
 
-        public async Task<T> GetAsync<T>(Uri uri, IDictionary<string, string> param)
+        public Task<T> GetAsync<T>(Uri uri, IDictionary<string, string> param)
+            => this.GetAsync<T>(uri, param, null);
+
+        public async Task<T> GetAsync<T>(Uri uri, IDictionary<string, string> param, string endpointName)
         {
             var requestUri = new Uri(this.RestApiBase, uri);
 
@@ -69,6 +72,9 @@ namespace OpenTween.Connection
                     await this.CheckStatusCode(response)
                         .ConfigureAwait(false);
 
+                    if (endpointName != null)
+                        MyCommon.TwitterApiInfo.UpdateFromHeader(response.Headers, endpointName);
+
                     using (var content = response.Content)
                     {
                         var responseText = await content.ReadAsStringAsync()