Skip to content

Commit

Permalink
Add GetPlayoffSeriesBySeasonYearAsync method and tests
Browse files Browse the repository at this point in the history
- Added a `break` statement in `TestMethodWithRetryAttribute` to exit the loop when a condition is met.
- Introduced new test methods in `LeagueTests` to test `GetPlayoffSeriesBySeasonYearAsync` with valid and invalid season years.
- Updated `README.md` to document the new `GetPlayoffSeriesBySeasonYearAsync` method in `NhlApi` and `NhlLeagueApi`.
- Added `GetPlayoffSeriesBySeasonYearAsync` to `NhlApi` to fetch the NHL playoff schedule for a given season year.
- Updated `INhlLeagueApi` interface to include `GetPlayoffSeriesBySeasonYearAsync`.
- Implemented `GetPlayoffSeriesBySeasonYearAsync` in `NhlLeagueApi` with season year validation and NHL web API retrieval.
- Created `PlayoffSeriesSchedule` and related classes (`Seed`, `BottomSeed`, `TopSeed`, `Round`, `Series`) to represent playoff series schedule data.
  • Loading branch information
Afischbacher committed Mar 8, 2025
1 parent 6195482 commit d33987d
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 2 deletions.
175 changes: 175 additions & 0 deletions Nhl.Api.Domain/Models/Schedule/PlayoffSeriesSchedule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using System.Collections.Generic;
using Newtonsoft.Json;

namespace Nhl.Api.Models.Schedule;


/// <summary>
/// Represents the base class of a seeded team.
/// </summary>
public abstract class Seed
{
/// <summary>
/// Gets or sets the unique identifier for the team.<br/>
/// Example: 13
/// </summary>
[JsonProperty("id")]
public int? Id { get; set; }

/// <summary>
/// Gets or sets the abbreviated team name.<br/>
/// Example: "FLA"
/// </summary>
[JsonProperty("abbrev")]
public required string Abbrev { get; set; }

/// <summary>
/// Gets or sets the number of wins for the team.<br/>
/// Example: 0
/// </summary>
[JsonProperty("wins")]
public int? Wins { get; set; }

/// <summary>
/// Gets or sets the URL of the team's light mode logo.<br/>
/// Example: "https://assets.nhle.com/logos/nhl/svg/FLA_light.svg"
/// </summary>
[JsonProperty("logo")]
public required string Logo { get; set; }

/// <summary>
/// Gets or sets the URL of the team's dark mode logo.<br/>
/// Example: "https://assets.nhle.com/logos/nhl/svg/FLA_dark.svg"
/// </summary>
[JsonProperty("darkLogo")]
public required string DarkLogo { get; set; }
}

/// <summary>
/// Represents the details of a bottom seeded team.
/// </summary>
public class BottomSeed : Seed
{
}

/// <summary>
/// Represents the details of a top seeded team.
/// </summary>
public class TopSeed : Seed
{
}

/// <summary>
/// Represents the schedule for playoff series within a season.
/// </summary>
public class PlayoffSeriesSchedule
{
/// <summary>
/// Gets or sets the identifier for the season.<br/>
/// Example: 20242025
/// </summary>
[JsonProperty("seasonId")]
public int? SeasonId { get; set; }

/// <summary>
/// Gets or sets the current round of the playoffs.<br/>
/// Example: 1
/// </summary>
[JsonProperty("currentRound")]
public int? CurrentRound { get; set; }

/// <summary>
/// Gets or sets the list of rounds in the playoff series schedule.<br/>
/// Example: new List&lt;Round&gt; { /* Round objects */ }
/// </summary>
[JsonProperty("rounds")]
public List<Round> Rounds { get; set; } = [];
}

/// <summary>
/// Represents a single round in the playoff schedule.
/// </summary>
public class Round
{
/// <summary>
/// Gets or sets the number of the round.<br/>
/// Example: 1
/// </summary>
[JsonProperty("roundNumber")]
public int? RoundNumber { get; set; }

/// <summary>
/// Gets or sets the label for the round (e.g., "1st-round").<br/>
/// Example: "1st-round"
/// </summary>
[JsonProperty("roundLabel")]
public required string RoundLabel { get; set; }

/// <summary>
/// Gets or sets the abbreviated label for the round (e.g., "R1").<br/>
/// Example: "R1"
/// </summary>
[JsonProperty("roundAbbrev")]
public required string RoundAbbrev { get; set; }

/// <summary>
/// Gets or sets the list of playoff series in this round.<br/>
/// Example: new List&lt;Series&gt; { /* Series objects */ }
/// </summary>
[JsonProperty("series")]
public List<Series> Series { get; set; } = new List<Series>();
}

