Skip to content

Commit 0f9f4ce

Browse files
authored
Add QueryWithResponse API (#38)
1 parent c8ae4e6 commit 0f9f4ce

File tree

4 files changed

+82
-2
lines changed

4 files changed

+82
-2
lines changed

IGDB.Tests/Games.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ public async Task ShouldReturnResponseWithoutQuery()
2727
Assert.True(games.Length == 10);
2828
}
2929

30+
[Fact]
31+
public async Task ShouldReturnResponseWithHeaders()
32+
{
33+
var games = await _api.QueryWithResponseAsync<Game>(IGDBClient.Endpoints.Games);
34+
35+
Assert.NotNull(games);
36+
Assert.True(games.GetContent().Length == 10);
37+
38+
var rawCount = games.ResponseMessage.Headers.GetValues("x-count").First();
39+
40+
Assert.NotNull(rawCount);
41+
Assert.True(int.Parse(rawCount) > 0);
42+
43+
var queryCount = games.GetQueryCount();
44+
45+
Assert.NotNull(queryCount);
46+
Assert.True(queryCount > 0);
47+
}
48+
3049
[Fact]
3150
public async Task ShouldReturnResponseWithSingleGame()
3251
{
@@ -159,12 +178,12 @@ public async Task ShouldReturnGameWithDlc()
159178

160179
Assert.NotEmpty(game.Dlcs.Ids);
161180
}
162-
181+
163182
[Fact]
164183
public async Task ShouldReturnGameCount()
165184
{
166185
var gameCount = await _api.CountAsync(IGDBClient.Endpoints.Games, "where id = 4;");
167-
186+
168187
Assert.NotNull(gameCount);
169188
Assert.Equal(1, gameCount.Count);
170189
}

IGDB/IGDBApi.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ public interface IGDBApi
3030
[Post("/{endpoint}")]
3131
Task<T[]> QueryAsync<T>([Path] string endpoint, [Body] string query = null);
3232

33+
/// <summary>
34+
/// Queries a standard IGDB endpoint with an APIcalypse query and returns the deserialized response along with the HttpResponseMessage. See endpoints in <see cref="IGDB.IGDBClient.Endpoints" />.
35+
/// </summary>
36+
/// <param name="endpoint">The IGDB endpoint name to query, see <see cref="IGDB.IGDBClient.Endpoints" /></param>
37+
/// <param name="query">The APIcalypse query to send</param>
38+
/// <typeparam name="T">The IGDB.Models.* entity to deserialize the response for.</typeparam>
39+
/// <returns>RestEase Response containing the HttpResponseMessage and the array of IGDB models of the specified type</returns>
40+
[Post("/{endpoint}")]
41+
Task<Response<T[]>> QueryWithResponseAsync<T>([Path] string endpoint, [Body] string query = null);
42+
3343
/// <summary>
3444
/// Queries a standard IGDB endpoint with an APIcalypse query. See endpoints in <see cref="IGDB.IGDBClient.Endpoints" />.
3545
/// </summary>
@@ -143,6 +153,27 @@ public async Task<T[]> QueryAsync<T>(string endpoint, string query = null)
143153
}
144154
}
145155

156+
public async Task<Response<T[]>> QueryWithResponseAsync<T>(string endpoint, string query = null)
157+
{
158+
try
159+
{
160+
return await _api.QueryWithResponseAsync<T>(endpoint, query);
161+
}
162+
catch (ApiException apiEx)
163+
{
164+
// Acquire new token and retry request (once)
165+
if (IsInvalidTokenResponse(apiEx))
166+
{
167+
await _tokenManager.RefreshTokenAsync();
168+
169+
return await _api.QueryWithResponseAsync<T>(endpoint, query);
170+
}
171+
172+
// Pass up any other exceptions
173+
throw apiEx;
174+
}
175+
}
176+
146177
public async Task<CountResponse> CountAsync(string endpoint, string query = null)
147178
{
148179
try

IGDB/ResponseExtensions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Linq;
3+
using System.Net.Http;
4+
using RestEase;
5+
6+
namespace IGDB
7+
{
8+
public static class ResponseExtensions
9+
{
10+
private const string CountHeader = "x-count";
11+
12+
/// <summary>
13+
/// The value of the X-Count header for IGDB query endpoints, if it exists.
14+
/// </summary>
15+
public static int? GetQueryCount<T>(this Response<T> response)
16+
{
17+
return int.TryParse(response.ResponseMessage.Headers.GetValues(CountHeader).First(), out var count) ? count : (int?)null;
18+
}
19+
}
20+
}

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ game.Cover.Value.Width; // 756
6969
game.Cover.Value.Height;
7070
```
7171

72+
### Handling Query Counts
73+
74+
You can use the `QueryWithResponse` API to return the raw `HttpResponseMessage` along with the deserialized response through `GetContent()`.
75+
76+
There is an extension method available that will retrieve the `X-Count` header returned by IGDB on query endpoints. This avoids having to issue two requests to get the total results for a query. You also have full access to any headers through the `ResponseMessage` property.
77+
78+
**See IGDB.Tests/Games.cs#ShouldReturnResponseWithHeaders** for an example test that covers this feature. You can also read more about the `Response<T>` [support in RestEase](https://github.com/canton7/RestEase?tab=readme-ov-file#return-types).
79+
80+
Alternatively, you can also use the `CountAsync()` API to just retrieve the count for a query.
81+
7282
### Custom Token Management
7383

7484
By default this client will request a OAuth bearer token on your behalf that is valid for 60 days and will store it statically **in memory**. If an invalid token is used and a 401 response is received, it will automatically acquire a new token and retry the request. See `TokenManagement.cs` for the default implementation.

0 commit comments

Comments
 (0)