OSDN Git Service

TwitterApi.MediaMetadataCreateメソッドを追加
authorKimura Youichi <kim.upsilon@bucyou.net>
Fri, 27 May 2016 17:42:40 +0000 (02:42 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Fri, 27 May 2016 19:42:55 +0000 (04:42 +0900)
OpenTween.Tests/Api/TwitterApiTest.cs
OpenTween.Tests/Connection/TwitterApiConnectionTest.cs
OpenTween/Api/TwitterApi.cs
OpenTween/Connection/IApiConnection.cs
OpenTween/Connection/TwitterApiConnection.cs

index 5e6c7a4..38ad4a3 100644 (file)
@@ -1192,6 +1192,28 @@ namespace OpenTween.Api
         }
 
         [Fact]
+        public async Task MediaMetadataCreate_Test()
+        {
+            using (var twitterApi = new TwitterApi())
+            {
+                var mock = new Mock<IApiConnection>();
+                mock.Setup(x =>
+                    x.PostJsonAsync(
+                        new Uri("https://upload.twitter.com/1.1/media/metadata/create.json", UriKind.Absolute),
+                        "{\"media_id\": \"12345\", \"alt_text\": {\"text\": \"hogehoge\"}}")
+                )
+                .Returns(Task.FromResult(0));
+
+                twitterApi.apiConnection = mock.Object;
+
+                await twitterApi.MediaMetadataCreate(mediaId: 12345L, altText: "hogehoge")
+                    .ConfigureAwait(false);
+
+                mock.VerifyAll();
+            }
+        }
+
+        [Fact]
         public async Task UserStreams_Test()
         {
             using (var twitterApi = new TwitterApi())
@@ -1217,5 +1239,17 @@ namespace OpenTween.Api
                 mock.VerifyAll();
             }
         }
+
+        [Theory]
+        [InlineData("", "")]
+        [InlineData("123ABCabc", "123ABCabc")]
+        [InlineData(@"\", @"\\")]
+        [InlineData("\"", "\\\"")]
+        [InlineData("\n", @"\u000A")]
+        [InlineData("\U0001D11E", @"\uD834\uDD1E")]
+        public void EscapeJsonString_Test(string targetText, string expectedText)
+        {
+            Assert.Equal(expectedText, TwitterApi.EscapeJsonString(targetText));
+        }
     }
 }
index a36b670..763e85c 100644 (file)
@@ -456,5 +456,42 @@ namespace OpenTween.Connection
                 Assert.Equal(0, mockHandler.QueueCount);
             }
         }
+
+        [Fact]
+        public async Task PostJsonAsync_Test()
+        {
+            using (var mockHandler = new HttpMessageHandlerMock())
+            using (var http = new HttpClient(mockHandler))
+            using (var apiConnection = new TwitterApiConnection("", ""))
+            {
+                apiConnection.http = http;
+
+                mockHandler.Enqueue(async x =>
+                {
+                    Assert.Equal(HttpMethod.Post, x.Method);
+                    Assert.Equal("https://api.twitter.com/1.1/hoge/tetete.json",
+                        x.RequestUri.AbsoluteUri);
+
+                    Assert.Equal("application/json; charset=utf-8", x.Content.Headers.ContentType.ToString());
+
+                    var body = await x.Content.ReadAsStringAsync()
+                        .ConfigureAwait(false);
+
+                    Assert.Equal("{\"aaaa\": 1111}", body);
+
+                    return new HttpResponseMessage(HttpStatusCode.OK)
+                    {
+                        Content = new StringContent("\"hogehoge\""),
+                    };
+                });
+
+                var endpoint = new Uri("hoge/tetete.json", UriKind.Relative);
+
+                await apiConnection.PostJsonAsync(endpoint, "{\"aaaa\": 1111}")
+                    .ConfigureAwait(false);
+
+                Assert.Equal(0, mockHandler.QueueCount);
+            }
+        }
     }
 }
index 4a22298..f1a966f 100644 (file)
@@ -650,6 +650,16 @@ namespace OpenTween.Api
             return this.apiConnection.PostLazyAsync<TwitterUploadMediaResult>(endpoint, null, paramMedia);
         }
 
+        public Task MediaMetadataCreate(long mediaId, string altText)
+        {
+            var endpoint = new Uri("https://upload.twitter.com/1.1/media/metadata/create.json");
+
+            var escapedAltText = EscapeJsonString(altText);
+            var json = $@"{{""media_id"": ""{mediaId}"", ""alt_text"": {{""text"": ""{escapedAltText}""}}}}";
+
+            return this.apiConnection.PostJsonAsync(endpoint, json);
+        }
+
         public Task<Stream> UserStreams(string replies = null, string track = null)
         {
             var endpoint = new Uri("https://userstream.twitter.com/1.1/user.json");
@@ -672,5 +682,23 @@ namespace OpenTween.Api
         {
             this.apiConnection?.Dispose();
         }
+
+        /// <summary>JSON に出力する文字列を ECMA-404 に従ってエスケープする</summary>
+        public static string EscapeJsonString(string rawText)
+        {
+            var builder = new StringBuilder(rawText.Length + 20);
+
+            foreach (var c in rawText)
+            {
+                if (c <= 0x1F || char.IsSurrogate(c))
+                    builder.AppendFormat(@"\u{0:X4}", (int)c);
+                else if (c == '\\' || c == '\"')
+                    builder.Append('\\').Append(c);
+                else
+                    builder.Append(c);
+            }
+
+            return builder.ToString();
+        }
     }
 }
index 60e8984..2fca29c 100644 (file)
@@ -37,5 +37,7 @@ namespace OpenTween.Connection
         Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string> param);
 
         Task<LazyJson<T>> PostLazyAsync<T>(Uri uri, IDictionary<string, string> param, IDictionary<string, IMediaItem> media);
+
+        Task PostJsonAsync(Uri uri, string json);
     }
 }
index 4b03c31..81e2734 100644 (file)
@@ -217,6 +217,35 @@ namespace OpenTween.Connection
             }
         }
 
+        public async Task PostJsonAsync(Uri uri, string json)
+        {
+            var requestUri = new Uri(RestApiBase, uri);
+            var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
+
+            using (var postContent = new StringContent(json, Encoding.UTF8, "application/json"))
+            {
+                request.Content = postContent;
+
+                try
+                {
+                    using (var response = await this.http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)
+                        .ConfigureAwait(false))
+                    {
+                        await this.CheckStatusCode(response)
+                            .ConfigureAwait(false);
+                    }
+                }
+                catch (HttpRequestException ex)
+                {
+                    throw TwitterApiException.CreateFromException(ex);
+                }
+                catch (OperationCanceledException ex)
+                {
+                    throw TwitterApiException.CreateFromException(ex);
+                }
+            }
+        }
+
         protected async Task CheckStatusCode(HttpResponseMessage response)
         {
             var statusCode = response.StatusCode;