diff --git a/GiantBomb.Api.Tests/Search.cs b/GiantBomb.Api.Tests/Search.cs index ef41fa8..c9847ea 100644 --- a/GiantBomb.Api.Tests/Search.cs +++ b/GiantBomb.Api.Tests/Search.cs @@ -13,6 +13,9 @@ public void search_resource_should_return_one_result_for_skyrim() { Assert.IsNotNull(result); Assert.AreEqual(1, result.Count); Assert.AreEqual(33394, result.First().Id); + Assert.IsNotNull(result.First().Platforms); + Assert.IsNotNull(result.First().Platforms.First()); + Assert.IsTrue(result.First().Platforms.First().Id > 0, "Platform is invalid"); } [Test] @@ -58,5 +61,16 @@ public void search_resource_should_limit_fields_to_id_for_all_result() { Assert.IsTrue(result.All(g => g.Id > 0)); Assert.IsTrue(result.All(g => string.IsNullOrWhiteSpace(g.Name))); } + + /// + /// BUGFIX: "mario" returns dup resultset + /// + [Test] + public void search_resource_should_not_return_duplicates_for_mario() { + var result = _client.SearchForAllGames("mario", limitFields: new[] { "id" }).ToList(); + + Assert.IsNotNull(result); + Assert.IsFalse(result.Any(r => result.Count(r2 => r2.Id == r.Id) > 1), "Results contain duplicate IDs"); + } } } diff --git a/GiantBomb.Api/Core.cs b/GiantBomb.Api/Core.cs index d69b77a..c39d543 100644 --- a/GiantBomb.Api/Core.cs +++ b/GiantBomb.Api/Core.cs @@ -1,161 +1,168 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Text; -using GiantBomb.Api.Model; -using RestSharp; - -namespace GiantBomb.Api { - public partial class GiantBombRestClient : IGiantBombRestClient { - private readonly RestClient _client; - - /// - /// Base URL of API (defaults to http://www.giantbomb.com/api/) - /// - public string BaseUrl { get; set; } - - /// - /// Your GiantBomb API token - /// - private string ApiKey { get; set; } - - /// - /// Create a new Rest client with your API token and custom base URL - /// - /// Your secret API token - /// The base API URL, for example, pre-release API versions - public GiantBombRestClient(string apiToken, Uri baseUrl) { - BaseUrl = baseUrl.ToString(); - ApiKey = apiToken; - - var assembly = Assembly.GetExecutingAssembly(); - var version = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location).ProductVersion; - - _client = new RestClient - { - UserAgent = "giantbomb-csharp/" + version, - BaseUrl = BaseUrl - }; - - // API token is used on every request - _client.AddDefaultParameter("api_key", ApiKey); - _client.AddDefaultParameter("format", "json"); - - // Deserializer - _client.AddHandler("application/text+json", new FastJsonDeserializer()); - } - - public GiantBombRestClient(string apiToken) - : this(apiToken, new Uri("http://www.giantbomb.com/api/")) { - - } - -#if FRAMEWORK - /// - /// Execute a manual REST request - /// - /// The type of object to create and populate with the returned data. - /// The RestRequest to execute (will use client credentials) - public T Execute(RestRequest request) where T : new() { - var response = _client.Execute(request); - return response.Data; - } - - /// - /// Execute a manual REST request - /// - /// The RestRequest to execute (will use client credentials) - public IRestResponse Execute(RestRequest request) { - return _client.Execute(request); - } -#endif - - public virtual RestRequest GetListResource(string resource, int page = 1, int pageSize = GiantBombBase.DefaultLimit, string[] fieldList = null, IDictionary sortOptions = null, IDictionary filterOptions = null) { - if (pageSize > GiantBombBase.DefaultLimit) - throw new ArgumentOutOfRangeException("pageSize", "Page size cannot be greater than " + GiantBombBase.DefaultLimit + "."); - - var request = new RestRequest { - Resource = resource + "//", - DateFormat = "yyyy-MM-dd HH:mm:ss" - }; - - if (page > 1) { - request.AddParameter("offset", pageSize * (page - 1)); - } - - request.AddParameter("limit", pageSize); - - if (fieldList != null) - request.AddParameter("field_list", String.Join(",", fieldList)); - - if (sortOptions != null) - request.AddParameter("sort", BuildKeyValueListForUrl(sortOptions)); - - if (filterOptions != null) - request.AddParameter("filter", BuildKeyValueListForUrl(filterOptions)); - - return request; - } - - private string BuildKeyValueListForUrl(IEnumerable> dictionary) - { - // format is like :,: - return String.Join(",", (from pair in dictionary - select pair.Key + ":" + pair.Value).ToArray()); - } - - private string BuildKeyValueListForUrl(IEnumerable> sortOptions) - { - - var sortDictionary = new Dictionary(); - - foreach(var kv in sortOptions) - sortDictionary.Add(kv.Key, kv.Value == SortDirection.Ascending ? "asc" : "desc"); - - return BuildKeyValueListForUrl(sortDictionary); - } - - public virtual IEnumerable GetListResource(string resource, int page = 1, int pageSize = GiantBombBase.DefaultLimit, string[] fieldList = null, IDictionary sortOptions = null, IDictionary filterOptions = null) where TResult : new() { - var request = GetListResource(resource, page, pageSize, fieldList, sortOptions, filterOptions); - var results = Execute>(request); - - if (results != null && results.StatusCode == GiantBombBase.StatusOk) - return results.Results; - - return null; - } - - public virtual RestRequest GetSingleResource(string resource, int resourceId, int id, string[] fieldList = null) { - var request = new RestRequest { - Resource = resource + "/{ResourceId}-{Id}//", - DateFormat = "yyyy-MM-dd HH:mm:ss" - }; - - request.AddUrlSegment("ResourceId", resourceId.ToString(CultureInfo.InvariantCulture)); - request.AddUrlSegment("Id", id.ToString(CultureInfo.InvariantCulture)); - - if (fieldList != null) - request.AddParameter("field_list", String.Join(",", fieldList)); - - return request; - } - - public virtual TResult GetSingleResource(string resource, int resourceId, int id, string[] fieldList = null) where TResult : class, new() { - var request = GetSingleResource(resource, resourceId, id, fieldList); - var result = Execute>(request); - - if (result != null && result.StatusCode == GiantBombBase.StatusOk) - return result.Results; - - return null; - } - } - - public enum SortDirection - { - Ascending, - Descending - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using GiantBomb.Api.Model; +using RestSharp; + +namespace GiantBomb.Api { + public partial class GiantBombRestClient : IGiantBombRestClient { + private readonly RestClient _client; + + /// + /// Base URL of API (defaults to http://www.giantbomb.com/api/) + /// + public string BaseUrl { get; set; } + + /// + /// Your GiantBomb API token + /// + private string ApiKey { get; set; } + + /// + /// Create a new Rest client with your API token and custom base URL + /// + /// Your secret API token + /// The base API URL, for example, pre-release API versions + public GiantBombRestClient(string apiToken, Uri baseUrl) { + BaseUrl = baseUrl.ToString(); + ApiKey = apiToken; + + var assembly = Assembly.GetExecutingAssembly(); + var version = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location).ProductVersion; + + _client = new RestClient + { + UserAgent = "giantbomb-csharp/" + version, + BaseUrl = BaseUrl + }; + + // API token is used on every request + _client.AddDefaultParameter("api_key", ApiKey); + _client.AddDefaultParameter("format", "json"); + + // Deserializer + _client.AddHandler("application/text+json", new FastJsonDeserializer()); + } + + public GiantBombRestClient(string apiToken) + : this(apiToken, new Uri("http://www.giantbomb.com/api/")) { + + } + +#if FRAMEWORK + /// + /// Execute a manual REST request + /// + /// The type of object to create and populate with the returned data. + /// The RestRequest to execute (will use client credentials) + public T Execute(RestRequest request) where T : new() { + var response = _client.Execute(request); + return response.Data; + } + + /// + /// Execute a manual REST request + /// + /// The RestRequest to execute (will use client credentials) + public IRestResponse Execute(RestRequest request) { + return _client.Execute(request); + } +#endif + + public virtual RestRequest GetListResource(string resource, int page = 1, int pageSize = GiantBombBase.DefaultLimit, string[] fieldList = null, IDictionary sortOptions = null, IDictionary filterOptions = null) { + if (pageSize > GiantBombBase.DefaultLimit) + throw new ArgumentOutOfRangeException("pageSize", "Page size cannot be greater than " + GiantBombBase.DefaultLimit + "."); + + var request = new RestRequest { + Resource = resource + "//", + DateFormat = "yyyy-MM-dd HH:mm:ss" + }; + + if (page > 1) { + + // HACK: Giant Bomb uses `page` for search instead of `offset`, assholes + if (resource == "search") { + request.AddParameter("page", page); + } + else { + request.AddParameter("offset", pageSize*(page - 1)); + } + } + + request.AddParameter("limit", pageSize); + + if (fieldList != null) + request.AddParameter("field_list", String.Join(",", fieldList)); + + if (sortOptions != null) + request.AddParameter("sort", BuildKeyValueListForUrl(sortOptions)); + + if (filterOptions != null) + request.AddParameter("filter", BuildKeyValueListForUrl(filterOptions)); + + return request; + } + + private string BuildKeyValueListForUrl(IEnumerable> dictionary) + { + // format is like :,: + return String.Join(",", (from pair in dictionary + select pair.Key + ":" + pair.Value).ToArray()); + } + + private string BuildKeyValueListForUrl(IEnumerable> sortOptions) + { + + var sortDictionary = new Dictionary(); + + foreach(var kv in sortOptions) + sortDictionary.Add(kv.Key, kv.Value == SortDirection.Ascending ? "asc" : "desc"); + + return BuildKeyValueListForUrl(sortDictionary); + } + + public virtual IEnumerable GetListResource(string resource, int page = 1, int pageSize = GiantBombBase.DefaultLimit, string[] fieldList = null, IDictionary sortOptions = null, IDictionary filterOptions = null) where TResult : new() { + var request = GetListResource(resource, page, pageSize, fieldList, sortOptions, filterOptions); + var results = Execute>(request); + + if (results != null && results.StatusCode == GiantBombBase.StatusOk) + return results.Results; + + return null; + } + + public virtual RestRequest GetSingleResource(string resource, int resourceId, int id, string[] fieldList = null) { + var request = new RestRequest { + Resource = resource + "/{ResourceId}-{Id}//", + DateFormat = "yyyy-MM-dd HH:mm:ss" + }; + + request.AddUrlSegment("ResourceId", resourceId.ToString(CultureInfo.InvariantCulture)); + request.AddUrlSegment("Id", id.ToString(CultureInfo.InvariantCulture)); + + if (fieldList != null) + request.AddParameter("field_list", String.Join(",", fieldList)); + + return request; + } + + public virtual TResult GetSingleResource(string resource, int resourceId, int id, string[] fieldList = null) where TResult : class, new() { + var request = GetSingleResource(resource, resourceId, id, fieldList); + var result = Execute>(request); + + if (result != null && result.StatusCode == GiantBombBase.StatusOk) + return result.Results; + + return null; + } + } + + public enum SortDirection + { + Ascending, + Descending + } +} diff --git a/GiantBomb.Api/Properties/AssemblyInfo.cs b/GiantBomb.Api/Properties/AssemblyInfo.cs index 102b34e..d86d5a1 100644 --- a/GiantBomb.Api/Properties/AssemblyInfo.cs +++ b/GiantBomb.Api/Properties/AssemblyInfo.cs @@ -1,17 +1,17 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("GiantBomb.Api")] -[assembly: AssemblyDescription("RestSharp-based API wrapper for the GiantBomb games database public API")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("kayub")] -[assembly: AssemblyProduct("GiantBomb.Api")] -[assembly: AssemblyCopyright("Copyright © kayub 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -[assembly: Guid("5d910805-e5ae-48f0-8e39-d37104d7a91e")] - -[assembly: AssemblyVersion("2.0.2")] -[assembly: AssemblyFileVersion("2.0.2")] -[assembly: AssemblyInformationalVersion("2.0.2")] \ No newline at end of file +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("GiantBomb.Api")] +[assembly: AssemblyDescription("RestSharp-based API wrapper for the GiantBomb games database public API")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("kayub")] +[assembly: AssemblyProduct("GiantBomb.Api")] +[assembly: AssemblyCopyright("Copyright © kayub 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("5d910805-e5ae-48f0-8e39-d37104d7a91e")] + +[assembly: AssemblyVersion("2.0.3")] +[assembly: AssemblyFileVersion("2.0.3")] +[assembly: AssemblyInformationalVersion("2.0.3")] \ No newline at end of file diff --git a/GiantBomb.Api/readme.txt b/GiantBomb.Api/readme.txt index 81ae1f3..93f5b4a 100644 --- a/GiantBomb.Api/readme.txt +++ b/GiantBomb.Api/readme.txt @@ -1,54 +1,54 @@ -GiantBomb C# ------------- - -## API v2 Support - -GiantBomb-C# 2.0+ is only compatible with GiantBomb APIv2. Keep using the old packages if you need v1 support, as there are breaking changes in v2! - -### Notable Changes - -* Search is now MUCH better in GiantBomb's v2 API, there's little to no need for `SearchAllGames` now unless you expect/want more than 100 results -* List resources support new `sort` and `filter` options -* All single resource requests must use a resource ID, e.g. "game/3030-33394", which GBCS implements transparently for you -* `Game` and `Release` now have `ExpectedReleaseDay` -* Search results now include platforms -* `Game` has two new fields: - - `Aliases` - newline delimited aliases - - `OriginalGameRating` -* You can now use `GetReleasesForGame()` to directly retrieve releases for a game in one request -* Using [FastJSON](http://www.codeproject.com/Articles/159450/fastJSON) to deserialize, which is... fast, obviously - -### Breaking Changes/Known Issues - -* Platform/Release `DateLastUpdated` is spelled incorrectly on the API (#10) - -## Readme - -This library aims to wrap the GiantBomb REST API in C# with strongly-typed models and is built on top of [RestSharp](https://github.com/johnsheehan/RestSharp). - -It also helps make your life easier when dealing with searching because it recursively fetches your search results all at once to enable better sorting. - -**Note: This is not really needed anymore due to search improvements in APIv2** - - var giantBomb = new GiantBombRestClient(); - - // Get all search results - var results = giantBomb.SearchForAllGames("assassin's creed"); - - // Display - return results.OrderByDescending(g => g.DateAdded) - - -## Nuget -Download and install the GiantBomb.Api Nuget package: - - PM> Install-Package GiantBomb.Api - -## Contributing -Read about [contributing on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). If you plan to contribute, you **must** read this. - -## Examples -Read about [examples on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). - -## License -Dual-licensed on MIT & GPL \ No newline at end of file +GiantBomb C# +------------ + +## 2.0.3 + +- Fixes issue with search paging (`offset` vs. `page` parameter) + +## API v2 Support + +GiantBomb-C# 2.0+ is only compatible with GiantBomb APIv2. Keep using the old packages if you need v1 support, as there are breaking changes in v2! + +### Notable Changes + +* Search is now MUCH better in GiantBomb's v2 API, there's little to no need for `SearchAllGames` now unless you expect/want more than 100 results +* List resources support new `sort` and `filter` options +* All single resource requests must use a resource ID, e.g. "game/3030-33394", which GBCS implements transparently for you +* `Game` and `Release` now have `ExpectedReleaseDay` +* Search results now include platforms +* `Game` has two new fields: + - `Aliases` - newline delimited aliases + - `OriginalGameRating` +* You can now use `GetReleasesForGame()` to directly retrieve releases for a game in one request +* Using [FastJSON](http://www.codeproject.com/Articles/159450/fastJSON) to deserialize, which is... fast, obviously + +## Readme + +This library aims to wrap the GiantBomb REST API in C# with strongly-typed models and is built on top of [RestSharp](https://github.com/johnsheehan/RestSharp). + +It also helps make your life easier when dealing with searching because it recursively fetches your search results all at once to enable better sorting. + +**Note: This is not really needed anymore due to search improvements in APIv2** + + var giantBomb = new GiantBombRestClient(); + + // Get all search results + var results = giantBomb.SearchForAllGames("assassin's creed"); + + // Display + return results.OrderByDescending(g => g.DateAdded) + + +## Nuget +Download and install the GiantBomb.Api Nuget package: + + PM> Install-Package GiantBomb.Api + +## Contributing +Read about [contributing on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). If you plan to contribute, you **must** read this. + +## Examples +Read about [examples on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). + +## License +Dual-licensed on MIT & GPL diff --git a/Readme.md b/Readme.md index 0e9bc82..93f5b4a 100644 --- a/Readme.md +++ b/Readme.md @@ -1,50 +1,54 @@ -GiantBomb C# ------------- - -## API v2 Support - -GiantBomb-C# 2.0+ is only compatible with GiantBomb APIv2. Keep using the old packages if you need v1 support, as there are breaking changes in v2! - -### Notable Changes - -* Search is now MUCH better in GiantBomb's v2 API, there's little to no need for `SearchAllGames` now unless you expect/want more than 100 results -* List resources support new `sort` and `filter` options -* All single resource requests must use a resource ID, e.g. "game/3030-33394", which GBCS implements transparently for you -* `Game` and `Release` now have `ExpectedReleaseDay` -* Search results now include platforms -* `Game` has two new fields: - - `Aliases` - newline delimited aliases - - `OriginalGameRating` -* You can now use `GetReleasesForGame()` to directly retrieve releases for a game in one request -* Using [FastJSON](http://www.codeproject.com/Articles/159450/fastJSON) to deserialize, which is... fast, obviously - -## Readme - -This library aims to wrap the GiantBomb REST API in C# with strongly-typed models and is built on top of [RestSharp](https://github.com/johnsheehan/RestSharp). - -It also helps make your life easier when dealing with searching because it recursively fetches your search results all at once to enable better sorting. - -**Note: This is not really needed anymore due to search improvements in APIv2** - - var giantBomb = new GiantBombRestClient(); - - // Get all search results - var results = giantBomb.SearchForAllGames("assassin's creed"); - - // Display - return results.OrderByDescending(g => g.DateAdded) - - -## Nuget -Download and install the GiantBomb.Api Nuget package: - - PM> Install-Package GiantBomb.Api - -## Contributing -Read about [contributing on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). If you plan to contribute, you **must** read this. - -## Examples -Read about [examples on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). - -## License -Dual-licensed on MIT & GPL +GiantBomb C# +------------ + +## 2.0.3 + +- Fixes issue with search paging (`offset` vs. `page` parameter) + +## API v2 Support + +GiantBomb-C# 2.0+ is only compatible with GiantBomb APIv2. Keep using the old packages if you need v1 support, as there are breaking changes in v2! + +### Notable Changes + +* Search is now MUCH better in GiantBomb's v2 API, there's little to no need for `SearchAllGames` now unless you expect/want more than 100 results +* List resources support new `sort` and `filter` options +* All single resource requests must use a resource ID, e.g. "game/3030-33394", which GBCS implements transparently for you +* `Game` and `Release` now have `ExpectedReleaseDay` +* Search results now include platforms +* `Game` has two new fields: + - `Aliases` - newline delimited aliases + - `OriginalGameRating` +* You can now use `GetReleasesForGame()` to directly retrieve releases for a game in one request +* Using [FastJSON](http://www.codeproject.com/Articles/159450/fastJSON) to deserialize, which is... fast, obviously + +## Readme + +This library aims to wrap the GiantBomb REST API in C# with strongly-typed models and is built on top of [RestSharp](https://github.com/johnsheehan/RestSharp). + +It also helps make your life easier when dealing with searching because it recursively fetches your search results all at once to enable better sorting. + +**Note: This is not really needed anymore due to search improvements in APIv2** + + var giantBomb = new GiantBombRestClient(); + + // Get all search results + var results = giantBomb.SearchForAllGames("assassin's creed"); + + // Display + return results.OrderByDescending(g => g.DateAdded) + + +## Nuget +Download and install the GiantBomb.Api Nuget package: + + PM> Install-Package GiantBomb.Api + +## Contributing +Read about [contributing on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). If you plan to contribute, you **must** read this. + +## Examples +Read about [examples on the wiki](https://github.com/kamranayub/GiantBomb-CSharp/wiki). + +## License +Dual-licensed on MIT & GPL