Skip to content

Commit

Permalink
Added GetUserStats GraphQL invocation of sproc
Browse files Browse the repository at this point in the history
  • Loading branch information
morrisonbrett committed Jan 3, 2025
1 parent 3b82c1a commit d004f79
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 1 deletion.
58 changes: 58 additions & 0 deletions HockeyPickup.Api.Tests/GraphQLTests/QueryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,62 @@ public async Task GetSession_CallsRepository()
result.Should().BeEquivalentTo(expectedSession);
sessionRepositoryMock.Verify(r => r.GetSessionAsync(1), Times.Once);
}

[Fact]
public async Task GetUserStats_ReturnsUserStats()
{
// Arrange
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
var loggerMock = new Mock<ILogger<Query>>();
var userRepositoryMock = new Mock<IUserRepository>();
var testDate = DateTime.UtcNow;

var expectedStats = new UserStatsResponse
{
MemberSince = testDate.AddYears(-1),
CurrentYearGamesPlayed = 5,
PriorYearGamesPlayed = 10,
CurrentYearBoughtTotal = 2,
PriorYearBoughtTotal = 3,
LastBoughtSessionDate = testDate,
CurrentYearSoldTotal = 1,
PriorYearSoldTotal = 2,
LastSoldSessionDate = testDate.AddDays(-7),
MostPlayedPosition = "Defense",
CurrentBuyRequests = 1
};

userRepositoryMock.Setup(repo => repo.GetUserStatsAsync("user1"))
.ReturnsAsync(expectedStats);

var query = new Query(httpContextAccessorMock.Object, loggerMock.Object);

// Act
var result = await query.GetUserStats("user1", userRepositoryMock.Object);

// Assert
result.Should().BeEquivalentTo(expectedStats);
userRepositoryMock.Verify(r => r.GetUserStatsAsync("user1"), Times.Once);
}

[Fact]
public async Task GetUserStats_WhenUserNotFound_ReturnsNull()
{
// Arrange
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
var loggerMock = new Mock<ILogger<Query>>();
var userRepositoryMock = new Mock<IUserRepository>();

userRepositoryMock.Setup(repo => repo.GetUserStatsAsync("nonexistent"))
.ReturnsAsync((UserStatsResponse?) null);

var query = new Query(httpContextAccessorMock.Object, loggerMock.Object);

// Act
var result = await query.GetUserStats("nonexistent", userRepositoryMock.Object);

// Assert
result.Should().BeNull();
userRepositoryMock.Verify(r => r.GetUserStatsAsync("nonexistent"), Times.Once);
}
}
9 changes: 9 additions & 0 deletions HockeyPickup.Api/Data/GraphQL/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,13 @@ public async Task<RegularSetDetailedResponse> GetRegularSet([GraphQLName("Regula
{
return await regularRepository.GetRegularSetAsync(regularSetId);
}

[Authorize]
[GraphQLDescription("Retrieves user statistics")]
[GraphQLType(typeof(UserStatsResponse))]
[GraphQLName("UserStats")]
public async Task<UserStatsResponse> GetUserStats([GraphQLName("UserId")][GraphQLDescription("The ID of the user")] string userId, [Service] IUserRepository userRepository)
{
return await userRepository.GetUserStatsAsync(userId);
}
}
1 change: 1 addition & 0 deletions HockeyPickup.Api/Data/Repositories/IUserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public interface IUserRepository
Task<IEnumerable<UserDetailedResponse>> GetDetailedUsersAsync();
Task<UserDetailedResponse> GetUserAsync(string userId);
Task<IEnumerable<LockerRoom13Response>> GetLockerRoom13SessionsAsync();
Task<UserStatsResponse?> GetUserStatsAsync(string userId);
}
11 changes: 10 additions & 1 deletion HockeyPickup.Api/Data/Repositories/UserRepository.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using HockeyPickup.Api.Data.Context;
using HockeyPickup.Api.Data.Entities;
using HockeyPickup.Api.Helpers;
using HockeyPickup.Api.Models.Domain;
using HockeyPickup.Api.Models.Responses;
using Microsoft.EntityFrameworkCore;
using System.Data;
using System.Diagnostics.CodeAnalysis;

namespace HockeyPickup.Api.Data.Repositories;

Expand Down Expand Up @@ -134,4 +134,13 @@ from buySell in buySells.DefaultIfEmpty()
.OrderBy(r => r.SessionDate)
.ToListAsync();
}

[ExcludeFromCodeCoverage]
public async Task<UserStatsResponse?> GetUserStatsAsync(string userId)
{
return (await _context.Database
.SqlQuery<UserStatsResponse>($"EXEC GetUserStats @UserId={userId}")
.ToListAsync())
.FirstOrDefault();
}
}
93 changes: 93 additions & 0 deletions HockeyPickup.Api/Models/Responses/UserStatsResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Text.Json.Serialization;

