Skip to content

Commit

Permalink
Fix search paging
Browse files Browse the repository at this point in the history
  • Loading branch information
kamranayub committed Nov 12, 2013
1 parent bcbbd3a commit f7ad5af
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 282 deletions.
14 changes: 14 additions & 0 deletions GiantBomb.Api.Tests/Search.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public class Search : TestBase {
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]
Expand Down Expand Up @@ -58,5 +61,16 @@ public class Search : TestBase {
Assert.IsTrue(result.All(g => g.Id > 0));
Assert.IsTrue(result.All(g => string.IsNullOrWhiteSpace(g.Name)));
}

/// <summary>
/// BUGFIX: "mario" returns dup resultset
/// </summary>
[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");
}
}
}
329 changes: 168 additions & 161 deletions GiantBomb.Api/Core.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Base URL of API (defaults to http://www.giantbomb.com/api/)
/// </summary>
public string BaseUrl { get; set; }

/// <summary>
/// Your GiantBomb API token
/// </summary>
private string ApiKey { get; set; }

/// <summary>
/// Create a new Rest client with your API token and custom base URL
/// </summary>
/// <param name="apiToken">Your secret API token</param>
/// <param name="baseUrl">The base API URL, for example, pre-release API versions</param>
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
/// <summary>
/// Execute a manual REST request
/// </summary>
/// <typeparam name="T">The type of object to create and populate with the returned data.</typeparam>
/// <param name="request">The RestRequest to execute (will use client credentials)</param>
public T Execute<T>(RestRequest request) where T : new() {
var response = _client.Execute<T>(request);
return response.Data;
}

/// <summary>
/// Execute a manual REST request
/// </summary>
/// <param name="request">The RestRequest to execute (will use client credentials)</param>
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<string, SortDirection> sortOptions = null, IDictionary<string, object> 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<KeyValuePair<string, object>> dictionary)
{
// format is like <key>:<value>,<key>:<value>
return String.Join(",", (from pair in dictionary
select pair.Key + ":" + pair.Value).ToArray());
}

private string BuildKeyValueListForUrl(IEnumerable<KeyValuePair<string, SortDirection>> sortOptions)
{

var sortDictionary = new Dictionary<string, object>();

foreach(var kv in sortOptions)
sortDictionary.Add(kv.Key, kv.Value == SortDirection.Ascending ? "asc" : "desc");

return BuildKeyValueListForUrl(sortDictionary);
}

public virtual IEnumerable<TResult> GetListResource<TResult>(string resource, int page = 1, int pageSize = GiantBombBase.DefaultLimit, string[] fieldList = null, IDictionary<string, SortDirection> sortOptions = null, IDictionary<string, object> filterOptions = null) where TResult : new() {
var request = GetListResource(resource, page, pageSize, fieldList, sortOptions, filterOptions);
var results = Execute<GiantBombResults<TResult>>(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<TResult>(string resource, int resourceId, int id, string[] fieldList = null) where TResult : class, new() {
var request = GetSingleResource(resource, resourceId, id, fieldList);
var result = Execute<GiantBombResult<TResult>>(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;

/// <summary>
/// Base URL of API (defaults to http://www.giantbomb.com/api/)
/// </summary>
public string BaseUrl { get; set; }

/// <summary>
/// Your GiantBomb API token
/// </summary>
private string ApiKey { get; set; }

/// <summary>
/// Create a new Rest client with your API token and custom base URL
/// </summary>
/// <param name="apiToken">Your secret API token</param>
/// <param name="baseUrl">The base API URL, for example, pre-release API versions</param>
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
/// <summary>
/// Execute a manual REST request
/// </summary>
/// <typeparam name="T">The type of object to create and populate with the returned data.</typeparam>
/// <param name="request">The RestRequest to execute (will use client credentials)</param>
public T Execute<T>(RestRequest request) where T : new() {
var response = _client.Execute<T>(request);
return response.Data;
}

/// <summary>
/// Execute a manual REST request
/// </summary>
/// <param name="request">The RestRequest to execute (will use client credentials)</param>
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<string, SortDirection> sortOptions = null, IDictionary<string, object> 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<KeyValuePair<string, object>> dictionary)
{
// format is like <key>:<value>,<key>:<value>
return String.Join(",", (from pair in dictionary
select pair.Key + ":" + pair.Value).ToArray());
}

private string BuildKeyValueListForUrl(IEnumerable<KeyValuePair<string, SortDirection>> sortOptions)
{

var sortDictionary = new Dictionary<string, object>();

foreach(var kv in sortOptions)
sortDictionary.Add(kv.Key, kv.Value == SortDirection.Ascending ? "asc" : "desc");

return BuildKeyValueListForUrl(sortDictionary);
}

public virtual IEnumerable<TResult> GetListResource<TResult>(string resource, int page = 1, int pageSize = GiantBombBase.DefaultLimit, string[] fieldList = null, IDictionary<string, SortDirection> sortOptions = null, IDictionary<string, object> filterOptions = null) where TResult : new() {
var request = GetListResource(resource, page, pageSize, fieldList, sortOptions, filterOptions);
var results = Execute<GiantBombResults<TResult>>(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<TResult>(string resource, int resourceId, int id, string[] fieldList = null) where TResult : class, new() {
var request = GetSingleResource(resource, resourceId, id, fieldList);
var result = Execute<GiantBombResult<TResult>>(request);

if (result != null && result.StatusCode == GiantBombBase.StatusOk)
return result.Results;

return null;
}
}

public enum SortDirection
{
Ascending,
Descending
}
}
34 changes: 17 additions & 17 deletions GiantBomb.Api/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -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")]
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")]
Loading

0 comments on commit f7ad5af

Please sign in to comment.