Merged PR 18: Removed todos and added User endpoint combined with register. Id changed to guid into Master

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

Related work items: #20
This commit is contained in:
Dominic Villemure
2024-04-08 22:16:36 +00:00
67 changed files with 1755 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,26 @@
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)
{
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,30 @@
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,
};
//todo: Need to handle errors better for the user.
var response = await _userManager.CreateAsync(applicationUser, password);
if (response.Errors.Any())
{
throw new InvalidOperationException(response.Errors.First().Description);
}
}
}

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)