Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ jobs:
- name: ⚙️ Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: '7.0.x'
dotnet-version: '9.0.x'
- name: ⚒ Build solution
run: dotnet build
- name: ⚙️ Restore .NET tools
run: dotnet tool restore
- name: 📝 Produce OpenAPI specification
run: dotnet swagger tofile --output openapi.yaml --yaml ./src/CrowdParlay.Users.Api/bin/Debug/net7.0/CrowdParlay.Users.Api.dll v1
run: dotnet swagger tofile --output openapi.yaml --yaml ./src/CrowdParlay.Social.Api/bin/Debug/net9.0/CrowdParlay.Social.Api.dll v1
- name: 🔗 Upload OpenAPI specification as release asset
uses: actions/upload-release-asset@v1
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: ⚙️ Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
dotnet-version: 9.0.x
- name: 📥 Restore dependencies
run: dotnet restore
- name: ⚒ Build solution
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /app

COPY /src .
RUN dotnet restore "CrowdParlay.Users.Api/CrowdParlay.Users.Api.csproj"
RUN dotnet publish "CrowdParlay.Users.Api/CrowdParlay.Users.Api.csproj" -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:7.0
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "CrowdParlay.Users.Api.dll"]
39 changes: 19 additions & 20 deletions src/CrowdParlay.Users.Api/CrowdParlay.Users.Api.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
Expand All @@ -10,31 +10,30 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CrowdParlay.Communication" Version="2.0.0"/>
<PackageReference Include="Grpc.AspNetCore" Version="2.60.0" />
<PackageReference Include="Grpc.StatusProto" Version="2.61.0" />
<PackageReference Include="Grpc.Tools" Version="2.61.0">
<PackageReference Include="CrowdParlay.Communication" Version="2.0.1" />
<PackageReference Include="Grpc.AspNetCore" Version="2.71.0" />
<PackageReference Include="Grpc.StatusProto" Version="2.71.0" />
<PackageReference Include="Grpc.Tools" Version="2.72.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MassTransit.RabbitMQ" Version="8.0.16"/>
<PackageReference Include="MassTransit.RabbitMQ" Version="8.5.0" />
<PackageReference Include="Mediator.Abstractions" Version="2.1.7"/>
<PackageReference Include="Microsoft.AspNet.WebApi.Versioning" Version="5.1.0"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.12"/>
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.6" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0"/>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.12"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.12">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="5.6.0" />
<PackageReference Include="OpenIddict.Validation.ServerIntegration" Version="5.6.0" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0"/>
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0"/>
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1"/>
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="6.4.0" />
<PackageReference Include="OpenIddict.Validation.ServerIntegration" Version="6.4.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
7 changes: 3 additions & 4 deletions src/CrowdParlay.Users.Api/Extensions/HttpContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Security.Claims;
using Dodo.Primitives;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using OpenIddict.Abstractions;
Expand All @@ -9,16 +8,16 @@ namespace CrowdParlay.Users.Api.Extensions;

