mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
Fix code analysis warnings
This commit is contained in:
@@ -8,6 +8,13 @@
|
||||
"csharpier"
|
||||
],
|
||||
"rollForward": false
|
||||
},
|
||||
"jetbrains.resharper.globaltools": {
|
||||
"version": "2026.2.1",
|
||||
"commands": [
|
||||
"jb"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,4 +83,4 @@ public class JwtAuthenticationHandler(
|
||||
/// <summary>
|
||||
/// Options for the <c>JWT</c> authentication scheme handled by <see cref="JwtAuthenticationHandler" />.
|
||||
/// </summary>
|
||||
public class JwtAuthenticationOptions : AuthenticationSchemeOptions { }
|
||||
public class JwtAuthenticationOptions : AuthenticationSchemeOptions;
|
||||
|
||||
@@ -413,7 +413,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have a valid access token for my account")]
|
||||
public void GivenIHaveAValidAccessTokenForMyAccount()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue<Guid>(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
@@ -432,7 +432,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have a valid confirmation token for my account")]
|
||||
public void GivenIHaveAValidConfirmationTokenForMyAccount()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue<Guid>(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
@@ -451,7 +451,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have an expired confirmation token for my account")]
|
||||
public void GivenIHaveAnExpiredConfirmationTokenForMyAccount()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue<Guid>(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
@@ -470,7 +470,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have a confirmation token signed with the wrong secret")]
|
||||
public void GivenIHaveAConfirmationTokenSignedWithTheWrongSecret()
|
||||
{
|
||||
Guid userId = scenario.TryGetValue<Guid>(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found in scenario");
|
||||
string? username = scenario.TryGetValue<string>(RegisteredUsernameKey, out string? user)
|
||||
@@ -668,7 +668,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
}
|
||||
|
||||
[When("I submit a refresh token request using a GET request")]
|
||||
public async Task WhenISubmitARefreshTokenRequestUsingAGETRequest()
|
||||
public async Task WhenISubmitARefreshTokenRequestUsingAGetRequest()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
HttpRequestMessage requestMessage = new(HttpMethod.Get, "/api/auth/refresh")
|
||||
@@ -979,7 +979,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitAResendConfirmationRequestForMyAccount()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
Guid userId = scenario.TryGetValue<Guid>(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: throw new InvalidOperationException("registered user ID not found");
|
||||
string? accessToken = scenario.TryGetValue<string>("accessToken", out string? at)
|
||||
@@ -1025,7 +1025,7 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitAResendConfirmationRequestWithoutAnAccessToken()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
Guid userId = scenario.TryGetValue<Guid>(RegisteredUserIdKey, out Guid id)
|
||||
Guid userId = scenario.TryGetValue(RegisteredUserIdKey, out Guid id)
|
||||
? id
|
||||
: Guid.NewGuid();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using Database.Seed.PipelineData;
|
||||
using Database.Seed.Sqlite;
|
||||
using Domain.Entities;
|
||||
using Features.Auth.DependencyInjection;
|
||||
using Features.Auth.Dtos;
|
||||
using Features.Auth.Repository;
|
||||
using Features.Breweries.DependencyInjection;
|
||||
using Features.Breweries.Repository;
|
||||
@@ -255,7 +254,7 @@ static Table BuildUserTable(IReadOnlyList<UserRecord> users)
|
||||
user.User.FirstName,
|
||||
user.User.LastName,
|
||||
user.Email,
|
||||
user.DateOfBirth.ToString()
|
||||
user.DateOfBirth
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ public sealed class BeerPost
|
||||
// Attributes
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public decimal ABV { get; set; }
|
||||
public int IBU { get; set; }
|
||||
public decimal Abv { get; set; }
|
||||
public int Ibu { get; set; }
|
||||
|
||||
// Audit Fields
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
@@ -8,7 +8,7 @@ public sealed class Country
|
||||
// Attributes
|
||||
public string CountryName { get; set; } = string.Empty;
|
||||
|
||||
public string ISO3166_1 { get; set; } = string.Empty; // this is the iso 3166-1 alpha-2 code for the country, e.g. "US" for United States, "CA" for Canada, etc.
|
||||
public string Iso31661 { get; set; } = string.Empty; // this is the iso 3166-1 alpha-2 code for the country, e.g. "US" for United States, "CA" for Canada, etc.
|
||||
|
||||
// Audit Fields
|
||||
public byte[]? RowVersion { get; set; }
|
||||
|
||||
@@ -13,7 +13,7 @@ public sealed class StateProvince
|
||||
|
||||
// this is the iso 3166-2 code for the state/province, e.g. "CA" for California, "ON" for Ontario, etc.
|
||||
// and is rendered like US-CA for California, CA-ON for Ontario, etc.
|
||||
public string ISO3166_2 { get; set; } = string.Empty;
|
||||
public string Iso31662 { get; set; } = string.Empty;
|
||||
|
||||
// Audit Fields
|
||||
public byte[]? RowVersion { get; set; }
|
||||
|
||||
@@ -11,14 +11,12 @@ namespace Features.Auth.Tests.Services;
|
||||
|
||||
public class TokenServiceValidationTests
|
||||
{
|
||||
private readonly Mock<IAuthRepository> _authRepositoryMock;
|
||||
private readonly Mock<ITokenInfrastructure> _tokenInfraMock;
|
||||
private readonly TokenService _tokenService;
|
||||
|
||||
public TokenServiceValidationTests()
|
||||
{
|
||||
_tokenInfraMock = new Mock<ITokenInfrastructure>();
|
||||
_authRepositoryMock = new Mock<IAuthRepository>();
|
||||
|
||||
// Set environment variables for tokens
|
||||
Environment.SetEnvironmentVariable(
|
||||
@@ -34,7 +32,7 @@ public class TokenServiceValidationTests
|
||||
"test-confirmation-secret-that-is-very-long-1234567890"
|
||||
);
|
||||
|
||||
_tokenService = new TokenService(_tokenInfraMock.Object, _authRepositoryMock.Object);
|
||||
_tokenService = new TokenService(_tokenInfraMock.Object, new Mock<IAuthRepository>().Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Data.Common;
|
||||
using Dapper;
|
||||
using Domain.Entities;
|
||||
using Domain.Exceptions;
|
||||
using Features.Auth.Dtos;
|
||||
using Infrastructure.Sql;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
@@ -46,7 +45,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
|
||||
INSERT INTO dbo.UserCredential (UserAccountId, Hash)
|
||||
VALUES (@UserAccountId, @Hash);
|
||||
""",
|
||||
new { UserAccountId = userAccountId, Hash = ua.UserCredential?.Hash },
|
||||
new { UserAccountId = userAccountId, ua.UserCredential?.Hash },
|
||||
transaction
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Domain.Entities;
|
||||
using Features.Auth.Dtos;
|
||||
|
||||
namespace Features.Auth.Repository;
|
||||
|
||||
|
||||
@@ -62,10 +62,6 @@ public interface ITokenService
|
||||
/// </summary>
|
||||
string GenerateConfirmationToken(UserAccount user);
|
||||
|
||||
/// <summary>Generates a token of the type specified by <typeparamref name="T" />.</summary>
|
||||
/// <typeparam name="T">Must be <see cref="TokenType" />.</typeparam>
|
||||
string GenerateToken<T>(UserAccount user)
|
||||
where T : struct, Enum;
|
||||
|
||||
/// <summary>Validates an access token's signature and expiration and extracts the caller's identity.</summary>
|
||||
/// <exception cref="Domain.Exceptions.UnauthorizedException">
|
||||
|
||||
@@ -88,30 +88,6 @@ public class TokenService : ITokenService
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when <typeparamref name="T" /> is not <see cref="TokenType" /> or does not resolve to a known token type.
|
||||
/// </exception>
|
||||
public string GenerateToken<T>(UserAccount user)
|
||||
where T : struct, Enum
|
||||
{
|
||||
if (typeof(T) != typeof(TokenType))
|
||||
throw new InvalidOperationException("Invalid token type");
|
||||
|
||||
string tokenTypeName = typeof(T).Name;
|
||||
if (!Enum.TryParse(typeof(TokenType), tokenTypeName, out object? parsed))
|
||||
throw new InvalidOperationException("Invalid token type");
|
||||
|
||||
TokenType tokenType = (TokenType)parsed;
|
||||
return tokenType switch
|
||||
{
|
||||
TokenType.AccessToken => GenerateAccessToken(user),
|
||||
TokenType.RefreshToken => GenerateRefreshToken(user),
|
||||
TokenType.ConfirmationToken => GenerateConfirmationToken(user),
|
||||
_ => throw new InvalidOperationException("Invalid token type"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ValidatedToken> ValidateAccessTokenAsync(string token)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ public class GetBreweryByIdHandlerTests
|
||||
);
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result!.BreweryPostId.Should().Be(brewery.BreweryPostId);
|
||||
result.BreweryPostId.Should().Be(brewery.BreweryPostId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -25,7 +25,6 @@ public class CreateBreweryValidator : AbstractValidator<CreateBreweryCommand>
|
||||
|
||||
RuleFor(x => x.Location.CityId)
|
||||
.NotEmpty()
|
||||
.When(x => x.Location is not null)
|
||||
.WithMessage("CityId is required.");
|
||||
|
||||
RuleFor(x => x.Location.AddressLine1)
|
||||
@@ -33,15 +32,12 @@ public class CreateBreweryValidator : AbstractValidator<CreateBreweryCommand>
|
||||
.When(x => x.Location is not null)
|
||||
.WithMessage("Address line 1 is required.")
|
||||
.MaximumLength(256)
|
||||
.When(x => x.Location is not null)
|
||||
.WithMessage("Address line 1 cannot exceed 256 characters.");
|
||||
|
||||
RuleFor(x => x.Location.PostalCode)
|
||||
.NotEmpty()
|
||||
.When(x => x.Location is not null)
|
||||
.WithMessage("Postal code is required.")
|
||||
.MaximumLength(20)
|
||||
.When(x => x.Location is not null)
|
||||
.WithMessage("Postal code cannot exceed 20 characters.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ public static class BreweryDtoMapper
|
||||
CityName: brewery.Location.City?.CityName ?? string.Empty,
|
||||
StateProvinceName: brewery.Location.City?.StateProvince?.StateProvinceName
|
||||
?? string.Empty,
|
||||
StateProvinceCode: brewery.Location.City?.StateProvince?.ISO3166_2
|
||||
StateProvinceCode: brewery.Location.City?.StateProvince?.Iso31662
|
||||
?? string.Empty,
|
||||
CountryName: brewery.Location.City?.StateProvince?.Country?.CountryName
|
||||
?? string.Empty,
|
||||
CountryCode: brewery.Location.City?.StateProvince?.Country?.ISO3166_1
|
||||
CountryCode: brewery.Location.City?.StateProvince?.Country?.Iso31661
|
||||
?? string.Empty,
|
||||
AddressLine1: brewery.Location.AddressLine1,
|
||||
AddressLine2: brewery.Location.AddressLine2,
|
||||
|
||||
@@ -36,7 +36,7 @@ public class EmailDispatcher(
|
||||
confirmationLink
|
||||
);
|
||||
|
||||
await emailProvider.SendAsync(email, "Welcome to The Biergarten App!", emailHtml, true);
|
||||
await emailProvider.SendAsync(email, "Welcome to The Biergarten App!", emailHtml);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -59,7 +59,7 @@ public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
|
||||
""",
|
||||
new
|
||||
{
|
||||
UserAccountId = userAccount.UserAccountId,
|
||||
userAccount.UserAccountId,
|
||||
userAccount.Username,
|
||||
userAccount.FirstName,
|
||||
userAccount.LastName,
|
||||
|
||||
@@ -1,44 +1 @@
|
||||
namespace Infrastructure.Sql;
|
||||
|
||||
/// <summary>
|
||||
/// Custom SQL Server error numbers (50000+) that a stored procedure could use to signal
|
||||
/// domain-level failures via <c>THROW</c>, for a repository to catch as
|
||||
/// <see cref="Microsoft.Data.SqlClient.SqlException" /> and translate via <c>ex.Number</c> into
|
||||
/// the corresponding <c>Domain.Exceptions</c> type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Currently unused: the schema has no stored procedures, and no repository throws or catches on
|
||||
/// these numbers. Repositories instead run their own existence/concurrency checks and throw
|
||||
/// <c>Domain.Exceptions</c> directly (see <c>docs/website/database.md</c> for the current
|
||||
/// approach). This type is kept in case a future stored procedure reintroduces the pattern; each
|
||||
/// number would
|
||||
/// identify a category of error shared by every procedure that raises it, not a single call site.
|
||||
/// </remarks>
|
||||
public static class SqlErrorCodes
|
||||
{
|
||||
/// <summary>
|
||||
/// A stored procedure's own input validation failed (e.g. a required parameter was <c>NULL</c>).
|
||||
/// Maps to HTTP 400 Bad Request.
|
||||
/// </summary>
|
||||
public const int Validation = 50400;
|
||||
|
||||
/// <summary>
|
||||
/// A stored procedure could not find a row it required, by primary key or foreign key. Maps to
|
||||
/// HTTP 404 Not Found.
|
||||
/// </summary>
|
||||
public const int NotFound = 50404;
|
||||
|
||||
/// <summary>
|
||||
/// A stored procedure detected a conflict: either a duplicate/already-exists row, or a stale
|
||||
/// optimistic-concurrency token (row-version mismatch). Maps to HTTP 409 Conflict.
|
||||
/// </summary>
|
||||
public const int Conflict = 50409;
|
||||
|
||||
/// <summary>
|
||||
/// A defensive check inside a stored procedure failed under conditions that should be
|
||||
/// unreachable in normal operation (e.g. an <c>INSERT</c> that should always affect one row
|
||||
/// affected zero). Left uncaught by repositories so it falls through to the generic
|
||||
/// <see cref="Microsoft.Data.SqlClient.SqlException" /> handler. Maps to HTTP 500/503.
|
||||
/// </summary>
|
||||
public const int InternalFailure = 50500;
|
||||
}
|
||||
namespace Infrastructure.Sql;
|
||||
9181
web/backend/report.sarif
Normal file
9181
web/backend/report.sarif
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user