Skip to content

Commit ed13f59

Browse files
committed
Atualização de versão e melhorias na verificação de e-mail
* Atualização de versão - Atualizados `DomainVer` e `PersistVer` para novos números de versão. * Testes e configuração do UnitOfWork - Adicionada nova função de teste para configurar o UnitOfWork. - Simplificada a lógica de adição de repositórios no `IUnitOfWorkBuilder`. - Introduzido método para adicionar repositórios de entidades. * Estrutura de e-mail e verificação - Atualizada a classe `Author` para incluir verificações de e-mail. - Criada a classe `EmailVerificationMapping` para configuração de entidades. - Desenvolvida a classe `EmailVerification` com lógica de validação. - Adicionada nova classe `VerifyEmail` para futuras implementações.
1 parent ec4599b commit ed13f59

File tree

7 files changed

+158
-30
lines changed

7 files changed

+158
-30
lines changed

RoyalCode.EnterprisePatterns/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<DomainVer>0.8.0</DomainVer>
1010
<DomainPreview></DomainPreview>
1111

12-
<PersistVer>0.8.4</PersistVer>
12+
<PersistVer>0.8.5</PersistVer>
1313
<PersistPreview></PersistPreview>
1414

1515
<CommandVer>0.1.0</CommandVer>

RoyalCode.EnterprisePatterns/RoyalCode.Persistence.Tests/UnitOfWork/UnitOfWorkBuilderTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using RoyalCode.Persistence.Tests.Entities;
44
using RoyalCode.Repositories;
55
using RoyalCode.UnitOfWork;
6+
using RoyalCode.UnitOfWork.EntityFramework;
67
using Xunit;
78

89
namespace RoyalCode.Persistence.Tests.UnitOfWork;
@@ -38,6 +39,36 @@ public void ConfigureUnitOfWorkContextAndRepository()
3839

3940
scope.Dispose();
4041
}
42+
43+
[Fact]
44+
public void ConfigureUnitOfWorkFromAssembly()
45+
{
46+
ServiceCollection services = new();
47+
48+
services.AddUnitOfWorkDefault()
49+
.ConfigureOptions(builder => builder.UseSqlite("DataSource=:memory:"))
50+
.ConfigureMappingsFromAssembly(typeof(UnitOfWorkBuilderTests).Assembly, true);
51+
52+
var root = services.BuildServiceProvider();
53+
var scope = root.CreateScope();
54+
var sp = scope.ServiceProvider;
55+
56+
var db = sp.GetService<DefaultDbContext>();
57+
Assert.NotNull(db);
58+
59+
db!.Database.EnsureCreated();
60+
61+
var uow = sp.GetService<IUnitOfWork>();
62+
Assert.NotNull(uow);
63+
64+
var repo = sp.GetService<IRepository<Person>>();
65+
Assert.NotNull(repo);
66+
67+
var set = db.Set<Person>();
68+
Assert.NotNull(set);
69+
70+
scope.Dispose();
71+
}
4172
}
4273

4374
#region Test classes

RoyalCode.EnterprisePatterns/RoyalCode.UnitOfWork.EntityFramework/Configurations/IUnitOfWorkBuilder.cs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -250,14 +250,11 @@ public TBuilder UseLoggerFactoryAndEnableSensitiveDataLogging(bool isDevelopment
250250
/// <returns>The same instance.</returns>
251251
public TBuilder ConfigureMappingsFromAssembly(Assembly assembly, bool addRepositories)
252252
{
253+
if (addRepositories)
254+
AddRepositories(assembly);
253255
return ConfigureModel(modelBuilder =>
254256
{
255-
if (addRepositories)
256-
modelBuilder.ApplyConfigurationsFromAssembly(
257-
assembly,
258-
type => AddRepositories(type));
259-
else
260-
modelBuilder.ApplyConfigurationsFromAssembly(assembly);
257+
modelBuilder.ApplyConfigurationsFromAssembly(assembly);
261258
});
262259
}
263260

@@ -276,32 +273,36 @@ public TBuilder ConfigureMappingsFromAssembly<TTypeFromAssembly>(bool addReposit
276273
return ConfigureMappingsFromAssembly(typeof(TTypeFromAssembly).Assembly, addRepositories);
277274
}
278275

279-
private bool AddRepositories(Type type)
276+
/// <summary>
277+
/// <para>
278+
/// Adds repositories for all entity types found in the specified assembly.
279+
/// </para>
280+
/// <para>
281+
/// Will scan the assembly for types that implement <see cref="IEntityTypeConfiguration{TEntity}"/>,
282+
/// so the entity type will be the generic type argument of the interface.
283+
/// </para>
284+
/// <para>
285+
/// For each entity type found, it will be added a repository related to the unit of work.
286+
/// </para>
287+
/// </summary>
288+
/// <param name="assembly"></param>
289+
/// <returns></returns>
290+
public TBuilder AddRepositories(Assembly assembly)
280291
{
281-
// checks if it is an entity configuration and gets the type of the configured entity
282-
Type? entityType = null;
283-
foreach (var @interface in type.GetInterfaces())
292+
// gets all entities types from the assembly getting the configurations (IEntityTypeConfiguration<>)
293+
var entityTypes = assembly.GetTypes()
294+
.Where(type => type.IsClass && !type.IsAbstract)
295+
.SelectMany(type => type.GetInterfaces())
296+
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))
297+
.Select(i => i.GetGenericArguments()[0]);
298+
299+
// adds repositories for each entity type
300+
foreach (var type in entityTypes)
284301
{
285-
if (!@interface.IsGenericType)
286-
{
287-
continue;
288-
}
289-
290-
if (@interface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))
291-
{
292-
entityType = @interface.GetGenericArguments()[0];
293-
}
302+
ConfigureRepositories(r => r.Add(type));
294303
}
295304

