Removed todos and added User endpoint combined with register. Id changed to guid

This commit is contained in:
Dominic Villemure
2024-04-03 23:57:01 -04:00
parent df8b42bdf1
commit 1f795c53bb
67 changed files with 1750 additions and 1895 deletions

View File

@@ -4,11 +4,9 @@ namespace Hutopy.Application.Common.Interfaces;
public interface IApplicationDbContext public interface IApplicationDbContext
{ {
DbSet<TodoList> TodoLists { get; }
DbSet<TodoItem> TodoItems { get; }
DbSet<FutureCreator> FutureCreators { get; } DbSet<FutureCreator> FutureCreators { get; }
DbSet<User> DomainUsers { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken); Task<int> SaveChangesAsync(CancellationToken cancellationToken);
} }

View File

@@ -1,19 +0,0 @@
using Hutopy.Domain.Entities;
namespace Hutopy.Application.Common.Models;
public class LookupDto
{
public int Id { get; init; }
public string? Title { get; init; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<TodoList, LookupDto>();
CreateMap<TodoItem, LookupDto>();
}
}
}

View File

@@ -3,7 +3,7 @@ using Hutopy.Domain.Entities;
namespace Hutopy.Application.FutureCreators.Commands; namespace Hutopy.Application.FutureCreators.Commands;
public abstract record CreateFutureCreatorCommand : IRequest<int> public record CreateFutureCreatorCommand : IRequest<Guid>
{ {
public required string FirstName { get; init; } public required string FirstName { get; init; }
public required string LastName { get; init; } public required string LastName { get; init; }
@@ -15,9 +15,9 @@ public abstract record CreateFutureCreatorCommand : IRequest<int>
public class CreateFutureCreatorCommandHandler( public class CreateFutureCreatorCommandHandler(
IApplicationDbContext context) IApplicationDbContext context)
: IRequestHandler<CreateFutureCreatorCommand, int> : IRequestHandler<CreateFutureCreatorCommand, Guid>
{ {
public async Task<int> Handle(CreateFutureCreatorCommand request, CancellationToken cancellationToken) public async Task<Guid> Handle(CreateFutureCreatorCommand request, CancellationToken cancellationToken)
{ {
var entity = new FutureCreator var entity = new FutureCreator
{ {

View File

@@ -0,0 +1,20 @@
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FutureCreators.Queries;
public class FutureCreatorListDto
{
public Guid Id { get; init; }
public required string FirstName { get; init; }
public required string LastName { get; init; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<FutureCreator, FutureCreatorListDto>();
}
}
}

View File

@@ -0,0 +1,27 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Mappings;
using Hutopy.Application.Common.Models;
namespace Hutopy.Application.FutureCreators.Queries;
public record GetFutureCreatorListQuery : IRequest<PaginatedList<FutureCreatorListDto>>
{
public int PageNumber { get; init; } = 1;
public int PageSize { get; init; } = 10;
}
public class GetFutureCreatorListQueryHandler(
IApplicationDbContext context,
IMapper mapper)
: IRequestHandler<GetFutureCreatorListQuery, PaginatedList<FutureCreatorListDto>>
{
public async Task<PaginatedList<FutureCreatorListDto>> Handle(GetFutureCreatorListQuery request, CancellationToken cancellationToken)
{
Console.WriteLine(request);
return await context.FutureCreators
.OrderBy(x => x.FirstName)
.ProjectTo<FutureCreatorListDto>(mapper.ConfigurationProvider)
.PaginatedListAsync(request.PageNumber, request.PageSize);
}
}

View File

@@ -1,35 +0,0 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Entities;
using Hutopy.Domain.Events;
namespace Hutopy.Application.TodoItems.Commands.CreateTodoItem;
public record CreateTodoItemCommand : IRequest<int>
{
public int ListId { get; init; }
public string? Title { get; init; }
}
public class CreateTodoItemCommandHandler(
IApplicationDbContext context)
: IRequestHandler<CreateTodoItemCommand, int>
{
public async Task<int> Handle(CreateTodoItemCommand request, CancellationToken cancellationToken)
{
var entity = new TodoItem
{
ListId = request.ListId,
Title = request.Title,
Done = false
};
entity.AddDomainEvent(new TodoItemCreatedEvent(entity));
context.TodoItems.Add(entity);
await context.SaveChangesAsync(cancellationToken);
return entity.Id;
}
}

View File

@@ -1,11 +0,0 @@
namespace Hutopy.Application.TodoItems.Commands.CreateTodoItem;
public class CreateTodoItemCommandValidator : AbstractValidator<CreateTodoItemCommand>
{
public CreateTodoItemCommandValidator()
{
RuleFor(v => v.Title)
.MaximumLength(200)
.NotEmpty();
}
}

View File

@@ -1,30 +0,0 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Events;
using Hutopy.Application.Common.Security;
using Hutopy.Domain.Constants;
namespace Hutopy.Application.TodoItems.Commands.DeleteTodoItem;
[Authorize(Roles = Roles.Administrator)]
[Authorize(Policy = Policies.CanDelete)]
public record DeleteTodoItemCommand(int Id) : IRequest;
public class DeleteTodoItemCommandHandler(
IApplicationDbContext context)
: IRequestHandler<DeleteTodoItemCommand>
{
public async Task Handle(DeleteTodoItemCommand request, CancellationToken cancellationToken)
{
var entity = await context.TodoItems
.FindAsync(new object[] { request.Id }, cancellationToken);
Guard.Against.NotFound(request.Id, entity);
context.TodoItems.Remove(entity);
entity.AddDomainEvent(new TodoItemDeletedEvent(entity));
await context.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -1,30 +0,0 @@
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.TodoItems.Commands.UpdateTodoItem;
public record UpdateTodoItemCommand : IRequest
{
public int Id { get; init; }
public string? Title { get; init; }
public bool Done { get; init; }
}
public class UpdateTodoItemCommandHandler(
IApplicationDbContext context)
: IRequestHandler<UpdateTodoItemCommand>
{
public async Task Handle(UpdateTodoItemCommand request, CancellationToken cancellationToken)
{
var entity = await context.TodoItems
.FindAsync(new object[] { request.Id }, cancellationToken);
Guard.Against.NotFound(request.Id, entity);
entity.Title = request.Title;
entity.Done = request.Done;
await context.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -1,11 +0,0 @@
namespace Hutopy.Application.TodoItems.Commands.UpdateTodoItem;
public class UpdateTodoItemCommandValidator : AbstractValidator<UpdateTodoItemCommand>
{
public UpdateTodoItemCommandValidator()
{
RuleFor(v => v.Title)
.MaximumLength(200)
.NotEmpty();
}
}

View File

@@ -1,34 +0,0 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Enums;
namespace Hutopy.Application.TodoItems.Commands.UpdateTodoItemDetail;
public record UpdateTodoItemDetailCommand : IRequest
{
public int Id { get; init; }
public int ListId { get; init; }
public PriorityLevel Priority { get; init; }
public string? Note { get; init; }
}
public class UpdateTodoItemDetailCommandHandler(
IApplicationDbContext context)
: IRequestHandler<UpdateTodoItemDetailCommand>
{
public async Task Handle(UpdateTodoItemDetailCommand request, CancellationToken cancellationToken)
{
var entity = await context.TodoItems
.FindAsync(new object[] { request.Id }, cancellationToken);
Guard.Against.NotFound(request.Id, entity);
entity.ListId = request.ListId;
entity.Priority = request.Priority;
entity.Note = request.Note;
await context.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -1,16 +0,0 @@
using Hutopy.Domain.Events;
using Microsoft.Extensions.Logging;
namespace Hutopy.Application.TodoItems.EventHandlers;
public class TodoItemCompletedEventHandler(
ILogger<TodoItemCompletedEventHandler> logger)
: INotificationHandler<TodoItemCompletedEvent>
{
public Task Handle(TodoItemCompletedEvent notification, CancellationToken cancellationToken)
{
logger.LogInformation("Hutopy Domain Event: {DomainEvent}", notification.GetType().Name);
return Task.CompletedTask;
}
}

View File

@@ -1,16 +0,0 @@
using Hutopy.Domain.Events;
using Microsoft.Extensions.Logging;
namespace Hutopy.Application.TodoItems.EventHandlers;
public class TodoItemCreatedEventHandler(
ILogger<TodoItemCreatedEventHandler> logger)
: INotificationHandler<TodoItemCreatedEvent>
{
public Task Handle(TodoItemCreatedEvent notification, CancellationToken cancellationToken)
{
logger.LogInformation("Hutopy Domain Event: {DomainEvent}", notification.GetType().Name);
return Task.CompletedTask;
}
}

View File

@@ -1,28 +0,0 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Mappings;
using Hutopy.Application.Common.Models;
namespace Hutopy.Application.TodoItems.Queries.GetTodoItemsWithPagination;
public abstract record GetTodoItemsWithPaginationQuery : IRequest<PaginatedList<TodoItemBriefDto>>
{
public int ListId { get; init; }
public int PageNumber { get; init; } = 1;
public int PageSize { get; init; } = 10;
}
public class GetTodoItemsWithPaginationQueryHandler(
IApplicationDbContext context,
IMapper mapper)
: IRequestHandler<GetTodoItemsWithPaginationQuery, PaginatedList<TodoItemBriefDto>>
{
public async Task<PaginatedList<TodoItemBriefDto>> Handle(GetTodoItemsWithPaginationQuery request, CancellationToken cancellationToken)
{
Console.WriteLine(request);
return await context.TodoItems
.Where(x => x.ListId == request.ListId)
.OrderBy(x => x.Title)
.ProjectTo<TodoItemBriefDto>(mapper.ConfigurationProvider)
.PaginatedListAsync(request.PageNumber, request.PageSize);
}
}

View File

@@ -1,16 +0,0 @@
namespace Hutopy.Application.TodoItems.Queries.GetTodoItemsWithPagination;
public class GetTodoItemsWithPaginationQueryValidator : AbstractValidator<GetTodoItemsWithPaginationQuery>
{
public GetTodoItemsWithPaginationQueryValidator()
{
RuleFor(x => x.ListId)
.NotEmpty().WithMessage("ListId is required.");
RuleFor(x => x.PageNumber)
.GreaterThanOrEqualTo(1).WithMessage("PageNumber at least greater than or equal to 1.");
RuleFor(x => x.PageSize)
.GreaterThanOrEqualTo(1).WithMessage("PageSize at least greater than or equal to 1.");
}
}

View File

@@ -1,22 +0,0 @@
using Hutopy.Domain.Entities;
namespace Hutopy.Application.TodoItems.Queries.GetTodoItemsWithPagination;
public class TodoItemBriefDto
{
public int Id { get; init; }
public int ListId { get; init; }
public string? Title { get; init; }
public bool Done { get; init; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<TodoItem, TodoItemBriefDto>();
}
}
}

View File

@@ -1,25 +0,0 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.TodoLists.Commands.CreateTodoList;
public record CreateTodoListCommand : IRequest<int>
{
public string? Title { get; init; }
}
public class CreateTodoListCommandHandler(
IApplicationDbContext context)
: IRequestHandler<CreateTodoListCommand, int>
{
public async Task<int> Handle(CreateTodoListCommand request, CancellationToken cancellationToken)
{
var entity = new TodoList { Title = request.Title };
context.TodoLists.Add(entity);
await context.SaveChangesAsync(cancellationToken);
return entity.Id;
}
}

View File

@@ -1,26 +0,0 @@
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.TodoLists.Commands.CreateTodoList;
public class CreateTodoListCommandValidator : AbstractValidator<CreateTodoListCommand>
{
private readonly IApplicationDbContext _context;
public CreateTodoListCommandValidator(IApplicationDbContext context)
{
_context = context;
RuleFor(v => v.Title)
.NotEmpty()
.MaximumLength(200)
.MustAsync(BeUniqueTitle)
.WithMessage("'{PropertyName}' must be unique.")
.WithErrorCode("Unique");
}
public async Task<bool> BeUniqueTitle(string title, CancellationToken cancellationToken)
{
return await _context.TodoLists
.AllAsync(l => l.Title != title, cancellationToken);
}
}

View File

@@ -1,23 +0,0 @@
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.TodoLists.Commands.DeleteTodoList;
public record DeleteTodoListCommand(int Id) : IRequest;
public class DeleteTodoListCommandHandler(
IApplicationDbContext context)
: IRequestHandler<DeleteTodoListCommand>
{
public async Task Handle(DeleteTodoListCommand request, CancellationToken cancellationToken)
{
var entity = await context.TodoLists
.Where(l => l.Id == request.Id)
.SingleOrDefaultAsync(cancellationToken);
Guard.Against.NotFound(request.Id, entity);
context.TodoLists.Remove(entity);
await context.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -1,21 +0,0 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Security;
using Hutopy.Domain.Constants;
namespace Hutopy.Application.TodoLists.Commands.PurgeTodoLists;
[Authorize(Roles = Roles.Administrator)]
[Authorize(Policy = Policies.CanPurge)]
public record PurgeTodoListsCommand : IRequest;
public class PurgeTodoListsCommandHandler(
IApplicationDbContext context)
: IRequestHandler<PurgeTodoListsCommand>
{
public async Task Handle(PurgeTodoListsCommand request, CancellationToken cancellationToken)
{
context.TodoLists.RemoveRange(context.TodoLists);
await context.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -1,28 +0,0 @@
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.TodoLists.Commands.UpdateTodoList;
public record UpdateTodoListCommand : IRequest
{
public int Id { get; init; }
public string? Title { get; init; }
}
public class UpdateTodoListCommandHandler(
IApplicationDbContext context)
: IRequestHandler<UpdateTodoListCommand>
{
public async Task Handle(UpdateTodoListCommand request, CancellationToken cancellationToken)
{
var entity = await context.TodoLists
.FindAsync(new object[] { request.Id }, cancellationToken);
Guard.Against.NotFound(request.Id, entity);
entity.Title = request.Title;
await context.SaveChangesAsync(cancellationToken);
}
}

View File

@@ -1,27 +0,0 @@
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.TodoLists.Commands.UpdateTodoList;
public class UpdateTodoListCommandValidator : AbstractValidator<UpdateTodoListCommand>
{
private readonly IApplicationDbContext _context;
public UpdateTodoListCommandValidator(IApplicationDbContext context)
{
_context = context;
RuleFor(v => v.Title)
.NotEmpty()
.MaximumLength(200)
.MustAsync(BeUniqueTitle)
.WithMessage("'{PropertyName}' must be unique.")
.WithErrorCode("Unique");
}
private async Task<bool> BeUniqueTitle(UpdateTodoListCommand model, string title, CancellationToken cancellationToken)
{
return await _context.TodoLists
.Where(l => l.Id != model.Id)
.AllAsync(l => l.Title != title, cancellationToken);
}
}

View File

@@ -1,32 +0,0 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Models;
using Hutopy.Application.Common.Security;
using Hutopy.Domain.Enums;
namespace Hutopy.Application.TodoLists.Queries.GetTodos;
[Authorize]
public record GetTodosQuery : IRequest<TodosVm>;
public class GetTodosQueryHandler(
IApplicationDbContext context,
IMapper mapper)
: IRequestHandler<GetTodosQuery, TodosVm>
{
public async Task<TodosVm> Handle(GetTodosQuery request, CancellationToken cancellationToken)
{
return new TodosVm
{
PriorityLevels = Enum.GetValues(typeof(PriorityLevel))
.Cast<PriorityLevel>()
.Select(p => new LookupDto { Id = (int)p, Title = p.ToString() })
.ToList(),
Lists = await context.TodoLists
.AsNoTracking()
.ProjectTo<TodoListDto>(mapper.ConfigurationProvider)
.OrderBy(t => t.Title)
.ToListAsync(cancellationToken)
};
}
}

View File

@@ -1,27 +0,0 @@
using Hutopy.Domain.Entities;
namespace Hutopy.Application.TodoLists.Queries.GetTodos;
public class TodoItemDto
{
public int Id { get; init; }
public int ListId { get; init; }
public string? Title { get; init; }
public bool Done { get; init; }
public int Priority { get; init; }
public string? Note { get; init; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<TodoItem, TodoItemDto>().ForMember(d => d.Priority,
opt => opt.MapFrom(s => (int)s.Priority));
}
}
}

View File

@@ -1,22 +0,0 @@
using Hutopy.Domain.Entities;
namespace Hutopy.Application.TodoLists.Queries.GetTodos;
public class TodoListDto
{
public int Id { get; init; }
public string? Title { get; init; }
public string? Colour { get; init; }
public IReadOnlyCollection<TodoItemDto> Items { get; init; } = Array.Empty<TodoItemDto>();
private class Mapping : Profile
{
public Mapping()
{
CreateMap<TodoList, TodoListDto>();
}
}
}

View File

@@ -1,10 +0,0 @@
using Hutopy.Application.Common.Models;
namespace Hutopy.Application.TodoLists.Queries.GetTodos;
public class TodosVm
{
public IReadOnlyCollection<LookupDto> PriorityLevels { get; init; } = Array.Empty<LookupDto>();
public IReadOnlyCollection<TodoListDto> Lists { get; init; } = Array.Empty<TodoListDto>();
}

View File

@@ -0,0 +1,39 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.Users.Commands;
public record CreateUserCommand : IRequest<Guid>
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required string EmailAddress { get; init; }
public required string UserName { get; init; }
public required string Password { get; init; }
}
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, Guid>
{
private readonly IApplicationDbContext _context;
public CreateUserCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public async Task<Guid> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var entity = new User
{
FirstName = request.FirstName,
LastName = request.LastName,
EmailAddress = request.EmailAddress,
UserName = request.UserName,
};
_context.DomainUsers.Add(entity);
await _context.SaveChangesAsync(cancellationToken);
return entity.Id;
}
}

View File

@@ -6,7 +6,7 @@ public abstract class BaseEntity
{ {
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types. // 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 // Using non-generic integer types for simplicity
public int Id { get; set; } public Guid Id { get; set; }
private readonly List<BaseEvent> _domainEvents = []; private readonly List<BaseEvent> _domainEvents = [];

View File

@@ -1,31 +0,0 @@
namespace Hutopy.Domain.Entities;
public class TodoItem : BaseAuditableEntity
{
public int ListId { get; set; }
public string? Title { get; set; }
public string? Note { get; set; }
public PriorityLevel Priority { get; set; }
public DateTime? Reminder { get; set; }
private bool _done;
public bool Done
{
get => _done;
set
{
if (value && !_done)
{
AddDomainEvent(new TodoItemCompletedEvent(this));
}
_done = value;
}
}
public TodoList List { get; set; } = null!;
}

View File

@@ -1,10 +0,0 @@
namespace Hutopy.Domain.Entities;
public class TodoList : BaseAuditableEntity
{
public string? Title { get; set; }
public Colour Colour { get; set; } = Colour.White;
public IList<TodoItem> Items { get; private set; } = new List<TodoItem>();
}

View File

@@ -0,0 +1,10 @@
namespace Hutopy.Domain.Entities;
public class User : BaseAuditableEntity
{
public Guid IdentityUserId { get; set; }
public required string FirstName { get; set; }
public required string LastName { get; set; }
public required string UserName { get; set; }
public required string EmailAddress { get; set; }
}

View File

@@ -1,8 +0,0 @@
namespace Hutopy.Domain.Events;
public class TodoItemCompletedEvent(
TodoItem item)
: BaseEvent
{
public TodoItem Item { get; } = item;
}

View File

@@ -1,8 +0,0 @@
namespace Hutopy.Domain.Events;
public class TodoItemCreatedEvent(
TodoItem item)
: BaseEvent
{
public TodoItem Item { get; } = item;
}

View File

@@ -1,8 +0,0 @@
namespace Hutopy.Domain.Events;
public class TodoItemDeletedEvent(
TodoItem item)
: BaseEvent
{
public TodoItem Item { get; } = item;
}

View File

@@ -1,6 +1,5 @@
global using Hutopy.Domain.Common; global using Hutopy.Domain.Common;
global using Hutopy.Domain.Entities; global using Hutopy.Domain.Entities;
global using Hutopy.Domain.Enums; global using Hutopy.Domain.Enums;
global using Hutopy.Domain.Events;
global using Hutopy.Domain.Exceptions; global using Hutopy.Domain.Exceptions;
global using Hutopy.Domain.ValueObjects; global using Hutopy.Domain.ValueObjects;

View File

@@ -0,0 +1,7 @@
namespace Hutopy.Domain.Interfaces;
public interface IUserService
{
Task CreateUserAsync(string email, string userName, string password);
}

View File

@@ -11,12 +11,10 @@ public class ApplicationDbContext(
DbContextOptions<ApplicationDbContext> options) DbContextOptions<ApplicationDbContext> options)
: IdentityDbContext<ApplicationUser>(options), IApplicationDbContext : IdentityDbContext<ApplicationUser>(options), IApplicationDbContext
{ {
public DbSet<TodoList> TodoLists => Set<TodoList>();
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
public DbSet<FutureCreator> FutureCreators => Set<FutureCreator>(); public DbSet<FutureCreator> FutureCreators => Set<FutureCreator>();
public DbSet<User> DomainUsers => Set<User>();
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);

View File

@@ -73,27 +73,8 @@ public class ApplicationDbContextInitializer(
await userManager.CreateAsync(administrator, "Administrator1!"); await userManager.CreateAsync(administrator, "Administrator1!");
if (!string.IsNullOrWhiteSpace(administratorRole.Name)) if (!string.IsNullOrWhiteSpace(administratorRole.Name))
{ {
await userManager.AddToRolesAsync(administrator, new [] { administratorRole.Name }); await userManager.AddToRolesAsync(administrator, new[] { administratorRole.Name });
} }
} }
// Default data
// Seed, if necessary
if (!context.TodoLists.Any())
{
context.TodoLists.Add(new TodoList
{
Title = "Todo List",
Items =
{
new TodoItem { Title = "Make a todo list 📃" },
new TodoItem { Title = "Check off the first item ✅" },
new TodoItem { Title = "Realise you've already done two things on the list! 🤯"},
new TodoItem { Title = "Reward yourself with a nice, long nap 🏆" },
}
});
await context.SaveChangesAsync();
}
} }
} }

View File

@@ -1,15 +0,0 @@
using Hutopy.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Hutopy.Infrastructure.Data.Configurations;
public class TodoItemConfiguration : IEntityTypeConfiguration<TodoItem>
{
public void Configure(EntityTypeBuilder<TodoItem> builder)
{
builder.Property(t => t.Title)
.HasMaxLength(200)
.IsRequired();
}
}

View File

@@ -1,18 +0,0 @@
using Hutopy.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Hutopy.Infrastructure.Data.Configurations;
public class TodoListConfiguration : IEntityTypeConfiguration<TodoList>
{
public void Configure(EntityTypeBuilder<TodoList> builder)
{
builder.Property(t => t.Title)
.HasMaxLength(200)
.IsRequired();
builder
.OwnsOne(b => b.Colour);
}
}

View File

@@ -0,0 +1,492 @@
// <auto-generated />
using System;
using Hutopy.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Hutopy.Infrastructure.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240404013754_AddDomainUser")]
partial class AddDomainUser
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ReasonToJoin")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SocialNetworkAccount")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("FutureCreators");
});
modelBuilder.Entity("Hutopy.Domain.Entities.TodoItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("Done")
.HasColumnType("bit");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<int>("ListId")
.HasColumnType("int");
b.Property<string>("Note")
.HasColumnType("nvarchar(max)");
b.Property<int>("Priority")
.HasColumnType("int");
b.Property<DateTime?>("Reminder")
.HasColumnType("datetime2");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.HasIndex("ListId");
b.ToTable("TodoItems");
});
modelBuilder.Entity("Hutopy.Domain.Entities.TodoList", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.ToTable("TodoLists");
});
modelBuilder.Entity("Hutopy.Domain.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<Guid>("IdentityUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("DomainUsers");
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Hutopy.Domain.Entities.TodoItem", b =>
{
b.HasOne("Hutopy.Domain.Entities.TodoList", "List")
.WithMany("Items")
.HasForeignKey("ListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("List");
});
modelBuilder.Entity("Hutopy.Domain.Entities.TodoList", b =>
{
b.OwnsOne("Hutopy.Domain.ValueObjects.Colour", "Colour", b1 =>
{
b1.Property<int>("TodoListId")
.HasColumnType("int");
b1.Property<string>("Code")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.HasKey("TodoListId");
b1.ToTable("TodoLists");
b1.WithOwner()
.HasForeignKey("TodoListId");
});
b.Navigation("Colour")
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Hutopy.Domain.Entities.TodoList", b =>
{
b.Navigation("Items");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Data.Migrations
{
/// <inheritdoc />
public partial class AddDomainUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "DomainUsers",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
IdentityUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
FirstName = table.Column<string>(type: "nvarchar(max)", nullable: false),
LastName = table.Column<string>(type: "nvarchar(max)", nullable: false),
UserName = table.Column<string>(type: "nvarchar(max)", nullable: false),
EmailAddress = table.Column<string>(type: "nvarchar(max)", nullable: false),
Password = table.Column<string>(type: "nvarchar(max)", nullable: false),
Created = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
LastModified = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_DomainUsers", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "DomainUsers");
}
}
}

View File

@@ -0,0 +1,372 @@
// <auto-generated />
using System;
using Hutopy.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Hutopy.Infrastructure.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240404023737_ChangeIdsToGuidsAndRemoveTodos")]
partial class ChangeIdsToGuidsAndRemoveTodos
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ReasonToJoin")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SocialNetworkAccount")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("FutureCreators");
});
modelBuilder.Entity("Hutopy.Domain.Entities.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<Guid>("IdentityUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("DomainUsers");
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,149 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Data.Migrations
{
/// <inheritdoc />
public partial class ChangeIdsToGuidsAndRemoveTodos : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TodoItems");
migrationBuilder.DropTable(
name: "TodoLists");
migrationBuilder.DropPrimaryKey(
name: "PK_FuturCreators",
table: "FutureCreators");
migrationBuilder.DropPrimaryKey(
name: "PK_DomainUsers",
table: "DomainUsers");
migrationBuilder.DropColumn(
name: "Id",
table: "FutureCreators");
migrationBuilder.DropColumn(
name: "Id",
table: "DomainUsers");
migrationBuilder.AddColumn<Guid>(
name: "Id",
table: "FutureCreators",
type: "uniqueidentifier",
nullable: false,
defaultValueSql: "NEWID()"
);
migrationBuilder.AddColumn<Guid>(
name: "Id",
table: "DomainUsers",
type: "uniqueidentifier",
nullable: false,
defaultValueSql: "NEWID()"
);
migrationBuilder.AddPrimaryKey(
name: "PK_FutureCreators",
table: "FutureCreators",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK_DomainUsers",
table: "DomainUsers",
column: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_FutureCreators",
table: "FutureCreators");
migrationBuilder.RenameTable(
name: "FutureCreators",
newName: "FutureCreator");
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "DomainUsers",
type: "int",
nullable: false,
oldClrType: typeof(Guid),
oldType: "uniqueidentifier")
.Annotation("SqlServer:Identity", "1, 1");
migrationBuilder.AlterColumn<int>(
name: "Id",
table: "FutureCreator",
type: "int",
nullable: false,
oldClrType: typeof(Guid),
oldType: "uniqueidentifier")
.Annotation("SqlServer:Identity", "1, 1");
migrationBuilder.AddPrimaryKey(
name: "PK_FutureCreator",
table: "FutureCreator",
column: "Id");
migrationBuilder.CreateTable(
name: "TodoLists",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Created = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
LastModified = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
Title = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Colour_Code = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoLists", x => x.Id);
});
migrationBuilder.CreateTable(
name: "TodoItems",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ListId = table.Column<int>(type: "int", nullable: false),
Created = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
Done = table.Column<bool>(type: "bit", nullable: false),
LastModified = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
Note = table.Column<string>(type: "nvarchar(max)", nullable: true),
Priority = table.Column<int>(type: "int", nullable: false),
Reminder = table.Column<DateTime>(type: "datetime2", nullable: true),
Title = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoItems", x => x.Id);
table.ForeignKey(
name: "FK_TodoItems_TodoLists_ListId",
column: x => x.ListId,
principalTable: "TodoLists",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_TodoItems_ListId",
table: "TodoItems",
column: "ListId");
}
}
}

View File

@@ -0,0 +1,368 @@
// <auto-generated />
using System;
using Hutopy.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Hutopy.Infrastructure.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240404030232_RemovePasswordFromDomainUser")]
partial class RemovePasswordFromDomainUser
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ReasonToJoin")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SocialNetworkAccount")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("FutureCreators");
});
modelBuilder.Entity("Hutopy.Domain.Entities.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<Guid>("IdentityUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("DomainUsers");
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Data.Migrations
{
/// <inheritdoc />
public partial class RemovePasswordFromDomainUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Password",
table: "DomainUsers");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Password",
table: "DomainUsers",
type: "nvarchar(max)",
nullable: false,
defaultValue: "");
}
}
}

