Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User as aggregate root #26

Merged
merged 4 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions Application/Authentication/Register/RegisterCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@ public async Task<Result<bool>> Handle(RegisterCommand request, CancellationToke
var user = User.Create(request.Name, request.Email, hash, Role.DefaultUser);

_context.Users.Add(user);

// refreshToken
var refreshToken = RefreshToken.Create("sample", user.Id);

// Security reasons
refreshToken.MakeExpire();

_context.RefreshTokens.Add(refreshToken);

await _context.SaveChangesAsync(cancellationToken);

Expand Down
2 changes: 1 addition & 1 deletion Domain/Users/RefreshToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static RefreshToken Create(string value, Guid userId)
{
return new(
value,
DateTime.UtcNow.AddDays(LifeTime),
DateTime.UtcNow,
userId
);
}
Expand Down
9 changes: 8 additions & 1 deletion Domain/Users/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@

public RefreshToken RefreshToken { get; protected set; }

protected User()

Check warning on line 67 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field '_name' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 67 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field '_email' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 67 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field '_password' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 67 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Role' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 67 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'RefreshToken' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
{

}

protected User(string name, string email, string password, int roleId, bool isVerified)

Check warning on line 72 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field '_name' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 72 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field '_email' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 72 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field '_password' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check warning on line 72 in Domain/Users/User.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Role' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
: base(Guid.NewGuid())
{
Name = name;
Expand All @@ -79,9 +79,16 @@
IsVerified = isVerified;
}

public static User Create(string name, string email, string password, int roleId)
public static User Create(
string name,
string email,
string password,
int roleId
)
{
User result = new(name, email, password, roleId, false);

result.RefreshToken = RefreshToken.Create("sample", result.Id);

result.RaiseEvent(new UserCreatedDomainEvent(result.Id));

Comment on lines 88 to 94
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему тут нет объявления роли? См сюда. Каждый раз, когда пытаешься привязать роль (пусть даже существующую: и Id, и Имя те же) EF Core смотрит на тебя, как на идиота и кидает Exception: duplicate primary key

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не понял что является причиной ошибки, EF Core? Просто из того обсуждения, что ты скинул я вообще не понял, почему это так. Почему ты вообще пытаешься передать роль в метод Create у RefreshToken-а, когда ни в одном методе Create этого класса не используется роль?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А, я не то выделил. Каюсь. Крч там дело в том, что change tracker'у невозможно сказать никак, что какая либо сущность не должна пробрасываться в базу. Поэтому если при создании пользователя делать ещё и роль, то он всегда кидает эксепшион, типа он считает, что я хочу по второму кругу ту же самую роль добавить. А команда EF уже кучу лет пытаются сделать readonly сущности, чтобы это решить.

Expand Down
3 changes: 2 additions & 1 deletion Infrastructure/Configurations/UserConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public void Configure(EntityTypeBuilder<User> builder)
builder
.HasOne(u => u.RefreshToken)
.WithOne(r => r.Owner)
.HasForeignKey<RefreshToken>(r => r.Id);
.HasForeignKey<RefreshToken>(r => r.Id)
.IsRequired();

// Many-to-one with role
builder
Expand Down
19 changes: 5 additions & 14 deletions Tests/ApplicationTests/Authentication/LogoutCommandHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@ public static IEnumerable<object[]> InvalidUsersAndTokens()
{
var user1 = User.Create("123", "test", "123123", 1);

// Expired token
var expiredToken = RefreshToken.Create("123123", user1.Id);
expiredToken.MakeExpire();

// Token that does not exist
var nonExistentToken = RefreshToken.Create("123", Guid.NewGuid());

return [[user1, expiredToken], [user1, nonExistentToken]];
return [[user1, user1.RefreshToken], [user1, nonExistentToken]];
}

[Theory]
Expand All @@ -35,11 +31,6 @@ public void Logout_ShouldReturnException_OnInvalidToken(User user, RefreshToken
// Arrange
RecreateDbContext();
Context.Users.Add(user);

if (token.Id == user.Id)
{
Context.RefreshTokens.Add(token);
}

Context.SaveChangesAsync().GetAwaiter().GetResult();

Expand Down Expand Up @@ -68,17 +59,17 @@ public void Refresh_ShouldReturnTokenResponse_OnValidToken()
RecreateDbContext();

var user = User.Create("123", "test", "123123", 1);

var token = RefreshToken.Create("123123123123", user.Id);

user.RefreshToken.Update("123123123");
user.VerifyEmail();

Context.Users.Add(user);
Context.RefreshTokens.Add(token);

Context.SaveChangesAsync().GetAwaiter().GetResult();

var handler = new LogoutCommandHandler(Context);

var request = new LogoutCommand(token.Value);
var request = new LogoutCommand(user.RefreshToken.Value);

// Act
var res = handler.Handle(request, default).GetAwaiter().GetResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,10 @@ public static IEnumerable<object[]> InvalidUsersAndTokens()
{
var user1 = User.Create("123", "test", "123123", 1);

// Expired token
var expiredToken = RefreshToken.Create("123123", user1.Id);
expiredToken.MakeExpire();

// Token that does not exist
var nonExistentToken = RefreshToken.Create("123", Guid.NewGuid());

return [[user1, expiredToken], [user1, nonExistentToken]];
return [[user1, user1.RefreshToken], [user1, nonExistentToken]];
}

[Theory]
Expand Down Expand Up @@ -67,17 +63,17 @@ public void Refresh_ShouldReturnTokenResponse_OnValidToken()
RecreateDbContext();

var user = User.Create("123", "test", "123123", 1);

var token = RefreshToken.Create("123123123123", user.Id);

user.VerifyEmail();

user.RefreshToken.Update(JwtProvider.GenerateRefreshToken());
Context.Users.Add(user);
Context.RefreshTokens.Add(token);

Context.SaveChangesAsync().GetAwaiter().GetResult();

var handler = new RefreshCommandHandler(Context, JwtProvider);

var request = new RefreshCommand(token.Value);
var request = new RefreshCommand(user.RefreshToken.Value);

// Act
var res = handler.Handle(request, default).GetAwaiter().GetResult();
Expand Down
1 change: 1 addition & 0 deletions Tests/DomainTests/RefreshTokenTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public void MakeExpire_Should_MakeTokenExpire()
{
// Arrange
var token = RefreshToken.Create("random123", Guid.NewGuid());
token.Update("bimbimbambam");

// Act
token.MakeExpire();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,8 @@ public async Task Logout_Should_ReturnBadRequest_IfRefreshTokenIsInvalid()
var httpClient = Factory.CreateClient();

var fakeToken = "bimbimbimbambambam";
var actualToken = "hehehehuh";

var user = User.Create("bimbim", "bambam", "123123", Role.DefaultUser);

var token = RefreshToken.Create(actualToken, user.Id);

httpClient.DefaultRequestHeaders.Add("cookie", [$"refreshToken={fakeToken}"]);

Expand All @@ -48,7 +45,6 @@ public async Task Logout_Should_ReturnBadRequest_IfRefreshTokenIsInvalid()
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

context.Users.Add(user);
context.RefreshTokens.Add(token);

await context.SaveChangesAsync();

Expand All @@ -72,7 +68,8 @@ public async Task Logout_Should_ReturnBaseResponse_IfRefreshTokenValid()

var user = User.Create("bimbim", "bambam", "123123", Role.DefaultUser);

var token = RefreshToken.Create(actualToken, user.Id);
user.RefreshToken.Update(actualToken);
user.VerifyEmail();

httpClient.DefaultRequestHeaders.Add("cookie", [$"refreshToken={actualToken}"]);

Expand All @@ -81,11 +78,9 @@ public async Task Logout_Should_ReturnBaseResponse_IfRefreshTokenValid()
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

context.Users.Add(user);
context.RefreshTokens.Add(token);

await context.SaveChangesAsync();


// Act
var response = await httpClient.PutAsync("Authentication/Logout", null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,8 @@ public async Task Refresh_Should_ReturnBadRequest_IfRefreshTokenIsInvalid()
var httpClient = Factory.CreateClient();

var fakeToken = "bimbimbimbambambam";
var actualToken = "hehehehuh";

var user = User.Create("bimbim", "bambam", "123123", Role.DefaultUser);

var token = RefreshToken.Create(actualToken, user.Id);

httpClient.DefaultRequestHeaders.Add("cookie", [$"refreshToken={fakeToken}"]);

Expand All @@ -49,7 +46,6 @@ public async Task Refresh_Should_ReturnBadRequest_IfRefreshTokenIsInvalid()
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

context.Users.Add(user);
context.RefreshTokens.Add(token);

await context.SaveChangesAsync();

Expand All @@ -69,11 +65,12 @@ public async Task Refresh_Should_ReturnTokenResponse_IfRefreshTokenValid()
// Arrange
var httpClient = Factory.CreateClient();

var actualToken = "hehehehuh";

var user = User.Create("bimbim", "bambam", "123123", Role.DefaultUser);

var token = RefreshToken.Create(actualToken, user.Id);
user.RefreshToken.Update("123123123");
user.VerifyEmail();

var actualToken = user.RefreshToken.Value;

httpClient.DefaultRequestHeaders.Add("cookie", [$"refreshToken={actualToken}"]);

Expand All @@ -82,11 +79,9 @@ public async Task Refresh_Should_ReturnTokenResponse_IfRefreshTokenValid()
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

context.Users.Add(user);
context.RefreshTokens.Add(token);

await context.SaveChangesAsync();


// Act
var response = await httpClient.PutAsync("Authentication/Refresh", null);

Expand Down
Loading