Remove unused fat
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Ardalis.GuardClauses" Version="4.2.0" />
|
||||
<PackageVersion Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
|
||||
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
|
||||
@@ -16,7 +15,6 @@
|
||||
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.8.1" />
|
||||
<PackageVersion Include="Google.Apis.Oauth2.v2" Version="1.67.0.1869" />
|
||||
<PackageVersion Include="JetBrains.Annotations" Version="2024.2.0" />
|
||||
<PackageVersion Include="MediatR" Version="12.2.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Facebook" Version="8.0.4" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.4" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
||||
|
||||
37
Hutopy.sln
37
Hutopy.sln
@@ -3,18 +3,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "src\Domain\Domain.csproj", "{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "src\Application\Application.csproj", "{34C0FACD-F3D9-400C-8945-554DD6B0819A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{117DA02F-5274-4565-ACC6-DA9B6E568B09}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6ED356A7-8B47-4613-AD01-C85CF28491BD}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{664D406C-2F83-48F0-BFC3-408D5CB53C65}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain.UnitTests", "tests\Domain.UnitTests\Domain.UnitTests.csproj", "{DC37FD87-552C-4613-9F16-1537CA522898}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E2DA20AA-28D1-455C-BF50-C49A8F831633}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
@@ -30,56 +22,27 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{4E4EE20C-F06A-4A1B-851F-C5577796941C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.FunctionalTests", "tests\Application.FunctionalTests\Application.FunctionalTests.csproj", "{EA6127A5-94C9-4C31-AD11-E6811B92B520}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure.IntegrationTests", "tests\Infrastructure.IntegrationTests\Infrastructure.IntegrationTests.csproj", "{01FA6786-921D-4CE8-8C50-4FDA66C9477D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DC37FD87-552C-4613-9F16-1537CA522898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DC37FD87-552C-4613-9F16-1537CA522898}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DC37FD87-552C-4613-9F16-1537CA522898}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DC37FD87-552C-4613-9F16-1537CA522898}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
|
||||
{34C0FACD-F3D9-400C-8945-554DD6B0819A} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
|
||||
{117DA02F-5274-4565-ACC6-DA9B6E568B09} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
|
||||
{DC37FD87-552C-4613-9F16-1537CA522898} = {664D406C-2F83-48F0-BFC3-408D5CB53C65}
|
||||
{4E4EE20C-F06A-4A1B-851F-C5577796941C} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
|
||||
{EA6127A5-94C9-4C31-AD11-E6811B92B520} = {664D406C-2F83-48F0-BFC3-408D5CB53C65}
|
||||
{01FA6786-921D-4CE8-8C50-4FDA66C9477D} = {664D406C-2F83-48F0-BFC3-408D5CB53C65}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3CB609D9-5D54-4C11-A371-DAAC8B74E430}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
## Patterns / strategy used
|
||||
- Clean Architecture ( with Infrastructure, Domain, Application and Web layers )
|
||||
- Minimal API endpoints.
|
||||
- Guards ( Fail fast ) : https://github.com/ardalis/GuardClauses
|
||||
|
||||
## Tools
|
||||
- Install Docker : https://www.docker.com/get-started/
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Hutopy.Application</RootNamespace>
|
||||
<AssemblyName>Hutopy.Application</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ardalis.GuardClauses" />
|
||||
<PackageReference Include="AutoMapper" />
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
|
||||
<PackageReference Include="Google.Apis.Oauth2.v2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||
<PackageReference Include="MinimalApis.Extensions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Domain\Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Hutopy.Domain.Common;
|
||||
|
||||
public abstract class BaseAuditableEntity : BaseEntity
|
||||
{
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public Guid? CreatedBy { get; set; }
|
||||
|
||||
public DateTimeOffset LastModifiedAt { get; set; }
|
||||
|
||||
public Guid? LastModifiedBy { get; set; }
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Hutopy.Domain.Common;
|
||||
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types.
|
||||
// Using non-generic integer types for simplicity
|
||||
public Guid Id { get; set; }
|
||||
|
||||
private readonly List<BaseEvent> _domainEvents = [];
|
||||
|
||||
[NotMapped]
|
||||
public IEnumerable<BaseEvent> DomainEvents => _domainEvents.AsReadOnly();
|
||||
|
||||
public void AddDomainEvent(BaseEvent domainEvent)
|
||||
{
|
||||
_domainEvents.Add(domainEvent);
|
||||
}
|
||||
|
||||
public void RemoveDomainEvent(BaseEvent domainEvent)
|
||||
{
|
||||
_domainEvents.Remove(domainEvent);
|
||||
}
|
||||
|
||||
public void ClearDomainEvents()
|
||||
{
|
||||
_domainEvents.Clear();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using MediatR;
|
||||
|
||||
namespace Hutopy.Domain.Common;
|
||||
|
||||
public abstract class BaseEvent : INotification
|
||||
{
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
namespace Hutopy.Domain.Common;
|
||||
|
||||
// Learn more: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/implement-value-objects
|
||||
public abstract class ValueObject
|
||||
{
|
||||
private static bool EqualOperator(ValueObject left, ValueObject right)
|
||||
{
|
||||
if (left is null ^ right is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return left?.Equals(right!) != false;
|
||||
}
|
||||
|
||||
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
|
||||
{
|
||||
return !(EqualOperator(left, right));
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<object> GetEqualityComponents();
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var other = (ValueObject)obj;
|
||||
return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCode();
|
||||
|
||||
foreach (var component in GetEqualityComponents())
|
||||
{
|
||||
hash.Add(component);
|
||||
}
|
||||
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Hutopy.Domain.Constants;
|
||||
|
||||
public abstract class Policies
|
||||
{
|
||||
public const string CanPurge = nameof(CanPurge);
|
||||
public const string CanDelete = nameof(CanDelete);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Hutopy.Domain</RootNamespace>
|
||||
<AssemblyName>Hutopy.Domain</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Apis.Oauth2.v2" />
|
||||
<PackageReference Include="MediatR" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Hutopy.Domain.Enums;
|
||||
|
||||
public enum PriorityLevel
|
||||
{
|
||||
None = 0,
|
||||
Low = 1,
|
||||
Medium = 2,
|
||||
High = 3
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
namespace Hutopy.Domain.Exceptions;
|
||||
|
||||
public class UnsupportedColourException(
|
||||
string code) : Exception($"Colour \"{code}\" is unsupported.");
|
||||
@@ -1,2 +0,0 @@
|
||||
global using Hutopy.Domain.Common;
|
||||
global using Hutopy.Domain.Exceptions;
|
||||
@@ -1,69 +0,0 @@
|
||||
namespace Hutopy.Domain.ValueObjects;
|
||||
|
||||
public class Colour(string code) : ValueObject
|
||||
{
|
||||
public static Colour From(string code)
|
||||
{
|
||||
var colour = new Colour(code);
|
||||
|
||||
if (!SupportedColours.Contains(colour))
|
||||
{
|
||||
throw new UnsupportedColourException(code);
|
||||
}
|
||||
|
||||
return colour;
|
||||
}
|
||||
|
||||
public static Colour White => new("#FFFFFF");
|
||||
|
||||
public static Colour Red => new("#FF5733");
|
||||
|
||||
public static Colour Orange => new("#FFC300");
|
||||
|
||||
public static Colour Yellow => new("#FFFF66");
|
||||
|
||||
public static Colour Green => new("#CCFF99");
|
||||
|
||||
public static Colour Blue => new("#6666FF");
|
||||
|
||||
public static Colour Purple => new("#9966CC");
|
||||
|
||||
public static Colour Grey => new("#999999");
|
||||
|
||||
public string Code { get; private set; } = string.IsNullOrWhiteSpace(code)?"#000000":code;
|
||||
|
||||
public static implicit operator string(Colour colour)
|
||||
{
|
||||
return colour.ToString();
|
||||
}
|
||||
|
||||
public static explicit operator Colour(string code)
|
||||
{
|
||||
return From(code);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Code;
|
||||
}
|
||||
|
||||
private static IEnumerable<Colour> SupportedColours
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return White;
|
||||
yield return Red;
|
||||
yield return Orange;
|
||||
yield return Yellow;
|
||||
yield return Green;
|
||||
yield return Blue;
|
||||
yield return Purple;
|
||||
yield return Grey;
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<object> GetEqualityComponents()
|
||||
{
|
||||
yield return Code;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Azure.Storage.Blobs.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Hutopy.Infrastructure.AzureBlob;
|
||||
namespace Hutopy.Infrastructure.BlobStorage;
|
||||
|
||||
public class AzureBlobStorage
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Hutopy.Application.AzureBlobStorage.Constants;
|
||||
namespace Hutopy.Infrastructure.BlobStorage;
|
||||
|
||||
public static class CommonFileNames
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Hutopy.Application.AzureBlobStorage.Constants;
|
||||
namespace Hutopy.Infrastructure.BlobStorage;
|
||||
|
||||
public static class ContainerNames
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Hutopy.Infrastructure.AzureBlob;
|
||||
namespace Hutopy.Infrastructure.BlobStorage;
|
||||
|
||||
public static class ContentTypes
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Hutopy.Application.AzureBlobStorage.Constants;
|
||||
namespace Hutopy.Infrastructure.BlobStorage;
|
||||
|
||||
public static class SubDirectoryNames
|
||||
{
|
||||
@@ -1,5 +1,4 @@
|
||||
using Hutopy.Domain.Constants;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -55,13 +54,13 @@ public class ApplicationDbContextInitializer(
|
||||
|
||||
private async Task TrySeedAsync()
|
||||
{
|
||||
var administratorRole = new ApplicationRole(Roles.Administrator);
|
||||
var administratorRole = new ApplicationRole(KnownRoles.Administrator);
|
||||
if (roleManager.Roles.All(r => r.Name != administratorRole.Name))
|
||||
{
|
||||
await roleManager.CreateAsync(administratorRole);
|
||||
}
|
||||
|
||||
var roleCreator = new ApplicationRole(Roles.Creator);
|
||||
var roleCreator = new ApplicationRole(KnownRoles.Creator);
|
||||
if (roleManager.Roles.All(r => r.Name != roleCreator.Name))
|
||||
{
|
||||
await roleManager.CreateAsync(roleCreator);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Hutopy.Domain.Constants;
|
||||
using Hutopy.Infrastructure.AzureBlob;
|
||||
using Hutopy.Infrastructure.BlobStorage;
|
||||
using Hutopy.Infrastructure.Data;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@@ -49,9 +48,6 @@ public static class DependencyInjection
|
||||
// Scoped services
|
||||
services.AddScoped<IdentityService>();
|
||||
|
||||
services.AddAuthorization(options =>
|
||||
options.AddPolicy(Policies.CanPurge, policy => policy.RequireRole(Roles.Administrator)));
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Hutopy.Domain.Constants;
|
||||
namespace Hutopy.Infrastructure.Identity;
|
||||
|
||||
public abstract class Roles
|
||||
public static class KnownRoles
|
||||
{
|
||||
public const string Administrator = nameof(Administrator);
|
||||
public const string Creator = nameof(Creator);
|
||||
@@ -18,9 +18,6 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools"/>
|
||||
<PackageReference Include="Stripe.net"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Application\Application.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Migrations\"/>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||
using Hutopy.Infrastructure.AzureBlob;
|
||||
using Hutopy.Infrastructure.BlobStorage;
|
||||
using Hutopy.Web.Features.Contents.Data;
|
||||
|
||||
namespace Hutopy.Web.Features.Contents.Handlers;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||
using Hutopy.Infrastructure.AzureBlob;
|
||||
using Hutopy.Infrastructure.BlobStorage;
|
||||
using Hutopy.Web.Features.Contents.Data;
|
||||
|
||||
namespace Hutopy.Web.Features.Contents.Handlers;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||
using Hutopy.Infrastructure.AzureBlob;
|
||||
using Hutopy.Infrastructure.BlobStorage;
|
||||
using Hutopy.Web.Common;
|
||||
using Hutopy.Web.Features.Contents.Data;
|
||||
using Hutopy.Web.Features.Contents.Handlers.Models;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||
using Hutopy.Infrastructure.AzureBlob;
|
||||
using Hutopy.Infrastructure.BlobStorage;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
using Hutopy.Web.Common;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||
using Hutopy.Infrastructure.AzureBlob;
|
||||
using Hutopy.Infrastructure.BlobStorage;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
|
||||
namespace Hutopy.Web.Features.Users.Handlers;
|
||||
|
||||
@@ -30,8 +30,7 @@ public class GoogleUserInfo
|
||||
|
||||
[PublicAPI]
|
||||
public record LoginWithGoogleRequest(
|
||||
string Token)
|
||||
: IRequest<LoginWithGoogleResponse>;
|
||||
string Token);
|
||||
|
||||
public record LoginWithGoogleResponse(
|
||||
string AccessToken,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
global using Ardalis.GuardClauses;
|
||||
global using FastEndpoints;
|
||||
global using FluentValidation;
|
||||
global using JetBrains.Annotations;
|
||||
global using MediatR;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Azure.Identity;
|
||||
using Hutopy.Application;
|
||||
using Hutopy.Infrastructure;
|
||||
using Hutopy.Infrastructure.Data;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Hutopy.Domain.Constants;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
using Hutopy.Web.Common;
|
||||
using Hutopy.Web.Features.Contents.Data;
|
||||
using Hutopy.Web.Features.Messages.Data;
|
||||
@@ -34,7 +33,7 @@ internal class TestDataSeeder(
|
||||
{
|
||||
if (contentContext.Contents.Any()) return;
|
||||
|
||||
_users.Add(await CreateUserAsync("admin", null, Roles.Administrator));
|
||||
_users.Add(await CreateUserAsync("admin", null, KnownRoles.Administrator));
|
||||
var userA = await CreateUserAsync("userA", null);
|
||||
_users.Add(userA);
|
||||
_users.Add(await CreateUserAsync("userB", null));
|
||||
@@ -44,7 +43,7 @@ internal class TestDataSeeder(
|
||||
var creatorUser = await CreateUserAsync(
|
||||
creator.Name,
|
||||
creator.Images.Logo,
|
||||
Roles.Creator);
|
||||
KnownRoles.Creator);
|
||||
|
||||
creator.Id = creatorUser.Id;
|
||||
creator.CreatedBy = creator.Id;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Application\Application.csproj" />
|
||||
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Hutopy.Application.FunctionalTests</RootNamespace>
|
||||
<AssemblyName>Hutopy.Application.FunctionalTests</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="appsettings.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="nunit" />
|
||||
<PackageReference Include="NUnit.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="Respawn" />
|
||||
<PackageReference Include="Testcontainers.MsSql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Web\Web.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Hutopy.Application.FunctionalTests;
|
||||
|
||||
using static Testing;
|
||||
|
||||
[TestFixture]
|
||||
public abstract class BaseTestFixture
|
||||
{
|
||||
[SetUp]
|
||||
public async Task TestSetUp()
|
||||
{
|
||||
await ResetState();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Data.Common;
|
||||
using Hutopy.Infrastructure.Data;
|
||||
using Hutopy.Web;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Hutopy.Application.FunctionalTests;
|
||||
|
||||
using static Testing;
|
||||
|
||||
public class CustomWebApplicationFactory(
|
||||
DbConnection connection)
|
||||
: WebApplicationFactory<Program>
|
||||
{
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
builder.ConfigureTestServices(services =>
|
||||
{
|
||||
services
|
||||
.RemoveAll<DbContextOptions<ApplicationDbContext>>()
|
||||
.AddDbContext<ApplicationDbContext>((sp, options) =>
|
||||
{
|
||||
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
||||
options.UseSqlServer(connection);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
global using Ardalis.GuardClauses;
|
||||
global using FluentAssertions;
|
||||
global using Moq;
|
||||
global using NUnit.Framework;
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Data.Common;
|
||||
|
||||
namespace Hutopy.Application.FunctionalTests;
|
||||
|
||||
public interface ITestDatabase
|
||||
{
|
||||
Task InitialiseAsync();
|
||||
|
||||
DbConnection GetConnection();
|
||||
|
||||
Task ResetAsync();
|
||||
|
||||
Task DisposeAsync();
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System.Data.Common;
|
||||
using Hutopy.Infrastructure.Data;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Respawn;
|
||||
|
||||
namespace Hutopy.Application.FunctionalTests;
|
||||
|
||||
public class SqlServerTestDatabase : ITestDatabase
|
||||
{
|
||||
private readonly string _connectionString = null!;
|
||||
private SqlConnection _connection = null!;
|
||||
private Respawner _respawner = null!;
|
||||
|
||||
public SqlServerTestDatabase()
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var connectionString = configuration.GetConnectionString("MssqlConnection");
|
||||
|
||||
Guard.Against.Null(connectionString);
|
||||
|
||||
_connectionString = connectionString;
|
||||
}
|
||||
|
||||
public async Task InitialiseAsync()
|
||||
{
|
||||
_connection = new SqlConnection(_connectionString);
|
||||
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseSqlServer(_connectionString)
|
||||
.Options;
|
||||
|
||||
var context = new ApplicationDbContext(options);
|
||||
|
||||
context.Database.Migrate();
|
||||
|
||||
_respawner = await Respawner.CreateAsync(_connectionString, new RespawnerOptions
|
||||
{
|
||||
TablesToIgnore = new Respawn.Graph.Table[] { "__EFMigrationsHistory" }
|
||||
});
|
||||
}
|
||||
|
||||
public DbConnection GetConnection()
|
||||
{
|
||||
return _connection;
|
||||
}
|
||||
|
||||
public async Task ResetAsync()
|
||||
{
|
||||
await _respawner.ResetAsync(_connectionString);
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
await _connection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Hutopy.Application.FunctionalTests;
|
||||
|
||||
public static class TestDatabaseFactory
|
||||
{
|
||||
public static async Task<ITestDatabase> CreateAsync()
|
||||
{
|
||||
var database = new TestcontainersTestDatabase();
|
||||
|
||||
await database.InitialiseAsync();
|
||||
|
||||
return database;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System.Data.Common;
|
||||
using Hutopy.Infrastructure.Data;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Respawn;
|
||||
using Testcontainers.MsSql;
|
||||
|
||||
namespace Hutopy.Application.FunctionalTests;
|
||||
|
||||
public class TestcontainersTestDatabase : ITestDatabase
|
||||
{
|
||||
private readonly MsSqlContainer _container;
|
||||
private DbConnection _connection = null!;
|
||||
private string _connectionString = null!;
|
||||
private Respawner _respawner = null!;
|
||||
|
||||
public TestcontainersTestDatabase()
|
||||
{
|
||||
_container = new MsSqlBuilder()
|
||||
.WithAutoRemove(true)
|
||||
.Build();
|
||||
}
|
||||
|
||||
public async Task InitialiseAsync()
|
||||
{
|
||||
await _container.StartAsync();
|
||||
|
||||
_connectionString = _container.GetConnectionString();
|
||||
|
||||
_connection = new SqlConnection(_connectionString);
|
||||
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseSqlServer(_connectionString)
|
||||
.Options;
|
||||
|
||||
var context = new ApplicationDbContext(options);
|
||||
|
||||
await context.Database.MigrateAsync();
|
||||
|
||||
_respawner = await Respawner.CreateAsync(_connectionString, new RespawnerOptions
|
||||
{
|
||||
TablesToIgnore = ["__EFMigrationsHistory"]
|
||||
});
|
||||
}
|
||||
|
||||
public DbConnection GetConnection()
|
||||
{
|
||||
return _connection;
|
||||
}
|
||||
|
||||
public async Task ResetAsync()
|
||||
{
|
||||
await _respawner.ResetAsync(_connectionString);
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
await _connection.DisposeAsync();
|
||||
await _container.DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
using Hutopy.Domain.Constants;
|
||||
using Hutopy.Infrastructure.Data;
|
||||
using Hutopy.Infrastructure.Identity;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Hutopy.Application.FunctionalTests;
|
||||
|
||||
[SetUpFixture]
|
||||
public partial class Testing
|
||||
{
|
||||
private static ITestDatabase _database;
|
||||
private static CustomWebApplicationFactory _factory = null!;
|
||||
private static IServiceScopeFactory _scopeFactory = null!;
|
||||
private static Guid _userId;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task RunBeforeAnyTests()
|
||||
{
|
||||
_database = await TestDatabaseFactory.CreateAsync();
|
||||
|
||||
_factory = new CustomWebApplicationFactory(_database.GetConnection());
|
||||
|
||||
_scopeFactory = _factory.Services.GetRequiredService<IServiceScopeFactory>();
|
||||
}
|
||||
|
||||
public static async Task<TResponse> SendAsync<TResponse>(IRequest<TResponse> request)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var mediator = scope.ServiceProvider.GetRequiredService<ISender>();
|
||||
|
||||
return await mediator.Send(request);
|
||||
}
|
||||
|
||||
public static async Task SendAsync(IBaseRequest request)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var mediator = scope.ServiceProvider.GetRequiredService<ISender>();
|
||||
|
||||
await mediator.Send(request);
|
||||
}
|
||||
|
||||
public static Guid GetUserId()
|
||||
{
|
||||
return _userId;
|
||||
}
|
||||
|
||||
public static async Task<Guid> RunAsDefaultUserAsync()
|
||||
{
|
||||
return await RunAsUserAsync(
|
||||
"test@local",
|
||||
"Testing1234!",
|
||||
[]);
|
||||
}
|
||||
|
||||
public static async Task<Guid> RunAsAdministratorAsync()
|
||||
{
|
||||
return await RunAsUserAsync(
|
||||
"administrator@local",
|
||||
"Administrator1234!",
|
||||
[Roles.Administrator]);
|
||||
}
|
||||
|
||||
public static async Task<Guid> RunAsUserAsync(string userName, string password, string[] roles)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<ApplicationUserManager>();
|
||||
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
UserName = userName, Email = userName, Firstname = "FirstName", Lastname = "LastName"
|
||||
};
|
||||
|
||||
var result = await userManager.CreateAsync(user, password);
|
||||
|
||||
if (roles.Any())
|
||||
{
|
||||
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
await roleManager.CreateAsync(new IdentityRole(role));
|
||||
}
|
||||
|
||||
await userManager.AddToRolesAsync(user, roles);
|
||||
}
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_userId = user.Id;
|
||||
|
||||
return _userId;
|
||||
}
|
||||
|
||||
var errors = string.Join(Environment.NewLine, result.ToApplicationResult().Errors);
|
||||
|
||||
throw new Exception($"Unable to create {userName}.{Environment.NewLine}{errors}");
|
||||
}
|
||||
|
||||
public static async Task ResetState()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _database.ResetAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
_userId = Guid.Empty;
|
||||
}
|
||||
|
||||
public static async Task<TEntity?> FindAsync<TEntity>(params object[] keyValues)
|
||||
where TEntity : class
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
return await context.FindAsync<TEntity>(keyValues);
|
||||
}
|
||||
|
||||
public static async Task AddAsync<TEntity>(TEntity entity)
|
||||
where TEntity : class
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
context.Add(entity);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public static async Task<int> CountAsync<TEntity>() where TEntity : class
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
|
||||
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
return await context.Set<TEntity>().CountAsync();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task RunAfterAnyTests()
|
||||
{
|
||||
await _database.DisposeAsync();
|
||||
await _factory.DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"MssqlConnection": "Server=(localdb)\\mssqllocaldb;Database=HutopyTestDb;Trusted_Connection=True;MultipleActiveResultSets=true"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Hutopy.Domain.UnitTests</RootNamespace>
|
||||
<AssemblyName>Hutopy.Domain.UnitTests</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="nunit" />
|
||||
<PackageReference Include="NUnit.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Domain\Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,50 +0,0 @@
|
||||
using Hutopy.Domain.Exceptions;
|
||||
using Hutopy.Domain.ValueObjects;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Hutopy.Domain.UnitTests.ValueObjects;
|
||||
|
||||
public class ColourTests
|
||||
{
|
||||
[Test]
|
||||
public void ShouldReturnCorrectColourCode()
|
||||
{
|
||||
var code = "#FFFFFF";
|
||||
|
||||
var colour = Colour.From(code);
|
||||
|
||||
colour.Code.Should().Be(code);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToStringReturnsCode()
|
||||
{
|
||||
var colour = Colour.White;
|
||||
|
||||
colour.ToString().Should().Be(colour.Code);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldPerformImplicitConversionToColourCodeString()
|
||||
{
|
||||
string code = Colour.White;
|
||||
|
||||
code.Should().Be("#FFFFFF");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldPerformExplicitConversionGivenSupportedColourCode()
|
||||
{
|
||||
var colour = (Colour)"#FFFFFF";
|
||||
|
||||
colour.Should().Be(Colour.White);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldThrowUnsupportedColourExceptionGivenNotSupportedColourCode()
|
||||
{
|
||||
FluentActions.Invoking(() => Colour.From("##FF33CC"))
|
||||
.Should().Throw<UnsupportedColourException>();
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
global using NUnit.Framework;
|
||||
@@ -1,19 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Hutopy.Infrastructure.IntegrationTests</RootNamespace>
|
||||
<AssemblyName>Hutopy.Infrastructure.IntegrationTests</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
<PackageReference Include="NUnit.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user