Skip to content
This repository has been archived by the owner on Apr 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #355 from aspriddell/replace-level-stats-api
Browse files Browse the repository at this point in the history
Replace level stats api
  • Loading branch information
aspriddell authored Dec 23, 2022
2 parents 2ec7d10 + 2152ade commit 1553082
Show file tree
Hide file tree
Showing 15 changed files with 113 additions and 117 deletions.
27 changes: 27 additions & 0 deletions DragonFruit.Six.Api.Tests/Data/AccountLevelTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Dragon6 API Copyright DragonFruit Network <inbox@dragonfruit.network>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using System.Threading.Tasks;
using DragonFruit.Six.Api.Accounts;
using DragonFruit.Six.Api.Accounts.Entities;
using NUnit.Framework;

namespace DragonFruit.Six.Api.Tests.Data
{
[TestFixture]
public class AccountLevelTests : Dragon6ApiTest
{
[TestCaseSource(nameof(Accounts))]
public async Task GetAccountLevel(UbisoftAccount account)
{
var level = await Client.GetAccountLevelAsync(account).ConfigureAwait(false);

if (level.Level == 0)
{
Assert.Inconclusive("User level has not been synchronised across all platforms");
}

Assert.GreaterOrEqual(level.Level, 5);
}
}
}
9 changes: 0 additions & 9 deletions DragonFruit.Six.Api.Tests/Data/LegacyStatsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,5 @@ public async Task TestOperatorStats(string userId, string operatorIndex, int kil
Assert.IsTrue(selectedOperator.Kd > 0);
Assert.IsTrue(selectedOperator.Wl > 0);
}

[TestCase("603fc6ba-db16-4aba-81b2-e9f9601d7d24", 300)]
public async Task PlayerLevelStatsTest(string userId, int level)
{
var accountLevel = await Client.GetLegacyLevelAsync(Accounts[userId]);

Assert.IsNotNull(accountLevel);
Assert.GreaterOrEqual(accountLevel.Level, level);
}
}
}
16 changes: 16 additions & 0 deletions DragonFruit.Six.Api/Accounts/Entities/AccountLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Dragon6 API Copyright DragonFruit Network <inbox@dragonfruit.network>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using Newtonsoft.Json;

namespace DragonFruit.Six.Api.Accounts.Entities
{
public class AccountLevel
{
[JsonProperty("level")]
public int Level { get; set; }

[JsonProperty("xp")]
public int XP { get; set; }
}
}
26 changes: 26 additions & 0 deletions DragonFruit.Six.Api/Accounts/Requests/AccountLevelRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Dragon6 API Copyright DragonFruit Network <inbox@dragonfruit.network>
// Licensed under Apache-2. Refer to the LICENSE file for more info

using DragonFruit.Data.Parameters;
using DragonFruit.Six.Api.Accounts.Entities;
using DragonFruit.Six.Api.Accounts.Enums;
using JetBrains.Annotations;

namespace DragonFruit.Six.Api.Accounts.Requests
{
public class AccountLevelRequest : UbiApiRequest
{
public override string Path => Platform.CrossPlatform.SpaceUrl(1) + "/title/r6s/rewards/public_profile";

public AccountLevelRequest(UbisoftAccount account)
{
Account = account;
}

public UbisoftAccount Account { get; }

[UsedImplicitly]
[QueryParameter("profile_id")]
private string ProfileId => Account.ProfileId;
}
}
13 changes: 13 additions & 0 deletions DragonFruit.Six.Api/Accounts/UbisoftAccountExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DragonFruit.Six.Api.Accounts.Entities;
using DragonFruit.Six.Api.Accounts.Enums;
Expand Down Expand Up @@ -40,5 +41,17 @@ public static Task<IEnumerable<UbisoftAccount>> GetAccountsAsync(this Dragon6Cli
var request = new UbisoftAccountRequest(queries, platform, identifierType);
return client.PerformAsync<JObject>(request).ContinueWith(t => t.Result.DeserializeUbisoftAccounts(), TaskContinuationOptions.OnlyOnRanToCompletion);
}