View File

@@ -24,11 +24,9 @@ namespace Hutopy.Infrastructure.Data.Migrations
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b => modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
{ {
b.Property<int>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("uniqueidentifier");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Created") b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset"); .HasColumnType("datetimeoffset");
@@ -68,16 +66,14 @@ namespace Hutopy.Infrastructure.Data.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("FutureCreator"); b.ToTable("FutureCreators");
}); });
modelBuilder.Entity("Hutopy.Domain.Entities.TodoItem", b => modelBuilder.Entity("Hutopy.Domain.Entities.User", b =>
{ {
b.Property<int>("Id") b.Property<Guid>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("int"); .HasColumnType("uniqueidentifier");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Created") b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset"); .HasColumnType("datetimeoffset");
@@ -85,8 +81,16 @@ namespace Hutopy.Infrastructure.Data.Migrations
b.Property<string>("CreatedBy") b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
b.Property<bool>("Done") b.Property<string>("EmailAddress")
.HasColumnType("bit"); .IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<Guid>("IdentityUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("LastModified") b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset"); .HasColumnType("datetimeoffset");
@@ -94,58 +98,17 @@ namespace Hutopy.Infrastructure.Data.Migrations
b.Property<string>("LastModifiedBy") b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
b.Property<int>("ListId") b.Property<string>("LastName")
.HasColumnType("int"); .IsRequired()
b.Property<string>("Note")
.HasColumnType("nvarchar(max)"); .HasColumnType("nvarchar(max)");
b.Property<int>("Priority") b.Property<string>("UserName")
.HasColumnType("int");
b.Property<DateTime?>("Reminder")
.HasColumnType("datetime2");
b.Property<string>("Title")
.IsRequired() .IsRequired()
.HasMaxLength(200) .HasColumnType("nvarchar(max)");
.HasColumnType("nvarchar(200)");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("ListId"); b.ToTable("DomainUsers");
b.ToTable("TodoItems");
});
modelBuilder.Entity("Hutopy.Domain.Entities.TodoList", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.HasKey("Id");
b.ToTable("TodoLists");
}); });
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b => modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
@@ -346,40 +309,6 @@ namespace Hutopy.Infrastructure.Data.Migrations
b.ToTable("AspNetUserTokens", (string)null); b.ToTable("AspNetUserTokens", (string)null);
}); });
modelBuilder.Entity("Hutopy.Domain.Entities.TodoItem", b =>
{
b.HasOne("Hutopy.Domain.Entities.TodoList", "List")
.WithMany("Items")
.HasForeignKey("ListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("List");
});
modelBuilder.Entity("Hutopy.Domain.Entities.TodoList", b =>
{
b.OwnsOne("Hutopy.Domain.ValueObjects.Colour", "Colour", b1 =>
{
b1.Property<int>("TodoListId")
.HasColumnType("int");
b1.Property<string>("Code")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.HasKey("TodoListId");
b1.ToTable("TodoLists");
b1.WithOwner()
.HasForeignKey("TodoListId");
});
b.Navigation("Colour")
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{ {
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
@@ -430,11 +359,6 @@ namespace Hutopy.Infrastructure.Data.Migrations
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
}); });
modelBuilder.Entity("Hutopy.Domain.Entities.TodoList", b =>
{
b.Navigation("Items");
});
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }

