Skip to content

Commit

Permalink
add cwls search
Browse files Browse the repository at this point in the history
  • Loading branch information
Koenari committed Dec 22, 2024
1 parent d5a2e55 commit 25486f8
Show file tree
Hide file tree
Showing 10 changed files with 506 additions and 8 deletions.
43 changes: 36 additions & 7 deletions NetStone.Test/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using NetStone.GameData.Packs;
using NetStone.Model.Parseables.Character;
using NetStone.Search.Character;
using NetStone.Search.CWLS;
using NetStone.Search.FreeCompany;
using NetStone.StaticData;
using NUnit.Framework;
Expand Down Expand Up @@ -575,16 +576,44 @@ public async Task CheckCrossworldLinkShell()
foreach (var member in cwls.Members)
{
Console.WriteLine($"{member.Name} ({member.Rank}) {member.RankIcon}\n" +
$"Id: {member.Id}\n" +
$"Avatar: {member.Avatar}\n" +
$"Server: {member.Server}\n" +
$"LS Rank: {member.LinkshellRank}\n" +
$"LS Rank Icon: {member.LinkshellRankIcon}");

$"\tId: {member.Id}\n" +
$"\tAvatar: {member.Avatar}\n" +
$"\tServer: {member.Server}\n" +
$"\tLS Rank: {member.LinkshellRank}\n" +
$"\tLS Rank Icon: {member.LinkshellRankIcon}");
}
cwls = await cwls.GetNextPage();
}

}

[Test]
public async Task CheckCrossworldLinkShellSearch()
{
var emptyQuery = new CrossWorldLinkShellSearchQuery()
{
Name = "abcedfas",
};
var emptyResult = await this.lodestone.SearchCrossWorldLinkshell(emptyQuery);
Assert.IsNotNull(emptyResult);
Assert.False(emptyResult.HasResults);
var query = new CrossWorldLinkShellSearchQuery()
{
Name = "Hell",
ActiveMembers = CrossWorldLinkShellSearchQuery.ActiveMemberChoice.ElevenToThirty,
DataCenter = "Chaos",
};
var results = await this.lodestone.SearchCrossWorldLinkshell(query);
Assert.IsNotNull(results);
Assert.True(results.HasResults);
Assert.AreEqual(2, results.NumPages);
while (results is not null)
{
foreach (var result in results.Results)
{
Console.WriteLine($"{result.Name} ({result.Id}): {result.ActiveMembers}\n");
}
results = await results.GetNextPage();
}
}

[Test]
Expand Down
5 changes: 5 additions & 0 deletions NetStone/Definitions/DefinitionsContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public abstract class DefinitionsContainer : IDisposable
/// </summary>
public CrossWorldLinkShellMemberDefinition CrossWorldLinkShellMember { get; protected set; }

/// <summary>
/// Definitions for cross world link shell searches
/// </summary>
public PagedDefinition<CrossWorldLinkShellSearchEntryDefinition> CrossWorldLinkShellSearch { get; protected set; }

/// <summary>
/// Definitions for link shells
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Newtonsoft.Json;

namespace NetStone.Definitions.Model.CWLS;
/// <summary>
/// Definition container for one Cross World Link Shell search result entry
/// </summary>
public class CrossWorldLinkShellSearchEntryDefinition : PagedEntryDefinition
{
/// <summary>
/// ID
/// </summary>
[JsonProperty("ID")] public DefinitionsPack Id { get; set; }

/// <summary>
/// Name
/// </summary>
[JsonProperty("NAME")] public DefinitionsPack Name { get; set; }

/// <summary>
/// Rank
/// </summary>
[JsonProperty("DC")] public DefinitionsPack Dc { get; set; }

/// <summary>
/// Rank Icon
/// </summary>
[JsonProperty("ACTIVE_MEMBERS")] public DefinitionsPack ActiveMembers { get; set; }
}
1 change: 1 addition & 0 deletions NetStone/Definitions/XivApiDefinitionsContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public override async Task Reload()

this.CrossWorldLinkShell = await GetDefinition<CrossWorldLinkShellDefinition>("cwls/cwls.json");
this.CrossWorldLinkShellMember = await GetDefinition<CrossWorldLinkShellMemberDefinition>("cwls/members.json");
this.CrossWorldLinkShellSearch = await GetDefinition<PagedDefinition<CrossWorldLinkShellSearchEntryDefinition>>("search/cwls.json");

this.LinkShell = await GetDefinition<LinkShellDefinition>("linkshell/ls.json");
this.LinkShellMember = await GetDefinition<LinkShellMemberDefinition>("linkshell/members.json");
Expand Down
11 changes: 11 additions & 0 deletions NetStone/LodestoneClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
using NetStone.Model.Parseables.FreeCompany.Members;
using NetStone.Model.Parseables.Linkshell;
using NetStone.Model.Parseables.Search.Character;
using NetStone.Model.Parseables.Search.CWLS;
using NetStone.Model.Parseables.Search.FreeCompany;
using NetStone.Search.Character;
using NetStone.Search.CWLS;
using NetStone.Search.FreeCompany;