[GraphQLName("UserStats")]
public class UserStatsResponse
{
[Required]
[Description("Date when user became a member")]
[JsonPropertyName("MemberSince")]
[JsonProperty(nameof(MemberSince), Required = Required.Always)]
[GraphQLName("MemberSince")]
[GraphQLDescription("Date when user became a member")]
public required DateTime MemberSince { get; set; }

[Required]
[Description("Games played in current year")]
[JsonPropertyName("CurrentYearGamesPlayed")]
[JsonProperty(nameof(CurrentYearGamesPlayed), Required = Required.Always)]
[GraphQLName("CurrentYearGamesPlayed")]
[GraphQLDescription("Games played in current year")]
public required int CurrentYearGamesPlayed { get; set; }

[Required]
[Description("Games played in prior year")]
[JsonPropertyName("PriorYearGamesPlayed")]
[JsonProperty(nameof(PriorYearGamesPlayed), Required = Required.Always)]
[GraphQLName("PriorYearGamesPlayed")]
[GraphQLDescription("Games played in prior year")]
public required int PriorYearGamesPlayed { get; set; }

[Required]
[Description("Spots bought in current year")]
[JsonPropertyName("CurrentYearBoughtTotal")]
[JsonProperty(nameof(CurrentYearBoughtTotal), Required = Required.Always)]
[GraphQLName("CurrentYearBoughtTotal")]
[GraphQLDescription("Spots bought in current year")]
public required int CurrentYearBoughtTotal { get; set; }

[Required]
[Description("Spots bought in prior year")]
[JsonPropertyName("PriorYearBoughtTotal")]
[JsonProperty(nameof(PriorYearBoughtTotal), Required = Required.Always)]
[GraphQLName("PriorYearBoughtTotal")]
[GraphQLDescription("Spots bought in prior year")]
public required int PriorYearBoughtTotal { get; set; }

[Description("Date of last bought session")]
[JsonPropertyName("LastBoughtSessionDate")]
[JsonProperty(nameof(LastBoughtSessionDate))]
[GraphQLName("LastBoughtSessionDate")]
[GraphQLDescription("Date of last bought session")]
public DateTime? LastBoughtSessionDate { get; set; }

[Required]
[Description("Spots sold in current year")]
[JsonPropertyName("CurrentYearSoldTotal")]
[JsonProperty(nameof(CurrentYearSoldTotal), Required = Required.Always)]
[GraphQLName("CurrentYearSoldTotal")]
[GraphQLDescription("Spots sold in current year")]
public required int CurrentYearSoldTotal { get; set; }

[Required]
[Description("Spots sold in prior year")]
[JsonPropertyName("PriorYearSoldTotal")]
[JsonProperty(nameof(PriorYearSoldTotal), Required = Required.Always)]
[GraphQLName("PriorYearSoldTotal")]
[GraphQLDescription("Spots sold in prior year")]
public required int PriorYearSoldTotal { get; set; }

[Description("Date of last sold session")]
[JsonPropertyName("LastSoldSessionDate")]
[JsonProperty(nameof(LastSoldSessionDate))]
[GraphQLName("LastSoldSessionDate")]
[GraphQLDescription("Date of last sold session")]
public DateTime? LastSoldSessionDate { get; set; }

[Description("Most frequently played position")]
[JsonPropertyName("MostPlayedPosition")]
[JsonProperty(nameof(MostPlayedPosition))]
[GraphQLName("MostPlayedPosition")]
[GraphQLDescription("Most frequently played position")]
public string? MostPlayedPosition { get; set; }

[Required]
[Description("Current active buy requests")]
[JsonPropertyName("CurrentBuyRequests")]
[JsonProperty(nameof(CurrentBuyRequests), Required = Required.Always)]
[GraphQLName("CurrentBuyRequests")]
[GraphQLDescription("Current active buy requests")]
public required int CurrentBuyRequests { get; set; }
}
2 changes: 2 additions & 0 deletions HockeyPickup.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public static void Main(string[] args)
o.DocumentProcessors.Add(new CustomModelDocumentProcessor<AspNetUser>());
o.DocumentProcessors.Add(new CustomModelDocumentProcessor<ApiResponse>());
o.DocumentProcessors.Add(new CustomModelDocumentProcessor<ApiDataResponse<object>>());
o.DocumentProcessors.Add(new CustomModelDocumentProcessor<UserStatsResponse>());
});
builder.Services.AddSwaggerGen(o =>
{
Expand All @@ -89,6 +90,7 @@ public static void Main(string[] args)
o.DocumentFilter<CustomModelDocumentFilter<AspNetUser>>();
o.DocumentFilter<CustomModelDocumentFilter<ApiResponse>>();
o.DocumentFilter<CustomModelDocumentFilter<ApiDataResponse<object>>>();
o.DocumentFilter<CustomModelDocumentFilter<UserStatsResponse>>();

o.SwaggerDoc("v1", new OpenApiInfo()
{
Expand Down

0 comments on commit d004f79

Please sign in to comment.