View File

@@ -1,8 +1,10 @@
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Constants; using Hutopy.Domain.Constants;
using Hutopy.Domain.Interfaces;
using Hutopy.Infrastructure.Data; using Hutopy.Infrastructure.Data;
using Hutopy.Infrastructure.Data.Interceptors; using Hutopy.Infrastructure.Data.Interceptors;
using Hutopy.Infrastructure.Identity; using Hutopy.Infrastructure.Identity;
using Hutopy.Infrastructure.Services;
using Hutopy.Infrastructure.Stripe; using Hutopy.Infrastructure.Stripe;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -41,6 +43,8 @@ public static class DependencyInjection
.AddBearerToken(IdentityConstants.BearerScheme); .AddBearerToken(IdentityConstants.BearerScheme);
services.AddAuthorizationBuilder(); services.AddAuthorizationBuilder();
services.AddScoped<IUserService, UserService>();
// Might need to change and use AddIdentity<User, Role>() when we need to integrate connection via third party ( facebook, google ) // Might need to change and use AddIdentity<User, Role>() when we need to integrate connection via third party ( facebook, google )
services services

View File

@@ -0,0 +1,24 @@
using Hutopy.Domain.Interfaces;
using Hutopy.Infrastructure.Identity;
using Microsoft.AspNetCore.Identity;
namespace Hutopy.Infrastructure.Services;
public class UserService(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager) : IUserService
{
private readonly UserManager<ApplicationUser> _userManager = userManager;
private readonly SignInManager<ApplicationUser> _signInManager = signInManager;
public async Task CreateUserAsync(string email, string userName, string password)
{
var applicationUser = new ApplicationUser
{
UserName = userName,
Email = email,
};
await _userManager.CreateAsync(applicationUser, password);
}
}

