var match = FoursquareCheckin.UrlPatternRegex.Match("https://www.swarmapp.com/c/xxxxxxxx");
Assert.True(match.Success);
Assert.Equal("xxxxxxxx", match.Groups["checkin_id"].Value);
- Assert.Equal("", match.Groups["signature"].Value);
-
- // signature 付きのチェックイン URL (www.swarmapp.com/c/***?s=***)
- match = FoursquareCheckin.UrlPatternRegex.Match("https://www.swarmapp.com/c/xxxxxxxx?s=aaaaaaa");
- Assert.True(match.Success);
- Assert.Equal("xxxxxxxx", match.Groups["checkin_id"].Value);
- Assert.Equal("aaaaaaa", match.Groups["signature"].Value);
+ }
+ [Fact]
+ public void LegacyUrlPattern_Test()
+ {
// 古い形式の URL (foursquare.com/***/checkin/***?s=***)
- match = FoursquareCheckin.UrlPatternRegex.Match("https://foursquare.com/hogehoge/checkin/xxxxxxxx?s=aaaaaaa");
+ var match = FoursquareCheckin.LegacyUrlPatternRegex.Match("https://foursquare.com/hogehoge/checkin/xxxxxxxx?s=aaaaaaa");
Assert.True(match.Success);
Assert.Equal("xxxxxxxx", match.Groups["checkin_id"].Value);
Assert.Equal("aaaaaaa", match.Groups["signature"].Value);
// 古い形式の URL (www.swarmapp.com/***/checkin/***?s=***)
- match = FoursquareCheckin.UrlPatternRegex.Match("https://www.swarmapp.com/hogehoge/checkin/xxxxxxxx?s=aaaaaaa");
+ match = FoursquareCheckin.LegacyUrlPatternRegex.Match("https://www.swarmapp.com/hogehoge/checkin/xxxxxxxx?s=aaaaaaa");
Assert.True(match.Success);
Assert.Equal("xxxxxxxx", match.Groups["checkin_id"].Value);
Assert.Equal("aaaaaaa", match.Groups["signature"].Value);
}
[Fact]
- public async Task GetThumbnailInfoAsync_RequestTest()
+ public async Task GetThumbnailInfoAsync_NewUrlTest()
+ {
+ var handler = new HttpMessageHandlerMock();
+ using (var http = new HttpClient(handler))
+ {
+ var service = new FoursquareCheckin(http);
+
+ handler.Enqueue(x =>
+ {
+ Assert.Equal(HttpMethod.Get, x.Method);
+ Assert.Equal("https://api.foursquare.com/v2/checkins/resolve",
+ x.RequestUri.GetLeftPart(UriPartial.Path));
+
+ var query = HttpUtility.ParseQueryString(x.RequestUri.Query);
+
+ Assert.Equal(ApplicationSettings.FoursquareClientId, query["client_id"]);
+ Assert.Equal(ApplicationSettings.FoursquareClientSecret, query["client_secret"]);
+ Assert.NotNull(query["v"]);
+ Assert.Equal("xxxxxxxx", query["shortId"]);
+
+ // リクエストに対するテストなのでレスポンスは適当に返す
+ return new HttpResponseMessage(HttpStatusCode.NotFound);
+ });
+
+ var post = new PostClass
+ {
+ PostGeo = null,
+ };
+
+ var thumb = await service.GetThumbnailInfoAsync(
+ "https://www.swarmapp.com/c/xxxxxxxx",
+ post, CancellationToken.None);
+
+ Assert.Equal(0, handler.QueueCount);
+ }
+ }
+
+ [Fact]
+ public async Task GetThumbnailInfoAsync_LegacyUrlTest()
{
var handler = new HttpMessageHandlerMock();
using (var http = new HttpClient(handler))
}
[Fact]
- public async Task GetThumbnailInfoAsync_RequestWithSignatureTest()
+ public async Task GetThumbnailInfoAsync_LegacyUrlWithSignatureTest()
{
var handler = new HttpMessageHandlerMock();
using (var http = new HttpClient(handler))
};
var thumb = await service.GetThumbnailInfoAsync(
- "https://foursquare.com/hogehoge/checkin/xxxxxxxx?s=aaaaaaa",
+ "https://www.swarmapp.com/c/xxxxxxxx",
post, CancellationToken.None);
Assert.Equal(1, handler.QueueCount);
class FoursquareCheckin : IThumbnailService
{
public static readonly Regex UrlPatternRegex =
- new Regex(@"^https?://(?:foursquare\.com|www\.swarmapp\.com)/(?:c/|.+?/checkin/)(?<checkin_id>[0-9a-z]+)(?:\?s=(?<signature>[^&]+))?");
+ new Regex(@"^https?://www\.swarmapp\.com/c/(?<checkin_id>[0-9a-zA-Z]+)");
+
+ public static readonly Regex LegacyUrlPatternRegex =
+ new Regex(@"^https?://(?:foursquare\.com|www\.swarmapp\.com)/.+?/checkin/(?<checkin_id>[0-9a-z]+)(?:\?s=(?<signature>[^&]+))?");
public static readonly string ApiBase = "https://api.foursquare.com/v2";
if (post.PostGeo != null)
return null;
+ var location = await this.FetchCheckinLocation(url, token)
+ .ConfigureAwait(false);
+
+ if (location == null)
+ {
+ location = await this.FetchCheckinLocationLegacy(url, token)
+ .ConfigureAwait(false);
+ }
+
+ if (location != null)
+ {
+ var map = MapThumb.GetDefaultInstance();
+
+ return await map.GetThumbnailInfoAsync(new PostClass.StatusGeo(location.Longitude, location.Latitude))
+ .ConfigureAwait(false);
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Foursquare のチェックイン URL から位置情報を取得します
+ /// </summary>
+ public async Task<GlobalLocation> FetchCheckinLocation(string url, CancellationToken token)
+ {
var match = UrlPatternRegex.Match(url);
+ if (!match.Success)
+ return null;
+
+ var checkinIdGroup = match.Groups["checkin_id"];
+
+ try
+ {
+ // Foursquare のチェックイン情報を取得
+ // 参照: https://developer.foursquare.com/docs/checkins/resolve
+
+ var query = new Dictionary<string, string>
+ {
+ ["client_id"] = ApplicationSettings.FoursquareClientId,
+ ["client_secret"] = ApplicationSettings.FoursquareClientSecret,
+ ["v"] = "20140419", // https://developer.foursquare.com/overview/versioning
+
+ ["shortId"] = checkinIdGroup.Value,
+ };
+
+ var apiUrl = new Uri(ApiBase + "/checkins/resolve?" + MyCommon.BuildQueryString(query));
+
+ using (var response = await this.http.GetAsync(apiUrl, token).ConfigureAwait(false))
+ {
+ response.EnsureSuccessStatusCode();
+
+ var jsonBytes = await response.Content.ReadAsByteArrayAsync()
+ .ConfigureAwait(false);
+
+ return ParseIntoLocation(jsonBytes);
+ }
+ }
+ catch (HttpRequestException)
+ {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Foursquare のチェックイン URL から位置情報を取得します (古い形式の URL)
+ /// </summary>
+ public async Task<GlobalLocation> FetchCheckinLocationLegacy(string url, CancellationToken token)
+ {
+ var match = LegacyUrlPatternRegex.Match(url);
if (!match.Success)
return null;
var jsonBytes = await response.Content.ReadAsByteArrayAsync()
.ConfigureAwait(false);
- var location = ParseIntoLocation(jsonBytes);
- if (location == null)
- return null;
-
- var map = MapThumb.GetDefaultInstance();
-
- return await map.GetThumbnailInfoAsync(new PostClass.StatusGeo(location.Longitude, location.Latitude))
- .ConfigureAwait(false);
+ return ParseIntoLocation(jsonBytes);
}
}
- catch (HttpRequestException) { }
-
- return null;
+ catch (HttpRequestException)
+ {
+ return null;
+ }
}
internal static GlobalLocation ParseIntoLocation(byte[] jsonBytes)