/// <summary>
/// Gets the current clearance level and XP of the provided <see cref="UbisoftAccount"/>
/// </summary>
/// <param name="client">The <see cref="Dragon6Client"/> to use</param>
/// <param name="account">The <see cref="UbisoftAccount"/> to get level stats for</param>
/// <param name="token">Optional cancellation token</param>
/// <returns><see cref="AccountLevel"/> containing the values for the user's level and xp</returns>
public static Task<AccountLevel> GetAccountLevelAsync(this Dragon6Client client, UbisoftAccount account, CancellationToken token = default)
{
return client.PerformAsync<AccountLevel>(new AccountLevelRequest(account), token);
}
}
}
26 changes: 18 additions & 8 deletions DragonFruit.Six.Api/Dragon6Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,29 @@ static Dragon6Client()
/// Handles the response before trying to deserialize it.
/// If a recognized error code has been returned, an appropriate exception will be thrown.
/// </summary>
protected override Task<T> ValidateAndProcess<T>(HttpResponseMessage response) => response.StatusCode switch
protected override async Task<T> ValidateAndProcess<T>(HttpResponseMessage response)
{
HttpStatusCode.Unauthorized => Task.FromException<T>(new InvalidTokenException(_access.Token)),
switch (response.StatusCode)
{
case HttpStatusCode.Forbidden:
case HttpStatusCode.BadGateway:
case HttpStatusCode.InternalServerError:
var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new UbisoftErrorException(response.StatusCode, error);

HttpStatusCode.BadRequest => Task.FromException<T>(new ArgumentException("Request was poorly formed. Check the properties passed and try again")),
case HttpStatusCode.Unauthorized:
throw new InvalidTokenException(_access.Token);

HttpStatusCode.Forbidden => Task.FromException<T>(new UbisoftErrorException()),
HttpStatusCode.BadGateway => Task.FromException<T>(new UbisoftErrorException()),
case HttpStatusCode.BadRequest:
throw new ArgumentException("Request was poorly formed. Check the properties passed and try again");

HttpStatusCode.NoContent => Task.FromResult<T>(default),
case HttpStatusCode.NoContent:
return default;

_ => base.ValidateAndProcess<T>(response)
};
default:
return await base.ValidateAndProcess<T>(response).ConfigureAwait(false);
}
}