View File

@@ -1,6 +1,8 @@
using Azure.Identity; using Azure.Identity;
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Interfaces;
using Hutopy.Infrastructure.Data; using Hutopy.Infrastructure.Data;
using Hutopy.Infrastructure.Services;
using Hutopy.Web.Services; using Hutopy.Web.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NSwag; using NSwag;
@@ -15,6 +17,8 @@ public static class DependencyInjection
services.AddDatabaseDeveloperPageExceptionFilter(); services.AddDatabaseDeveloperPageExceptionFilter();
services.AddScoped<IUser, CurrentUser>(); services.AddScoped<IUser, CurrentUser>();
services.AddScoped<IUserService, UserService>();
services.AddHttpContextAccessor(); services.AddHttpContextAccessor();

View File

@@ -1,4 +1,6 @@
using Hutopy.Application.FutureCreators.Commands; using Hutopy.Application.Common.Models;
using Hutopy.Application.FutureCreators.Commands;
using Hutopy.Application.FutureCreators.Queries;
namespace Hutopy.Web.Endpoints; namespace Hutopy.Web.Endpoints;
@@ -7,11 +9,17 @@ public class JoinUs : EndpointGroupBase
public override void Map(WebApplication app) public override void Map(WebApplication app)
{ {
app.MapGroup(this) app.MapGroup(this)
.MapGet(GetFutureCreators)
.MapPost(CreateFutureCreator); .MapPost(CreateFutureCreator);
} }
private static Task<int> CreateFutureCreator(ISender sender, CreateFutureCreatorCommand command) private static Task<Guid> CreateFutureCreator(ISender sender, CreateFutureCreatorCommand command)
{ {
return sender.Send(command); return sender.Send(command);
} }
private static Task<PaginatedList<FutureCreatorListDto>> GetFutureCreators(ISender sender, [AsParameters] GetFutureCreatorListQuery query)
{
return sender.Send(query);
}
} }