296-
// if it is not an entity configuration, ignore
297-
if (entityType is null)
298-
return false;
299-
300-
// adds the repository for the configured entity
301-
ConfigureRepositories(r => r.Add(entityType));
302-
303-
// finally returns true to indicate that the configuration has been added
304-
return true;
305+
return (TBuilder)this;
305306
}
306307
}
307308

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace RoyalCode.Examples.Blogs.Contracts.Authors
8+
{
9+
internal class VerifyEmail
10+
{
11+
}
12+
}

RoyalCode.Examples/RoyalCode.Examples.Blogs/Core/Support/Author.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public Author(string name, string email)
1010
Name = name;
1111
Email = email;
1212
CreatedAt = DateTime.UtcNow;
13+
EmailVerifications = [new EmailVerification(this)];
1314
}
1415

1516
#nullable disable
@@ -29,6 +30,8 @@ public Author() { }
2930

3031
public DateTime? LastModifiedDate { get; set; }
3132

33+
public ICollection<EmailVerification> EmailVerifications { get; set; }
34+
3235
public void ConfirmAuthor()
3336
{
3437
IsConfirmed = true;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using RoyalCode.Entities;
2+
using RoyalCode.SmartProblems;
3+
using System.Text;
4+
5+
namespace RoyalCode.Examples.Blogs.Core.Support;
6+
7+
public class EmailVerification : Entity<Guid>
8+
{
9+
10+
public EmailVerification(Author author)
11+
{
12+
Id = Guid.CreateVersion7();
13+
LinkCode = Guid.NewGuid().ToString("N");
14+
15+
StringBuilder sb = new StringBuilder();
16+
for (int i = 0; i < 6; i++)
17+
{
18+
sb.Append(Random.Shared.Next(0, 10));
19+
}
20+
UICode = sb.ToString();
21+
22+
Author = author;
23+
CreatedAt = DateTimeOffset.UtcNow;
24+
ValidUntil = CreatedAt.AddDays(1);
25+
}
26+
27+
#nullable disable
28+
/// <summary>
29+
/// Constructor for deserialization purposes.
30+
/// </summary>
31+
public EmailVerification() { }
32+
#nullable enable
33+
34+
public string LinkCode { get; set; }
35+
36+
public string UICode { get; set; }
37+
38+
public Author Author { get; set; }
39+
40+
public DateTimeOffset CreatedAt { get; set; }
41+
42+
public DateTimeOffset ValidUntil { get; set; }
43+
44+
public DateTimeOffset? ValidatedAt { get; set; }
45+
46+
public Result TryValidate(string code)
47+
{
48+
if (ValidatedAt.HasValue)
49+
{
50+
return Problems.InvalidState("Email already validated.");
51+
}
52+
53+
if (UICode != code)
54+
{
55+
return Problems.InvalidParameter("Invalid verification code.");
56+
}
57+
58+
if (DateTimeOffset.UtcNow > ValidUntil)
59+
{
60+
return Problems.InvalidState("Verification code expired.");
61+
}
62+
63+
ValidatedAt = DateTimeOffset.UtcNow;
64+
Author.ConfirmAuthor();
65+
66+
return Result.Ok();
67+
}
68+
}

RoyalCode.Examples/RoyalCode.Examples.Blogs/Infra/Persistence/ConfigureWorkContext.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ public static IWorkContextBuilder<TDbContext> ConfigureBlogs<TDbContext>(this IW
2222
.Add<Post>()
2323
.Add<Core.Blogs.Thread>()
2424
.Add<Comment>()
25-
.Add<Author>();
25+
.Add<Author>()
26+
.Add<EmailVerification>();
2627
})
2728
.ConfigureSearches(b =>
2829
{
@@ -102,4 +103,16 @@ public void Configure(EntityTypeBuilder<Author> builder)
102103
{
103104

104105
}
106+
}
107+
108+
public class EmailVerificationMapping : IEntityTypeConfiguration<EmailVerification>
109+
{
110+
public void Configure(EntityTypeBuilder<EmailVerification> builder)
111+
{
112+
builder
113+
.HasOne(ev => ev.Author)
114+
.WithMany(a => a.EmailVerifications)
115+
.HasForeignKey("AuthorId")
116+
.OnDelete(DeleteBehavior.Cascade);
117+
}
105118
}

0 commit comments

Comments
 (0)