protected internal async ValueTask<ClientTokenInjector> RequestToken()
{
Expand Down
2 changes: 1 addition & 1 deletion DragonFruit.Six.Api/Exceptions/InvalidTokenException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace DragonFruit.Six.Api.Exceptions
public class InvalidTokenException : Exception
{
public InvalidTokenException(IUbisoftToken token)
: base("The Token has expired or is invalid")
: base("The Token has expired or is invalid for this request")
{
Token = token;
}
Expand Down
5 changes: 3 additions & 2 deletions DragonFruit.Six.Api/Exceptions/UbisoftErrorException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
// Licensed under Apache-2. Refer to the LICENSE file for more info

using System;
using System.Net;

namespace DragonFruit.Six.Api.Exceptions
{
public class UbisoftErrorException : Exception
{
public UbisoftErrorException()
: base("A Ubisoft server has disallowed this request, probably due to the Ubi-AppId header. Please check and try again")
public UbisoftErrorException(HttpStatusCode code, string message)
: base($"Ubisoft returned an error: {code} - {message}")
{
}
}
Expand Down
31 changes: 0 additions & 31 deletions DragonFruit.Six.Api/Legacy/Entities/LegacyLevelStats.cs

This file was deleted.

8 changes: 0 additions & 8 deletions DragonFruit.Six.Api/Legacy/LegacyStatsDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,6 @@ public static ILookup<string, LegacyWeaponStats> DeserializeWeaponStats(this JOb
return json.RemoveContainer<JObject>()?.Properties().SelectMany(x => DeserializeWeaponStatsInternal(x, weaponClasses)).ToLookup(x => x.ProfileId);
}

/// <summary>
/// Deserializes a <see cref="JObject"/> into a <see cref="IReadOnlyDictionary{TKey,TValue}"/> of <see cref="LegacyLevelStats"/>
/// </summary>
public static IReadOnlyDictionary<string, LegacyLevelStats> DeserializePlayerLevelStats(this JObject json)
{
return json.RemoveContainer<JArray>().ToObject<IEnumerable<LegacyLevelStats>>()?.ToDictionary(x => x.ProfileId);
}

private static IEnumerable<LegacyOperatorStats> DeserializeOperatorStatsInternal(JProperty data)
{
var property = (JObject)data.Value;
Expand Down
26 changes: 0 additions & 26 deletions DragonFruit.Six.Api/Legacy/LegacyStatsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,32 +99,6 @@ public static Task<ILookup<string, LegacyWeaponStats>> GetLegacyWeaponStatsAsync
return GetPlatformStatsImplAsync(client, accounts, a => new LegacyStatsRequest(a, LegacyStatTypes.Weapons), LegacyStatsDeserializer.DeserializeWeaponStats, token);
}

/// <summary>
/// Gets the <see cref="LegacyLevelStats"/> for the <see cref="UbisoftAccount"/> provided
/// </summary>
/// <param name="client">The <see cref="Dragon6Client"/> to use</param>
/// <param name="account">The <see cref="UbisoftAccount"/>s to get stats for</param>
/// <param name="token">Optional cancellation token</param>
/// <returns><see cref="LegacyWeaponStats"/> for the provided <see cref="UbisoftAccount"/>, or null if not found</returns>
public static Task<LegacyLevelStats> GetLegacyLevelAsync(this Dragon6Client client, UbisoftAccount account, CancellationToken token = default)
{
return GetLegacyLevelAsync(client, account.Yield(), token).ContinueWith(t => t.Result.For(account), TaskContinuationOptions.OnlyOnRanToCompletion);
}

/// <summary>
/// Gets the <see cref="LegacyLevelStats"/> for the <see cref="UbisoftAccount"/>s provided
/// </summary>
/// <param name="client">The <see cref="Dragon6Client"/> to use</param>
/// <param name="accounts">The <see cref="UbisoftAccount"/>s to get stats for</param>
/// <param name="token">Optional cancellation token</param>
/// <returns><see cref="IReadOnlyDictionary{TKey,TValue}"/> of <see cref="LegacyWeaponStats"/> for the provided <see cref="UbisoftAccount"/></returns>
public static Task<IReadOnlyDictionary<string, LegacyLevelStats>> GetLegacyLevelAsync(this Dragon6Client client, IEnumerable<UbisoftAccount> accounts, CancellationToken token = default)
{
return GetPlatformStatsImplAsync(client, accounts,
a => new PlayerLevelStatsRequest(a),
LegacyStatsDeserializer.DeserializePlayerLevelStats, token);
}

internal static Task<T> GetPlatformStatsImplAsync<T>(ApiClient client, IEnumerable<UbisoftAccount> accounts, Func<IEnumerable<UbisoftAccount>, PlatformSpecificRequest> requestFactory, Func<JObject, T> postProcessor, CancellationToken token)
{
// LegacyStatsRequest is a PlatformSpecific request, so the accounts need to be split by platform
Expand Down
23 changes: 0 additions & 23 deletions DragonFruit.Six.Api/Legacy/Requests/PlayerLevelStatsRequest.cs

This file was deleted.

4 changes: 1 addition & 3 deletions DragonFruit.Six.Api/Seasonal/Enums/Region.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ public enum Region
{
EMEA = 1,
NCSA = 2,
APAC = 4,

All = EMEA | NCSA | APAC
APAC = 4
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ public SeasonalStatsRecordRequest(IEnumerable<UbisoftAccount> accounts, IEnumera
/// This is left for legacy seasons, which remain region-specific
/// </remarks>
[QueryParameter("region_ids", EnumHandlingMode.StringLower)]
public Region Regions { get; set; } = Region.All;
public Region Regions { get; set; } = Region.APAC | Region.EMEA | Region.NCSA;

[QueryParameter("profile_ids", CollectionConversionMode.Concatenated)]
private IEnumerable<string> AccountIds => Accounts.Select(x => x.ProfileId);

[QueryParameter("board_ids", CollectionConversionMode.Concatenated)]
private IEnumerable<string> BoardIds => Enum.GetValues(typeof(BoardType))
.Cast<BoardType>()
.Where(x => Boards.HasFlagFast(x))
.Where(x => Boards.HasFlagFast(x) && x != BoardType.All)
.Select(x => typeof(BoardType).GetField(x.ToString()).GetCustomAttribute<EnumMemberAttribute>()?.Value);
}
}
10 changes: 6 additions & 4 deletions DragonFruit.Six.Api/Seasonal/SeasonStatsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,21 @@ public static Task<IReadOnlyCollection<SeasonalStats>> GetSeasonalStatsRecordsAs
/// </remarks>
public static async Task<IReadOnlyCollection<SeasonalStats>> GetSeasonalStatsRecordsAsync(this Dragon6Client client, IEnumerable<UbisoftAccount> accounts, IEnumerable<int> seasonIds, BoardType? boards = null, Region? regions = null, CancellationToken token = default)
{
regions ??= Region.All;
boards ??= BoardType.All;
seasonIds ??= (-1).Yield();
accounts = accounts as IReadOnlyCollection<UbisoftAccount> ?? accounts.ToList();
seasonIds = seasonIds as IReadOnlyCollection<int> ?? seasonIds.ToList();

var requests = new List<SeasonalStatsRecordRequest>(4);
var ranked2Seasons = seasonIds.Where(x => x is >= SeasonalStatsRecordRequest.CrossPlatformProgressionId or -1);
var otherSeasons = seasonIds.Except(ranked2Seasons);

// handle creation of ranked 2.0 requests
if (ranked2Seasons.Any())
{
requests.Add(new SeasonalStatsRecordRequest(accounts, ranked2Seasons, boards.Value));
}

var otherSeasons = seasonIds.Except(ranked2Seasons);

if (otherSeasons.Any())
{
var platformRequests = accounts.GroupBy(x => x.Platform).Select(x => new SeasonalStatsRecordRequest(x, otherSeasons, boards.Value));
Expand All @@ -74,7 +72,11 @@ public static async Task<IReadOnlyCollection<SeasonalStats>> GetSeasonalStatsRec

var seasonalStatsRequests = requests.Select(x =>
{
x.Regions = regions.Value;
if (regions.HasValue)
{
x.Regions = regions.Value;
}

return client.PerformAsync<JObject>(x, token);
});

Expand Down

0 comments on commit 1553082

Please sign in to comment.