View File

@@ -1,52 +0,0 @@
using Hutopy.Application.Common.Models;
using Hutopy.Application.TodoItems.Commands.CreateTodoItem;
using Hutopy.Application.TodoItems.Commands.DeleteTodoItem;
using Hutopy.Application.TodoItems.Commands.UpdateTodoItem;
using Hutopy.Application.TodoItems.Commands.UpdateTodoItemDetail;
using Hutopy.Application.TodoItems.Queries.GetTodoItemsWithPagination;
namespace Hutopy.Web.Endpoints;
public class TodoItems : EndpointGroupBase
{
public override void Map(WebApplication app)
{
app.MapGroup(this)
.RequireAuthorization()
.MapGet(GetTodoItemsWithPagination)
.MapPost(CreateTodoItem)
.MapPut(UpdateTodoItem, "{id}")
.MapPut(UpdateTodoItemDetail, "UpdateDetail/{id}")
.MapDelete(DeleteTodoItem, "{id}");
}
private static Task<PaginatedList<TodoItemBriefDto>> GetTodoItemsWithPagination(ISender sender, [AsParameters] GetTodoItemsWithPaginationQuery query)
{
return sender.Send(query);
}
private static Task<int> CreateTodoItem(ISender sender, CreateTodoItemCommand command)
{
return sender.Send(command);
}
private static async Task<IResult> UpdateTodoItem(ISender sender, int id, UpdateTodoItemCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
private static async Task<IResult> UpdateTodoItemDetail(ISender sender, int id, UpdateTodoItemDetailCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
private static async Task<IResult> DeleteTodoItem(ISender sender, int id)
{
await sender.Send(new DeleteTodoItemCommand(id));
return Results.NoContent();
}
}

View File

@@ -1,42 +0,0 @@
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Application.TodoLists.Commands.DeleteTodoList;
using Hutopy.Application.TodoLists.Commands.UpdateTodoList;
using Hutopy.Application.TodoLists.Queries.GetTodos;
namespace Hutopy.Web.Endpoints;
public class TodoLists : EndpointGroupBase
{
public override void Map(WebApplication app)
{
app.MapGroup(this)
.RequireAuthorization()
.MapGet(GetTodoLists)
.MapPost(CreateTodoList)
.MapPut(UpdateTodoList, "{id}")
.MapDelete(DeleteTodoList, "{id}");
}
private static Task<TodosVm> GetTodoLists(ISender sender)
{
return sender.Send(new GetTodosQuery());
}
private static Task<int> CreateTodoList(ISender sender, CreateTodoListCommand command)
{
return sender.Send(command);
}
private static async Task<IResult> UpdateTodoList(ISender sender, int id, UpdateTodoListCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
private static async Task<IResult> DeleteTodoList(ISender sender, int id)
{
await sender.Send(new DeleteTodoListCommand(id));
return Results.NoContent();
}
}

View File

@@ -1,4 +1,6 @@
using Hutopy.Infrastructure.Identity; using Hutopy.Application.Users.Commands;
using Hutopy.Domain.Interfaces;
using Hutopy.Infrastructure.Identity;
namespace Hutopy.Web.Endpoints; namespace Hutopy.Web.Endpoints;
@@ -7,6 +9,13 @@ public class Users : EndpointGroupBase
public override void Map(WebApplication app) public override void Map(WebApplication app)
{ {
app.MapGroup(this) app.MapGroup(this)
.MapPost(CreateUser)
.MapIdentityApi<ApplicationUser>(); .MapIdentityApi<ApplicationUser>();
} }
public async Task<Guid> CreateUser(ISender sender, CreateUserCommand command, IUserService userService)
{
await userService.CreateUserAsync(command.EmailAddress, command.UserName, command.Password);
return await sender.Send(command);
}
} }

View File

@@ -1,6 +1,8 @@
using Hutopy.Application; using Hutopy.Application;
using Hutopy.Domain.Interfaces;
using Hutopy.Infrastructure; using Hutopy.Infrastructure;
using Hutopy.Infrastructure.Data; using Hutopy.Infrastructure.Data;
using Hutopy.Infrastructure.Services;
using Hutopy.Web; using Hutopy.Web;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -22,6 +24,7 @@ builder.Services.AddApplicationServices();
builder.Services.AddInfrastructureServices(builder.Configuration); builder.Services.AddInfrastructureServices(builder.Configuration);
builder.Services.AddWebServices(); builder.Services.AddWebServices();
builder.Services.AddScoped<IUserService, UserService>();
var app = builder.Build(); var app = builder.Build();

View File

@@ -7,6 +7,46 @@
}, },
"paths": { "paths": {
"/api/JoinUs": { "/api/JoinUs": {
"get": {
"tags": [
"JoinUs"
],
"operationId": "GetFutureCreators",
"parameters": [
{
"name": "PageNumber",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 1
},
{
"name": "PageSize",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 2
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PaginatedListOfFutureCreatorListDto"
}
}
}
}
}
},
"post": { "post": {
"tags": [ "tags": [
"JoinUs" "JoinUs"
@@ -30,8 +70,8 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"type": "integer", "type": "string",
"format": "int32" "format": "guid"
} }
} }
} }
@@ -71,73 +111,18 @@
} }
} }
}, },
"/api/TodoItems": { "/api/Users": {
"get": {
"tags": [
"TodoItems"
],
"operationId": "GetTodoItemsWithPagination",
"parameters": [
{
"name": "ListId",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 1
},
{
"name": "PageNumber",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 2
},
{
"name": "PageSize",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 3
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PaginatedListOfTodoItemBriefDto"
}
}
}
}
},
"security": [
{
"JWT": []
}
]
},
"post": { "post": {
"tags": [ "tags": [
"TodoItems" "Users"
], ],
"operationId": "CreateTodoItem", "operationId": "CreateUser",
"requestBody": { "requestBody": {
"x-name": "command", "x-name": "command",
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/CreateTodoItemCommand" "$ref": "#/components/schemas/CreateUserCommand"
} }
} }
}, },
@@ -150,261 +135,13 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"type": "integer", "type": "string",
"format": "int32" "format": "guid"
} }
} }
} }
} }
},
"security": [
{
"JWT": []
} }
]
}
},
"/api/TodoItems/{id}": {
"put": {
"tags": [
"TodoItems"
],
"operationId": "UpdateTodoItem",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 1
}
],
"requestBody": {
"x-name": "command",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateTodoItemCommand"
}
}
},
"required": true,
"x-position": 2
},
"responses": {
"200": {
"description": ""
}
},
"security": [
{
"JWT": []
}
]
},
"delete": {
"tags": [
"TodoItems"
],
"operationId": "DeleteTodoItem",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 1
}
],
"responses": {
"200": {
"description": ""
}
},
"security": [
{
"JWT": []
}
]
}
},
"/api/TodoItems/UpdateDetail/{id}": {
"put": {
"tags": [
"TodoItems"
],
"operationId": "UpdateTodoItemDetail",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 1
}
],
"requestBody": {
"x-name": "command",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateTodoItemDetailCommand"
}
}
},
"required": true,
"x-position": 2
},
"responses": {
"200": {
"description": ""
}
},
"security": [
{
"JWT": []
}
]
}
},
"/api/TodoLists": {
"get": {
"tags": [
"TodoLists"
],
"operationId": "GetTodoLists",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TodosVm"
}
}
}
}
},
"security": [
{
"JWT": []
}
]
},
"post": {
"tags": [
"TodoLists"
],
"operationId": "CreateTodoList",
"requestBody": {
"x-name": "command",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateTodoListCommand"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "integer",
"format": "int32"
}
}
}
}
},
"security": [
{
"JWT": []
}
]
}
},
"/api/TodoLists/{id}": {
"put": {
"tags": [
"TodoLists"
],
"operationId": "UpdateTodoList",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 1
}
],
"requestBody": {
"x-name": "command",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateTodoListCommand"
}
}
},
"required": true,
"x-position": 2
},
"responses": {
"200": {
"description": ""
}
},
"security": [
{
"JWT": []
}
]
},
"delete": {
"tags": [
"TodoLists"
],
"operationId": "DeleteTodoList",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 1
}
],
"responses": {
"200": {
"description": ""
}
},
"security": [
{
"JWT": []
}
]
} }
}, },
"/api/Users/register": { "/api/Users/register": {
@@ -822,6 +559,52 @@
}, },
"components": { "components": {
"schemas": { "schemas": {
"PaginatedListOfFutureCreatorListDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FutureCreatorListDto"
}
},
"pageNumber": {
"type": "integer",
"format": "int32"
},
"totalPages": {
"type": "integer",
"format": "int32"
},
"totalCount": {
"type": "integer",
"format": "int32"
},
"hasPreviousPage": {
"type": "boolean"
},
"hasNextPage": {
"type": "boolean"
}
}
},
"FutureCreatorListDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"format": "guid"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
}
}
},
"CreateFutureCreatorCommand": { "CreateFutureCreatorCommand": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -848,6 +631,7 @@
}, },
"CreateSessionCheckoutCommand": { "CreateSessionCheckoutCommand": {
"type": "object", "type": "object",
"x-abstract": true,
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"price": { "price": {
@@ -859,231 +643,24 @@
} }
} }
}, },
"PaginatedListOfTodoItemBriefDto": { "CreateUserCommand": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"items": { "firstName": {
"type": "array", "type": "string"
"items": {
"$ref": "#/components/schemas/TodoItemBriefDto"
}
}, },
"pageNumber": { "lastName": {
"type": "integer", "type": "string"
"format": "int32"
}, },
"totalPages": { "emailAddress": {
"type": "integer", "type": "string"
"format": "int32"
}, },
"totalCount": { "userName": {
"type": "integer", "type": "string"
"format": "int32"
}, },
"hasPreviousPage": { "password": {
"type": "boolean" "type": "string"
},
"hasNextPage": {
"type": "boolean"
}
}
},
"TodoItemBriefDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"listId": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
},
"done": {
"type": "boolean"
}
}
},
"CreateTodoItemCommand": {
"type": "object",
"additionalProperties": false,
"properties": {
"listId": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
}
}
},
"UpdateTodoItemCommand": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
},
"done": {
"type": "boolean"
}
}
},
"UpdateTodoItemDetailCommand": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"listId": {
"type": "integer",
"format": "int32"
},
"priority": {
"$ref": "#/components/schemas/PriorityLevel"
},
"note": {
"type": "string",
"nullable": true
}
}
},
"PriorityLevel": {
"type": "integer",
"description": "",
"x-enumNames": [
"None",
"Low",
"Medium",
"High"
],
"enum": [
0,
1,
2,
3
]
},
"TodosVm": {
"type": "object",
"additionalProperties": false,
"properties": {
"priorityLevels": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LookupDto"
}
},
"lists": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TodoListDto"
}
}
}
},
"LookupDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
}
}
},
"TodoListDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
},
"colour": {
"type": "string",
"nullable": true
},
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TodoItemDto"
}
}
}
},
"TodoItemDto": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"listId": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
},
"done": {
"type": "boolean"
},
"priority": {
"type": "integer",
"format": "int32"
},
"note": {
"type": "string",
"nullable": true
}
}
},
"CreateTodoListCommand": {
"type": "object",
"additionalProperties": false,
"properties": {
"title": {
"type": "string",
"nullable": true
}
}
},
"UpdateTodoListCommand": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"title": {
"type": "string",
"nullable": true
} }
} }
}, },