public static class HttpContextExtensions
{
public static Uuid? GetUserId(this HttpContext context)
public static Guid? GetUserId(this HttpContext context)
{
var userId =
context.User.GetClaim(CookieAuthenticationConstants.UserIdClaim)
?? context.User.GetClaim(OpenIddictConstants.Claims.Subject);

return Uuid.TryParse(userId, out var value) ? value : null;
return Guid.TryParse(userId, out var value) ? value : null;
}

public static async Task SignInAsync(this HttpContext context, string authenticationScheme, Uuid userId)
public static async Task SignInAsync(this HttpContext context, string authenticationScheme, Guid userId)
{
var userIdClaimType = authenticationScheme switch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace CrowdParlay.Users.Api.Services;

/// <summary>
/// An endpoint parameter transformer that applies kebab-case naming convention to endpoint routes and parameter names.
/// For example, turns '/Animal/8/AddType' into '/animals/8/add-type'.
/// For example, turns '/Animals/8/AddType' into '/animals/8/add-type'.
/// </summary>
public partial class KebabCaseParameterPolicy : IOutboundParameterTransformer
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Security.Claims;
using CrowdParlay.Users.Api.Extensions;
using CrowdParlay.Users.Domain.Abstractions;
using Dodo.Primitives;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Abstractions;
using OpenIddict.Server;
Expand All @@ -23,7 +22,7 @@ public async ValueTask HandleAsync(OpenIddictServerEvents.HandleTokenRequestCont
return;

var jwt = _jwtHandler.ReadJwtToken(context.Request.RefreshToken);
var userId = Uuid.Parse(jwt.Subject);
var userId = Guid.Parse(jwt.Subject);
var user = await _usersRepository.GetByIdAsync(userId);
if (user is null)
{
Expand Down
3 changes: 1 addition & 2 deletions src/CrowdParlay.Users.Api/Services/gRPC/UsersService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using CrowdParlay.Users.Domain.Abstractions;
using CrowdParlay.Users.gRPC;
using Dodo.Primitives;
using Google.Protobuf.WellKnownTypes;
using Google.Rpc;
using Grpc.Core;
Expand All @@ -17,7 +16,7 @@ public class UsersGrpcService : UsersService.UsersServiceBase

public override async Task<User> GetUser(GetUserRequest request, ServerCallContext context)
{
var user = await _users.GetByIdAsync(Uuid.Parse(request.Id));
var user = await _users.GetByIdAsync(Guid.Parse(request.Id));
return user.Adapt<User>();
}

Expand Down
3 changes: 1 addition & 2 deletions src/CrowdParlay.Users.Api/Swagger/SwaggerWebHostFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Reflection;
using CrowdParlay.Users.Api.Extensions;
using Dodo.Primitives;
using Microsoft.AspNetCore;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
Expand Down Expand Up @@ -33,7 +32,7 @@ public static IWebHost CreateWebHost() => WebHost.CreateDefaultBuilder()
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlDocsFileName));

options.SupportNonNullableReferenceTypes();
options.MapType<Uuid>(() => new OpenApiSchema
options.MapType<Guid>(() => new OpenApiSchema
{
Type = "string",
Format = "uuid"
Expand Down
26 changes: 17 additions & 9 deletions src/CrowdParlay.Users.Api/v1/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using CrowdParlay.Users.Application.Features.Users.Commands;
using CrowdParlay.Users.Application.Features.Users.Queries;
using CrowdParlay.Users.Application.Models;
using Dodo.Primitives;
using CrowdParlay.Users.Domain;
using Mapster;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
Expand All @@ -26,6 +26,17 @@ public class UsersController : ApiControllerBase
public UsersController(IDataProtectionProvider dataProtectionProvider) =>
_externalLoginTicketProtector = dataProtectionProvider.CreateProtector(ExternalLoginTicketsConstants.DataProtectionPurpose);

/// <summary>
/// Returns users.
/// </summary>
[HttpGet]
[Consumes(MediaTypeNames.Application.Json), Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(typeof(Page<Search.Response>), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.InternalServerError)]
[ProducesResponseType(typeof(ValidationProblem), (int)HttpStatusCode.BadRequest)]
public async Task<Page<Search.Response>> Search(SortingStrategy order, int offset, int count) =>
await Mediator.Send(new Search.Query(order, offset, count));

/// <summary>
/// Creates a user.
/// </summary>
Expand Down Expand Up @@ -74,7 +85,7 @@ public UsersController(IDataProtectionProvider dataProtectionProvider) =>
[ProducesResponseType(typeof(GetById.Response), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.InternalServerError)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.NotFound)]
public async Task<GetById.Response> GetById([FromRoute] Uuid userId) =>
public async Task<GetById.Response> GetById([FromRoute] Guid userId) =>
await Mediator.Send(new GetById.Query(userId));

/// <summary>
Expand All @@ -85,11 +96,8 @@ public UsersController(IDataProtectionProvider dataProtectionProvider) =>
[ProducesResponseType(typeof(GetById.Response), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.InternalServerError)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.NotFound)]
public async Task<UserInfoResponse> Self()
{
var user = await Mediator.Send(new GetById.Query(HttpContext.GetUserId()!.Value));
return user.Adapt<UserInfoResponse>();
}
public async Task<GetById.Response> Self() =>
await Mediator.Send(new GetById.Query(HttpContext.GetUserId()!.Value));

/// <summary>
/// Returns user with the specified username.
Expand All @@ -113,7 +121,7 @@ public async Task<UserInfoResponse> Self()
[ProducesResponseType(typeof(ValidationProblem), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.Forbidden)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.NotFound)]
public async Task<Update.Response> Update([FromRoute] Uuid userId, [FromBody] UsersUpdateRequest request)
public async Task<Update.Response> Update([FromRoute] Guid userId, [FromBody] UsersUpdateRequest request)
{
if (userId != HttpContext.GetUserId())
throw new ForbiddenException();
Expand All @@ -130,7 +138,7 @@ public async Task<UserInfoResponse> Self()
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.InternalServerError)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.Forbidden)]
[ProducesResponseType(typeof(Problem), (int)HttpStatusCode.NotFound)]
public async Task Delete([FromRoute] Uuid userId)
public async Task Delete([FromRoute] Guid userId)
{
if (userId != HttpContext.GetUserId())
throw new ForbiddenException();
Expand Down
4 changes: 1 addition & 3 deletions src/CrowdParlay.Users.Api/v1/DTOs/UsersDTOs.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using Dodo.Primitives;

namespace CrowdParlay.Users.Api.v1.DTOs;

public sealed record UserInfoResponse(
Uuid Id,
Guid Id,
string Username,
string DisplayName,
string? AvatarUrl);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CrowdParlay.Communication" Version="2.0.0"/>
<PackageReference Include="FluentValidation" Version="11.7.1"/>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.7.1"/>
<PackageReference Include="CrowdParlay.Communication" Version="2.0.1" />
<PackageReference Include="FluentValidation" Version="12.0.0" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.0.0" />
<PackageReference Include="Mapster" Version="7.4.0"/>
<PackageReference Include="MassTransit" Version="8.0.16"/>
<PackageReference Include="MassTransit" Version="8.5.0" />
<PackageReference Include="Mediator.Abstractions" Version="2.1.7"/>
<PackageReference Include="Mediator.SourceGenerator" Version="2.1.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="2.2.0"/>
<PackageReference Include="OpenIddict.Abstractions" Version="4.9.0"/>
<PackageReference Include="OpenIddict.Server.AspNetCore" Version="4.9.0"/>
<PackageReference Include="OpenIddict.Validation.AspNetCore" Version="4.9.0"/>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Abstractions" Version="2.3.0" />
<PackageReference Include="OpenIddict.Abstractions" Version="6.4.0" />
<PackageReference Include="OpenIddict.Server.AspNetCore" Version="6.4.0" />
<PackageReference Include="OpenIddict.Validation.AspNetCore" Version="6.4.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.12.1" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using CrowdParlay.Communication;
using CrowdParlay.Users.Domain.Abstractions;
using Dodo.Primitives;
using FluentValidation;
using MassTransit;
using Mediator;
Expand All @@ -9,7 +8,7 @@ namespace CrowdParlay.Users.Application.Features.Users.Commands;

public static class Delete
{
public sealed record Command(Uuid Id) : IRequest;
public sealed record Command(Guid Id) : IRequest;

public sealed class Validator : AbstractValidator<Command>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using CrowdParlay.Users.Application.Models;
using CrowdParlay.Users.Domain.Abstractions;
using CrowdParlay.Users.Domain.Entities;
using Dodo.Primitives;
using FluentValidation;
using MassTransit;
using Mediator;
Expand Down Expand Up @@ -91,7 +90,7 @@ public async ValueTask<Response> Handle(Command request, CancellationToken cance

var user = new User
{
Id = Uuid.NewTimeBased(),
Id = Guid.CreateVersion7(),
Username = request.Username,
Email = request.Email,
DisplayName = request.DisplayName,
Expand All @@ -110,7 +109,7 @@ public async ValueTask<Response> Handle(Command request, CancellationToken cance

var externalLogin = new ExternalLogin
{
Id = Uuid.NewTimeBased(),
Id = Guid.CreateVersion7(),
UserId = user.Id,
ProviderId = request.ExternalLoginTicket.ProviderId,
Identity = request.ExternalLoginTicket.Identity
Expand All @@ -122,7 +121,7 @@ public async ValueTask<Response> Handle(Command request, CancellationToken cance
}

public sealed record Response(
Uuid Id,
Guid Id,
string Username,
string Email,
string DisplayName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using CrowdParlay.Users.Application.Exceptions;
using CrowdParlay.Users.Application.Extensions;
using CrowdParlay.Users.Domain.Abstractions;
using Dodo.Primitives;
using FluentValidation;
using MassTransit;
using Mediator;
Expand All @@ -14,7 +13,7 @@ namespace CrowdParlay.Users.Application.Features.Users.Commands;
public static class Update
{
public sealed record Command(
Uuid Id,
Guid Id,
string? Username,
string? DisplayName,
string? Email,
Expand Down Expand Up @@ -103,7 +102,7 @@ await _users.GetByIdAsync(request.Id, cancellationToken)
}

public sealed record Response(
Uuid Id,
Guid Id,
string Username,
string DisplayName,
string Email,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using CrowdParlay.Users.Application.Exceptions;
using CrowdParlay.Users.Domain.Abstractions;
using Dodo.Primitives;
using FluentValidation;
using Mediator;

namespace CrowdParlay.Users.Application.Features.Users.Queries;

public static class GetById
{
public sealed record Query(Uuid Id) : IRequest<Response>;
public sealed record Query(Guid Id) : IRequest<Response>;

public sealed class Validator : AbstractValidator<Query>
{
Expand All @@ -31,5 +30,5 @@ await _users.GetByIdAsync(request.Id, cancellationToken)
}
}

public sealed record Response(Uuid Id, string Username, string DisplayName, string? AvatarUrl);
public sealed record Response(Guid Id, string Username, string DisplayName, string? AvatarUrl);
}
Loading
Loading