namespace NetStone;
Expand Down Expand Up @@ -162,6 +164,15 @@ await GetParsed($"/lodestone/character/{query.BuildQueryString()}&page={page}",
await GetParsed($"/lodestone/crossworld_linkshell/{id}?page={page}",
node => new LodestoneCrossWorldLinkShell(this, node, this.Definitions,id));

/// <summary>
/// Search lodestone for a character with the specified query.
/// </summary>
/// <param name="query"><see cref="CharacterSearchQuery"/> object detailing search parameters</param>
/// <param name="page">The page of search results to fetch.</param>
/// <returns><see cref="CharacterSearchPage"/> containing search results.</returns>
public async Task<CrossWorldLinkShellSearchPage?> SearchCrossWorldLinkshell(CrossWorldLinkShellSearchQuery query, int page = 1) =>
await GetParsed($"/lodestone/crossworld_linkshell/{query.BuildQueryString()}&page={page}",
node => new CrossWorldLinkShellSearchPage(this, node, this.Definitions.CrossWorldLinkShellSearch, query));

/// <summary>
/// Gets a link shell by its id.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Threading.Tasks;
using HtmlAgilityPack;
using NetStone.Definitions.Model.CWLS;
using NetStone.Model.Parseables.Character;

namespace NetStone.Model.Parseables.Search.CWLS;

public class CrossWorldLinkShellSearchEntry : LodestoneParseable
{
private readonly LodestoneClient client;
private readonly CrossWorldLinkShellSearchEntryDefinition definition;

///
public CrossWorldLinkShellSearchEntry(LodestoneClient client, HtmlNode rootNode, CrossWorldLinkShellSearchEntryDefinition definition) :
base(rootNode)
{
this.client = client;
this.definition = definition;
}

/// <summary>
/// Character name
/// </summary>
public string Name => Parse(this.definition.Name);

/// <summary>
/// Lodestone Id
/// </summary>
public string? Id => ParseHrefId(this.definition.Id);

public int ActiveMembers => int.TryParse(Parse(this.definition.ActiveMembers), out var parsed) ? parsed : -1;

/// <summary>
/// Fetch character profile
/// </summary>
/// <returns>Task of retrieving character</returns>
public async Task<LodestoneCharacter?> GetCharacter() =>
this.Id is null ? null : await this.client.GetCharacter(this.Id);

///<inheritdoc />
public override string ToString() => this.Name;
}
125 changes: 125 additions & 0 deletions NetStone/Model/Parseables/Search/CWLS/CrossWorldLinkShellSearchPage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using HtmlAgilityPack;
using NetStone.Definitions;
using NetStone.Definitions.Model;
using NetStone.Definitions.Model.CWLS;
using NetStone.Search.CWLS;

namespace NetStone.Model.Parseables.Search.CWLS;

/// <summary>
/// Models cross world link shell search results
/// </summary>
public class CrossWorldLinkShellSearchPage : LodestoneParseable, IPaginatedResult<CrossWorldLinkShellSearchPage>
{
private readonly LodestoneClient client;
private readonly CrossWorldLinkShellSearchQuery currentQuery;

private readonly PagedDefinition<CrossWorldLinkShellSearchEntryDefinition> pageDefinition;

/// <summary>
/// Constructs character search results
/// </summary>
/// <param name="client"></param>
/// <param name="rootNode"></param>
/// <param name="pageDefinition"></param>
/// <param name="currentQuery"></param>
public CrossWorldLinkShellSearchPage(LodestoneClient client, HtmlNode rootNode, PagedDefinition<CrossWorldLinkShellSearchEntryDefinition> pageDefinition,
CrossWorldLinkShellSearchQuery currentQuery) : base(rootNode)
{
this.client = client;
this.currentQuery = currentQuery;
this.pageDefinition = pageDefinition;
}

/// <summary>
/// Indicates if any results are present
/// </summary>
public bool HasResults => !HasNode(this.pageDefinition.NoResultsFound);

private CrossWorldLinkShellSearchEntry[]? parsedResults;

/// <summary>
/// List all results
/// </summary>
public IEnumerable<CrossWorldLinkShellSearchEntry> Results
{
get
{
if (!this.HasResults)
return Array.Empty<CrossWorldLinkShellSearchEntry>();

if (this.parsedResults == null)
ParseSearchResults();

return this.parsedResults!;
}
}

private void ParseSearchResults()
{
var container = QueryContainer(this.pageDefinition);

this.parsedResults = new CrossWorldLinkShellSearchEntry[container.Length];
for (var i = 0; i < this.parsedResults.Length; i++)
{
this.parsedResults[i] = new CrossWorldLinkShellSearchEntry(this.client, container[i], this.pageDefinition.Entry);
}
}

private int? currentPageVal;

///<inheritdoc />
public int CurrentPage
{
get
{
if (!this.HasResults)
return 0;

if (!this.currentPageVal.HasValue)
ParsePagesCount();

return this.currentPageVal!.Value;
}
}

private int? numPagesVal;

///<inheritdoc />
public int NumPages
{
get
{
if (!this.HasResults)
return 0;

if (!this.numPagesVal.HasValue)
ParsePagesCount();

return this.numPagesVal!.Value;
}
}

private void ParsePagesCount()
{
var results = ParseRegex(this.pageDefinition.PageInfo);

this.currentPageVal = int.Parse(results["CurrentPage"].Value);
this.numPagesVal = int.Parse(results["NumPages"].Value);
}

///<inheritdoc />
public async Task<CrossWorldLinkShellSearchPage?> GetNextPage()
{
if (!this.HasResults)
return null;

if (this.CurrentPage == this.NumPages)
return null;

return await this.client.SearchCrossWorldLinkshell(this.currentQuery, this.CurrentPage + 1);
}
}
Loading

0 comments on commit 25486f8

Please sign in to comment.