View File

@@ -1,49 +0,0 @@
using Hutopy.Application.Common.Exceptions;
using Hutopy.Application.TodoItems.Commands.CreateTodoItem;
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FunctionalTests.TodoItems.Commands;
using static Testing;
public class CreateTodoItemTests : BaseTestFixture
{
[Test]
public async Task ShouldRequireMinimumFields()
{
var command = new CreateTodoItemCommand();
await FluentActions.Invoking(() =>
SendAsync(command)).Should().ThrowAsync<ValidationException>();
}
[Test]
public async Task ShouldCreateTodoItem()
{
var userId = await RunAsDefaultUserAsync();
var listId = await SendAsync(new CreateTodoListCommand
{
Title = "New List"
});
var command = new CreateTodoItemCommand
{
ListId = listId,
Title = "Tasks"
};
var itemId = await SendAsync(command);
var item = await FindAsync<TodoItem>(itemId);
item.Should().NotBeNull();
item!.ListId.Should().Be(command.ListId);
item.Title.Should().Be(command.Title);
item.CreatedBy.Should().Be(userId);
item.Created.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
item.LastModifiedBy.Should().Be(userId);
item.LastModified.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
}
}

View File

@@ -1,41 +0,0 @@
using Hutopy.Application.TodoItems.Commands.CreateTodoItem;
using Hutopy.Application.TodoItems.Commands.DeleteTodoItem;
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FunctionalTests.TodoItems.Commands;
using static Testing;
public class DeleteTodoItemTests : BaseTestFixture
{
[Test]
public async Task ShouldRequireValidTodoItemId()
{
var command = new DeleteTodoItemCommand(99);
await FluentActions.Invoking(() =>
SendAsync(command)).Should().ThrowAsync<NotFoundException>();
}
[Test]
public async Task ShouldDeleteTodoItem()
{
var listId = await SendAsync(new CreateTodoListCommand
{
Title = "New List"
});
var itemId = await SendAsync(new CreateTodoItemCommand
{
ListId = listId,
Title = "New Item"
});
await SendAsync(new DeleteTodoItemCommand(itemId));
var item = await FindAsync<TodoItem>(itemId);
item.Should().BeNull();
}
}