/// <summary>
/// Represents a playoff series between two teams.
/// </summary>
public class Series
{
/// <summary>
/// Gets or sets the series letter (e.g., "A", "B", etc.).<br/>
/// Example: "A"
/// </summary>
[JsonProperty("seriesLetter")]
public required string SeriesLetter { get; set; }

/// <summary>
/// Gets or sets the round number associated with the series.<br/>
/// Example: 1
/// </summary>
[JsonProperty("roundNumber")]
public int? RoundNumber { get; set; }

/// <summary>
/// Gets or sets the label for the series.<br/>
/// Example: "1st-round"
/// </summary>
[JsonProperty("seriesLabel")]
public required string SeriesLabel { get; set; }

/// <summary>
/// Gets or sets the URL link for the series details.<br/>
/// Example: "/schedule/playoff-series/2025/series-a/bluejackets-vs-panthers"
/// </summary>
[JsonProperty("seriesLink")]
public required string SeriesLink { get; set; }

/// <summary>
/// Gets or sets the details of the bottom seeded team in the series.<br/>
/// Example: <code> new BottomSeed { Id = 29, Abbrev = "CBJ", Wins = 0, Logo = "https://assets.nhle.com/logos/nhl/svg/CBJ_light.svg", DarkLogo = "https://assets.nhle.com/logos/nhl/svg/CBJ_dark.svg" } </code>
/// </summary>
[JsonProperty("bottomSeed")]
public required BottomSeed BottomSeed { get; set; }

/// <summary>
/// Gets or sets the details of the top seeded team in the series.<br/>
/// Example: <code> new TopSeed { Id = 13, Abbrev = "FLA", Wins = 0, Logo = "https://assets.nhle.com/logos/nhl/svg/FLA_light.svg", DarkLogo = "https://assets.nhle.com/logos/nhl/svg/FLA_dark.svg" } </code>
/// </summary>
[JsonProperty("topSeed")]
public required TopSeed TopSeed { get; set; }

/// <summary>
/// Gets or sets the number of wins required to win the series.<br/>Example: 4
/// </summary>
[JsonProperty("neededToWin")]
public int? NeededToWin { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public override TestResult[] Execute(ITestMethod testMethod)
}
else
{
return result;
break;
}
}
catch (Exception) when (count > 0)
Expand Down
29 changes: 29 additions & 0 deletions Nhl.Api.Tests/LeagueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -624,4 +624,33 @@ public async Task GetLeagueMetadataInformation_Returns_Valid_Information_Just_Pl
Assert.IsNotNull(result);
Assert.AreEqual(result.Players.Count, 1);
}

[TestMethodWithRetry(RetryCount = 5)]
[DataRow(SeasonYear.season20242025)]
[DataRow(SeasonYear.season20082009)]
[DataRow(SeasonYear.season20132014)]
[DataRow(SeasonYear.season20002001)]
[DataRow(SeasonYear.season19931994)]

public async Task GetPlayoffSeriesBySeasonYearAsync_Returns_Valid_Information(string seasonYear)
{
// Arrange
await using var nhlApi = new NhlApi();

// Act
var result = await nhlApi.GetPlayoffSeriesBySeasonYearAsync(seasonYear, cancellationToken: default);

// Assert
Assert.IsNotNull(result);
}