View File

@@ -1,57 +0,0 @@
using Hutopy.Application.TodoItems.Commands.CreateTodoItem;
using Hutopy.Application.TodoItems.Commands.UpdateTodoItem;
using Hutopy.Application.TodoItems.Commands.UpdateTodoItemDetail;
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Domain.Entities;
using Hutopy.Domain.Enums;
namespace Hutopy.Application.FunctionalTests.TodoItems.Commands;
using static Testing;
public class UpdateTodoItemDetailTests : BaseTestFixture
{
[Test]
public async Task ShouldRequireValidTodoItemId()
{
var command = new UpdateTodoItemCommand { Id = 99, Title = "New Title" };
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<NotFoundException>();
}
[Test]
public async Task ShouldUpdateTodoItem()
{
var userId = await RunAsDefaultUserAsync();
var listId = await SendAsync(new CreateTodoListCommand
{
Title = "New List"
});
var itemId = await SendAsync(new CreateTodoItemCommand
{
ListId = listId,
Title = "New Item"
});
var command = new UpdateTodoItemDetailCommand
{
Id = itemId,
ListId = listId,
Note = "This is the note.",
Priority = PriorityLevel.High
};
await SendAsync(command);
var item = await FindAsync<TodoItem>(itemId);
item.Should().NotBeNull();
item!.ListId.Should().Be(command.ListId);
item.Note.Should().Be(command.Note);
item.Priority.Should().Be(command.Priority);
item.LastModifiedBy.Should().NotBeNull();
item.LastModifiedBy.Should().Be(userId);
item.LastModified.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
}
}

View File

@@ -1,51 +0,0 @@
using Hutopy.Application.TodoItems.Commands.CreateTodoItem;
using Hutopy.Application.TodoItems.Commands.UpdateTodoItem;
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FunctionalTests.TodoItems.Commands;
using static Testing;
public class UpdateTodoItemTests : BaseTestFixture
{
[Test]
public async Task ShouldRequireValidTodoItemId()
{
var command = new UpdateTodoItemCommand { Id = 99, Title = "New Title" };
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<NotFoundException>();
}
[Test]
public async Task ShouldUpdateTodoItem()
{
var userId = await RunAsDefaultUserAsync();
var listId = await SendAsync(new CreateTodoListCommand
{
Title = "New List"
});
var itemId = await SendAsync(new CreateTodoItemCommand
{
ListId = listId,
Title = "New Item"
});
var command = new UpdateTodoItemCommand
{
Id = itemId,
Title = "Updated Item Title"
};
await SendAsync(command);
var item = await FindAsync<TodoItem>(itemId);
item.Should().NotBeNull();
item!.Title.Should().Be(command.Title);
item.LastModifiedBy.Should().NotBeNull();
item.LastModifiedBy.Should().Be(userId);
item.LastModified.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
}
}

View File

@@ -1,54 +0,0 @@
using Hutopy.Application.Common.Exceptions;
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FunctionalTests.TodoLists.Commands;
using static Testing;
public class CreateTodoListTests : BaseTestFixture
{
[Test]
public async Task ShouldRequireMinimumFields()
{
var command = new CreateTodoListCommand();
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<ValidationException>();
}
[Test]
public async Task ShouldRequireUniqueTitle()
{
await SendAsync(new CreateTodoListCommand
{
Title = "Shopping"
});
var command = new CreateTodoListCommand
{
Title = "Shopping"
};
await FluentActions.Invoking(() =>
SendAsync(command)).Should().ThrowAsync<ValidationException>();
}
[Test]
public async Task ShouldCreateTodoList()
{
var userId = await RunAsDefaultUserAsync();
var command = new CreateTodoListCommand
{
Title = "Tasks"
};
var id = await SendAsync(command);
var list = await FindAsync<TodoList>(id);
list.Should().NotBeNull();
list!.Title.Should().Be(command.Title);
list.CreatedBy.Should().Be(userId);
list.Created.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
}
}

View File

@@ -1,32 +0,0 @@
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Application.TodoLists.Commands.DeleteTodoList;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FunctionalTests.TodoLists.Commands;
using static Testing;
public class DeleteTodoListTests : BaseTestFixture
{
[Test]
public async Task ShouldRequireValidTodoListId()
{
var command = new DeleteTodoListCommand(99);
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<NotFoundException>();
}
[Test]
public async Task ShouldDeleteTodoList()
{
var listId = await SendAsync(new CreateTodoListCommand
{
Title = "New List"
});
await SendAsync(new DeleteTodoListCommand(listId));
var list = await FindAsync<TodoList>(listId);
list.Should().BeNull();
}
}

View File

@@ -1,75 +0,0 @@
using Hutopy.Application.Common.Exceptions;
using Hutopy.Application.Common.Security;
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Application.TodoLists.Commands.PurgeTodoLists;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FunctionalTests.TodoLists.Commands;
using static Testing;
public class PurgeTodoListsTests : BaseTestFixture
{
[Test]
public async Task ShouldDenyAnonymousUser()
{
var command = new PurgeTodoListsCommand();
command.GetType().Should().BeDecoratedWith<AuthorizeAttribute>();
var action = () => SendAsync(command);
await action.Should().ThrowAsync<UnauthorizedAccessException>();
}
[Test]
public async Task ShouldDenyNonAdministrator()
{
await RunAsDefaultUserAsync();
var command = new PurgeTodoListsCommand();
var action = () => SendAsync(command);
await action.Should().ThrowAsync<ForbiddenAccessException>();
}
[Test]
public async Task ShouldAllowAdministrator()
{
await RunAsAdministratorAsync();
var command = new PurgeTodoListsCommand();
var action = () => SendAsync(command);
await action.Should().NotThrowAsync<ForbiddenAccessException>();
}
[Test]
public async Task ShouldDeleteAllLists()
{
await RunAsAdministratorAsync();
await SendAsync(new CreateTodoListCommand
{
Title = "New List #1"
});
await SendAsync(new CreateTodoListCommand
{
Title = "New List #2"
});
await SendAsync(new CreateTodoListCommand
{
Title = "New List #3"
});
await SendAsync(new PurgeTodoListsCommand());
var count = await CountAsync<TodoList>();
count.Should().Be(0);
}
}

View File

@@ -1,70 +0,0 @@
using Hutopy.Application.Common.Exceptions;
using Hutopy.Application.TodoLists.Commands.CreateTodoList;
using Hutopy.Application.TodoLists.Commands.UpdateTodoList;
using Hutopy.Domain.Entities;
namespace Hutopy.Application.FunctionalTests.TodoLists.Commands;
using static Testing;
public class UpdateTodoListTests : BaseTestFixture
{
[Test]
public async Task ShouldRequireValidTodoListId()
{
var command = new UpdateTodoListCommand { Id = 99, Title = "New Title" };
await FluentActions.Invoking(() => SendAsync(command)).Should().ThrowAsync<NotFoundException>();
}
[Test]
public async Task ShouldRequireUniqueTitle()
{
var listId = await SendAsync(new CreateTodoListCommand
{
Title = "New List"
});
await SendAsync(new CreateTodoListCommand
{
Title = "Other List"
});
var command = new UpdateTodoListCommand
{
Id = listId,
Title = "Other List"
};
(await FluentActions.Invoking(() =>
SendAsync(command))
.Should().ThrowAsync<ValidationException>().Where(ex => ex.Errors.ContainsKey("Title")))
.And.Errors["Title"].Should().Contain("'Title' must be unique.");
}
[Test]
public async Task ShouldUpdateTodoList()
{
var userId = await RunAsDefaultUserAsync();
var listId = await SendAsync(new CreateTodoListCommand
{
Title = "New List"
});
var command = new UpdateTodoListCommand
{
Id = listId,
Title = "Updated List Title"
};
await SendAsync(command);
var list = await FindAsync<TodoList>(listId);
list.Should().NotBeNull();
list!.Title.Should().Be(command.Title);
list.LastModifiedBy.Should().NotBeNull();
list.LastModifiedBy.Should().Be(userId);
list.LastModified.Should().BeCloseTo(DateTime.Now, TimeSpan.FromMilliseconds(10000));
}
}

View File

@@ -1,61 +0,0 @@
using Hutopy.Application.TodoLists.Queries.GetTodos;
using Hutopy.Domain.Entities;
using Hutopy.Domain.ValueObjects;
namespace Hutopy.Application.FunctionalTests.TodoLists.Queries;
using static Testing;
public class GetTodosTests : BaseTestFixture
{
[Test]
public async Task ShouldReturnPriorityLevels()
{
await RunAsDefaultUserAsync();
var query = new GetTodosQuery();
var result = await SendAsync(query);
result.PriorityLevels.Should().NotBeEmpty();
}
[Test]
public async Task ShouldReturnAllListsAndItems()
{
await RunAsDefaultUserAsync();
await AddAsync(new TodoList
{
Title = "Shopping",
Colour = Colour.Blue,
Items =
{
new TodoItem { Title = "Apples", Done = true },
new TodoItem { Title = "Milk", Done = true },
new TodoItem { Title = "Bread", Done = true },
new TodoItem { Title = "Toilet paper" },
new TodoItem { Title = "Pasta" },
new TodoItem { Title = "Tissues" },
new TodoItem { Title = "Tuna" }
}
});
var query = new GetTodosQuery();
var result = await SendAsync(query);
result.Lists.Should().HaveCount(1);
result.Lists.First().Items.Should().HaveCount(7);
}
[Test]
public async Task ShouldDenyAnonymousUser()
{
var query = new GetTodosQuery();
var action = () => SendAsync(query);
await action.Should().ThrowAsync<UnauthorizedAccessException>();
}
}

View File

@@ -1,6 +1,5 @@
using Hutopy.Application.Common.Behaviours; using Hutopy.Application.Common.Behaviours;
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.TodoItems.Commands.CreateTodoItem;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@@ -9,37 +8,13 @@ namespace Hutopy.Application.UnitTests.Common.Behaviours;
public class RequestLoggerTests public class RequestLoggerTests
{ {
private Mock<ILogger<CreateTodoItemCommand>> _logger = null!;
private Mock<IUser> _user = null!; private Mock<IUser> _user = null!;
private Mock<IIdentityService> _identityService = null!; private Mock<IIdentityService> _identityService = null!;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_logger = new Mock<ILogger<CreateTodoItemCommand>>();
_user = new Mock<IUser>(); _user = new Mock<IUser>();
_identityService = new Mock<IIdentityService>(); _identityService = new Mock<IIdentityService>();
} }
[Test]
public async Task ShouldCallGetUserNameAsyncOnceIfAuthenticated()
{
_user.Setup(x => x.Id).Returns(Guid.NewGuid().ToString());
var requestLogger = new LoggingBehaviour<CreateTodoItemCommand>(_logger.Object, _user.Object, _identityService.Object);
await requestLogger.Process(new CreateTodoItemCommand { ListId = 1, Title = "title" }, new CancellationToken());
_identityService.Verify(i => i.GetUserNameAsync(It.IsAny<string>()), Times.Once);
}
[Test]
public async Task ShouldNotCallGetUserNameAsyncOnceIfUnauthenticated()
{
var requestLogger = new LoggingBehaviour<CreateTodoItemCommand>(_logger.Object, _user.Object, _identityService.Object);
await requestLogger.Process(new CreateTodoItemCommand { ListId = 1, Title = "title" }, new CancellationToken());
_identityService.Verify(i => i.GetUserNameAsync(It.IsAny<string>()), Times.Never);
}
} }

View File

@@ -3,8 +3,6 @@ using System.Runtime.CompilerServices;
using AutoMapper; using AutoMapper;
using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Models; using Hutopy.Application.Common.Models;
using Hutopy.Application.TodoItems.Queries.GetTodoItemsWithPagination;
using Hutopy.Application.TodoLists.Queries.GetTodos;
using Hutopy.Domain.Entities; using Hutopy.Domain.Entities;
using NUnit.Framework; using NUnit.Framework;
@@ -29,19 +27,6 @@ public class MappingTests
_configuration.AssertConfigurationIsValid(); _configuration.AssertConfigurationIsValid();
} }
[Test]
[TestCase(typeof(TodoList), typeof(TodoListDto))]
[TestCase(typeof(TodoItem), typeof(TodoItemDto))]
[TestCase(typeof(TodoList), typeof(LookupDto))]
[TestCase(typeof(TodoItem), typeof(LookupDto))]
[TestCase(typeof(TodoItem), typeof(TodoItemBriefDto))]
public void ShouldSupportMappingFromSourceToDestination(Type source, Type destination)
{
var instance = GetInstanceOf(source);
_mapper.Map(instance, source, destination);
}
private object GetInstanceOf(Type type) private object GetInstanceOf(Type type)
{ {
if (type.GetConstructor(Type.EmptyTypes) != null) if (type.GetConstructor(Type.EmptyTypes) != null)