[TestMethodWithRetry(RetryCount = 5)]
public async Task GetPlayoffSeriesBySeasonYearAsync_With_Invalid_Season_Year_Throws_Argument_Exception()
{
// Arrange
await using var nhlApi = new NhlApi();

// Act / Assert
await Assert.ThrowsExceptionAsync<ArgumentException>(async () => await nhlApi.GetPlayoffSeriesBySeasonYearAsync(" ", cancellationToken: default));
}
}
38 changes: 38 additions & 0 deletions Nhl.Api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ Thank you to all the people in the hockey community, especially:
- [GetPlayerSeasonGameLogsBySeasonAndGameTypeAsync(playerId,seasonYear,gameType,cancellationToken)](#M-Nhl-Api-NhlApi-GetPlayerSeasonGameLogsBySeasonAndGameTypeAsync-System-Int32,System-String,Nhl-Api-Enumerations-Game-GameType,System-Threading-CancellationToken- 'Nhl.Api.NhlApi.GetPlayerSeasonGameLogsBySeasonAndGameTypeAsync(System.Int32,System.String,Nhl.Api.Enumerations.Game.GameType,System.Threading.CancellationToken)')
- [GetPlayerSpotlightAsync(cancellationToken)](#M-Nhl-Api-NhlApi-GetPlayerSpotlightAsync-System-Threading-CancellationToken- 'Nhl.Api.NhlApi.GetPlayerSpotlightAsync(System.Threading.CancellationToken)')
- [GetPlayerStatisticsBySeasonAndFilterExpressionAsync(seasonYear,expressionPlayerFilter,playerStatisticsFilterToSortBy,limit,offsetStart,gameType,cancellationToken)](#M-Nhl-Api-NhlApi-GetPlayerStatisticsBySeasonAndFilterExpressionAsync-System-String,Nhl-Api-Models-Player-ExpressionPlayerFilter,Nhl-Api-Models-Player-PlayerStatisticsFilter,System-Int32,System-Int32,Nhl-Api-Enumerations-Game-GameType,System-Threading-CancellationToken- 'Nhl.Api.NhlApi.GetPlayerStatisticsBySeasonAndFilterExpressionAsync(System.String,Nhl.Api.Models.Player.ExpressionPlayerFilter,Nhl.Api.Models.Player.PlayerStatisticsFilter,System.Int32,System.Int32,Nhl.Api.Enumerations.Game.GameType,System.Threading.CancellationToken)')
- [GetPlayoffSeriesBySeasonYearAsync(seasonYear,cancellationToken)](#M-Nhl-Api-NhlApi-GetPlayoffSeriesBySeasonYearAsync-System-String,System-Threading-CancellationToken- 'Nhl.Api.NhlApi.GetPlayoffSeriesBySeasonYearAsync(System.String,System.Threading.CancellationToken)')
- [GetRealtimePlayerStatisticsBySeasonAndFilterExpressionAsync(seasonYear,expressionPlayerFilter,playerRealtimeStatisticsFilterToSortBy,limit,offsetStart,gameType,cancellationToken)](#M-Nhl-Api-NhlApi-GetRealtimePlayerStatisticsBySeasonAndFilterExpressionAsync-System-String,Nhl-Api-Models-Player-ExpressionPlayerFilter,Nhl-Api-Models-Player-PlayerRealtimeStatisticsFilter,System-Int32,System-Int32,Nhl-Api-Enumerations-Game-GameType,System-Threading-CancellationToken- 'Nhl.Api.NhlApi.GetRealtimePlayerStatisticsBySeasonAndFilterExpressionAsync(System.String,Nhl.Api.Models.Player.ExpressionPlayerFilter,Nhl.Api.Models.Player.PlayerRealtimeStatisticsFilter,System.Int32,System.Int32,Nhl.Api.Enumerations.Game.GameType,System.Threading.CancellationToken)')
- [GetSkaterStatisticsLeadersAsync(playerStatisticsType,seasonYear,gameType,limit,cancellationToken)](#M-Nhl-Api-NhlApi-GetSkaterStatisticsLeadersAsync-Nhl-Api-Enumerations-Statistic-PlayerStatisticsType,Nhl-Api-Enumerations-Game-GameType,System-String,System-Int32,System-Threading-CancellationToken- 'Nhl.Api.NhlApi.GetSkaterStatisticsLeadersAsync(Nhl.Api.Enumerations.Statistic.PlayerStatisticsType,Nhl.Api.Enumerations.Game.GameType,System.String,System.Int32,System.Threading.CancellationToken)')
- [GetSourcesToWatchGamesAsync(cancellationToken)](#M-Nhl-Api-NhlApi-GetSourcesToWatchGamesAsync-System-Threading-CancellationToken- 'Nhl.Api.NhlApi.GetSourcesToWatchGamesAsync(System.Threading.CancellationToken)')
Expand Down Expand Up @@ -223,6 +224,7 @@ Thank you to all the people in the hockey community, especially:
- [GetLeagueStandingsByDateAsync(date,cancellationToken)](#M-Nhl-Api-NhlLeagueApi-GetLeagueStandingsByDateAsync-System-DateOnly,System-Threading-CancellationToken- 'Nhl.Api.NhlLeagueApi.GetLeagueStandingsByDateAsync(System.DateOnly,System.Threading.CancellationToken)')
- [GetLeagueStandingsSeasonInformationAsync(cancellationToken)](#M-Nhl-Api-NhlLeagueApi-GetLeagueStandingsSeasonInformationAsync-System-Threading-CancellationToken- 'Nhl.Api.NhlLeagueApi.GetLeagueStandingsSeasonInformationAsync(System.Threading.CancellationToken)')
- [GetLeagueWeekScheduleByDateAsync(date,cancellationToken)](#M-Nhl-Api-NhlLeagueApi-GetLeagueWeekScheduleByDateAsync-System-DateOnly,System-Threading-CancellationToken- 'Nhl.Api.NhlLeagueApi.GetLeagueWeekScheduleByDateAsync(System.DateOnly,System.Threading.CancellationToken)')
- [GetPlayoffSeriesBySeasonYearAsync(seasonYear,cancellationToken)](#M-Nhl-Api-NhlLeagueApi-GetPlayoffSeriesBySeasonYearAsync-System-String,System-Threading-CancellationToken- 'Nhl.Api.NhlLeagueApi.GetPlayoffSeriesBySeasonYearAsync(System.String,System.Threading.CancellationToken)')
- [GetSourcesToWatchGamesAsync(cancellationToken)](#M-Nhl-Api-NhlLeagueApi-GetSourcesToWatchGamesAsync-System-Threading-CancellationToken- 'Nhl.Api.NhlLeagueApi.GetSourcesToWatchGamesAsync(System.Threading.CancellationToken)')
- [GetTeamColorsAsync(team,cancellationToken)](#M-Nhl-Api-NhlLeagueApi-GetTeamColorsAsync-Nhl-Api-Models-Enumerations-Team-TeamEnum,System-Threading-CancellationToken- 'Nhl.Api.NhlLeagueApi.GetTeamColorsAsync(Nhl.Api.Models.Enumerations.Team.TeamEnum,System.Threading.CancellationToken)')
- [GetTeamColorsAsync(teamId,cancellationToken)](#M-Nhl-Api-NhlLeagueApi-GetTeamColorsAsync-System-Int32,System-Threading-CancellationToken- 'Nhl.Api.NhlLeagueApi.GetTeamColorsAsync(System.Int32,System.Threading.CancellationToken)')
Expand Down Expand Up @@ -1050,6 +1052,24 @@ Returns all the NHL player game center statistics for a specific player for a sp
| gameType | [Nhl.Api.Enumerations.Game.GameType](#T-Nhl-Api-Enumerations-Game-GameType 'Nhl.Api.Enumerations.Game.GameType') | The game type for the NHL season for the player statistics |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token to cancel the asynchronous operation |

<a name='M-Nhl-Api-NhlApi-GetPlayoffSeriesBySeasonYearAsync-System-String,System-Threading-CancellationToken-'></a>
### GetPlayoffSeriesBySeasonYearAsync(seasonYear,cancellationToken) `method`

##### Summary

Returns the current NHL playofff schedule for the current season

##### Returns

Returns a collection of playoff series match ups by year

##### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| seasonYear | [System.String](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.String 'System.String') | The eight digit number format for the season, see [SeasonYear](#T-Nhl-Api-Models-Season-SeasonYear 'Nhl.Api.Models.Season.SeasonYear') for more information, Example: 20232024 |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token that can be used by other objects or threads to receive notice of cancellation |

<a name='M-Nhl-Api-NhlApi-GetRealtimePlayerStatisticsBySeasonAndFilterExpressionAsync-System-String,Nhl-Api-Models-Player-ExpressionPlayerFilter,Nhl-Api-Models-Player-PlayerRealtimeStatisticsFilter,System-Int32,System-Int32,Nhl-Api-Enumerations-Game-GameType,System-Threading-CancellationToken-'></a>
### GetRealtimePlayerStatisticsBySeasonAndFilterExpressionAsync(seasonYear,expressionPlayerFilter,playerRealtimeStatisticsFilterToSortBy,limit,offsetStart,gameType,cancellationToken) `method`

Expand Down Expand Up @@ -2196,6 +2216,24 @@ Returns the NHL league schedule for the specified date
| date | [System.DateOnly](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.DateOnly 'System.DateOnly') | The date requested for the NHL league schedule, Example: 2024-02-10 |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token that can be used by other objects or threads to receive notice of cancellation |

<a name='M-Nhl-Api-NhlLeagueApi-GetPlayoffSeriesBySeasonYearAsync-System-String,System-Threading-CancellationToken-'></a>
### GetPlayoffSeriesBySeasonYearAsync(seasonYear,cancellationToken) `method`

##### Summary

Returns the current NHL playofff schedule for the current season

##### Returns

Returns a collection of playoff series match ups by year

##### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| seasonYear | [System.String](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.String 'System.String') | The eight digit number format for the season, see [SeasonYear](#T-Nhl-Api-Models-Season-SeasonYear 'Nhl.Api.Models.Season.SeasonYear') for more information, Example: 20232024 |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token that can be used by other objects or threads to receive notice of cancellation |

<a name='M-Nhl-Api-NhlLeagueApi-GetSourcesToWatchGamesAsync-System-Threading-CancellationToken-'></a>
### GetSourcesToWatchGamesAsync(cancellationToken) `method`

Expand Down
9 changes: 9 additions & 0 deletions Nhl.Api/Src/Api/NhlApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -745,4 +745,13 @@ public void Dispose() =>
/// <returns>The await-able result of the asynchronous operation</returns>
public async ValueTask DisposeAsync() =>
await Task.Run(() => GC.SuppressFinalize(this));

/// <summary>
/// Returns the current NHL playofff schedule for the current season
/// </summary>
/// <param name="seasonYear">The eight digit number format for the season, see <see cref="SeasonYear"/> for more information, Example: 20232024</param>
/// <param name="cancellationToken"> A cancellation token that can be used by other objects or threads to receive notice of cancellation</param>
/// <returns>Returns a collection of playoff series match ups by year</returns>
public async Task<PlayoffSeriesSchedule> GetPlayoffSeriesBySeasonYearAsync(string seasonYear, CancellationToken cancellationToken = default) =>
await _nhlLeagueApi.GetPlayoffSeriesBySeasonYearAsync(seasonYear, cancellationToken);
}
10 changes: 9 additions & 1 deletion Nhl.Api/Src/LeagueApi/INhlLeagueApi.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Nhl.Api;
namespace Nhl.Api;

/// <summary>
/// The official unofficial NHL League API providing various NHL league information including teams, franchises, standings, awards and more
Expand Down Expand Up @@ -209,4 +209,12 @@ public interface INhlLeagueApi
/// <returns>Returns true or false based on the current time and date</returns>
public Task<bool> IsLeagueActiveAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Returns the current NHL playofff schedule for the current season
/// </summary>
/// <param name="seasonYear">The eight digit number format for the season, see <see cref="SeasonYear"/> for more information, Example: 20232024</param>
/// <param name="cancellationToken"> A cancellation token that can be used by other objects or threads to receive notice of cancellation</param>
/// <returns>Returns a collection of playoff series match ups by year </returns>
public Task<PlayoffSeriesSchedule> GetPlayoffSeriesBySeasonYearAsync(string seasonYear, CancellationToken cancellationToken = default);

}
18 changes: 18 additions & 0 deletions Nhl.Api/Src/LeagueApi/NhlLeagueApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -613,4 +613,22 @@ public async Task<LeagueMetadataInformation> GetLeagueMetadataInformationAsync(L
/// <param name="cancellationToken"> A cancellation token that can be used by other objects or threads to receive notice of cancellation</param>
/// <returns>Returns true or false based on the current time and date</returns>
public async Task<bool> IsLeagueActiveAsync(CancellationToken cancellationToken = default) => await this.IsRegularSeasonActiveAsync(cancellationToken) || await this.IsPlayoffSeasonActiveAsync(cancellationToken) || await this.IsPreSeasonActiveAsync(cancellationToken);

/// <summary>
/// Returns the current NHL playofff schedule for the current season
/// </summary>
/// <param name="seasonYear">The eight digit number format for the season, see <see cref="SeasonYear"/> for more information, Example: 20232024</param>
/// <param name="cancellationToken"> A cancellation token that can be used by other objects or threads to receive notice of cancellation</param>
/// <returns>Returns a collection of playoff series match ups by year </returns>
public async Task<PlayoffSeriesSchedule> GetPlayoffSeriesBySeasonYearAsync(string seasonYear, CancellationToken cancellationToken = default)
{
if (seasonYear?.Length != 8)
{
throw new ArgumentException("The season year must be in the eight digit format, Example: 20232024");
}


return await _nhlWebApiHttpClient.GetAsync<PlayoffSeriesSchedule>("/playoff-series/carousel/20232024", cancellationToken);
}

}

0 comments on commit d33987d

Please sign in to comment.