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

Refactoring & Fixes #34

Merged
merged 1 commit into from
Mar 6, 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
15 changes: 9 additions & 6 deletions src/TeamUp.Api/Extensions/ResultExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using TeamUp.Common;
using Microsoft.AspNetCore.Mvc;

using TeamUp.Common;

namespace TeamUp.Api.Extensions;

Expand All @@ -22,10 +24,11 @@ public static IResult Match(this Result result, Func<IResult> success)

public static IResult ToResponse(this ErrorBase error)
{
return TypedResults.Problem(
title: error.GetType().Name,
detail: error.Message,
statusCode: error switch
return TypedResults.Problem(new ProblemDetails
{
Title = error.GetType().Name,
Detail = error.Message,
Status = error switch
{
AuthenticationError => StatusCodes.Status401Unauthorized,
AuthorizationError => StatusCodes.Status403Forbidden,
Expand All @@ -37,6 +40,6 @@ public static IResult ToResponse(this ErrorBase error)
InternalError => StatusCodes.Status500InternalServerError,
_ => StatusCodes.Status500InternalServerError
}
);
});
}
}
20 changes: 20 additions & 0 deletions src/TeamUp.Common/Result/Extensions/ResultExtensions.Ensure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,24 @@ public static Result<TValue> EnsureNotNull<TValue, TProperty, TError>(this Resul
var self = await selfTask;
return self.EnsureSecondNotNull(error);
}

public static Result<TProperty> ThenEnsure<TValue, TProperty, TError>(this Result<TValue> self, Func<TValue, TProperty> selector, Rule<TProperty> rule, TError error) where TError : ErrorBase
{
if (self.IsFailure)
return self.Error;

var property = selector(self.Value);
if (!rule(property))
return error;

return property;
}

public static Result<TProperty> ThenEnsure<TValue, TProperty, TRule>(this Result<TValue> self, Func<TValue, TProperty> selector, TRule rule) where TRule : IRuleWithError<TProperty>
{
if (self.IsFailure)
return self.Error;

return rule.Apply(selector(self.Value));
}
}
4 changes: 3 additions & 1 deletion src/TeamUp.Contracts/Events/CreateEventRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ public Validator(IDateTimeProvider dateTimeProvider)
.Must((model, to) => model.FromUtc < to)
.WithMessage("Event cannot end before it starts.");

RuleFor(x => x.Description).NotEmpty();
RuleFor(x => x.Description)
.NotEmpty()
.MaximumLength(EventConstants.EVENT_DESCRIPTION_MAX_SIZE);

RuleFor(x => x.MeetTime).GreaterThan(TimeSpan.Zero);

Expand Down
8 changes: 8 additions & 0 deletions src/TeamUp.Contracts/Events/EventConstatnts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TeamUp.Contracts.Events;

public static class EventConstants
{
public const int EVENT_DESCRIPTION_MAX_SIZE = 30;

public const int EVENT_REPLY_MESSAGE_MAX_SIZE = 80;
}
8 changes: 6 additions & 2 deletions src/TeamUp.Contracts/Events/UpsertEventReplyRequest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using FluentValidation;
using System.ComponentModel.DataAnnotations;

using FluentValidation;

using TeamUp.Contracts.Abstractions;

Expand All @@ -7,6 +9,8 @@ namespace TeamUp.Contracts.Events;
public sealed class UpsertEventReplyRequest : IRequestBody
{
public required ReplyType ReplyType { get; init; }

[DataType(DataType.Text)]
public required string Message { get; init; }

public sealed class Validator : AbstractValidator<UpsertEventReplyRequest>
Expand All @@ -18,7 +22,7 @@ public Validator()
RuleFor(x => x.Message)
.Empty().When(x => x.ReplyType == ReplyType.Yes, ApplyConditionTo.CurrentValidator)
.NotEmpty().When(x => x.ReplyType == ReplyType.No || x.ReplyType == ReplyType.Maybe, ApplyConditionTo.CurrentValidator)
.MaximumLength(80);
.MaximumLength(EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE);
}
}
}
5 changes: 5 additions & 0 deletions src/TeamUp.Contracts/Teams/TeamConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ public static class TeamConstants

public const int NICKNAME_MIN_SIZE = 3;
public const int NICKNAME_MAX_SIZE = 30;

public const int EVENTTYPE_NAME_MIN_SIZE = 3;
public const int EVENTTYPE_NAME_MAX_SIZE = 30;

public const int EVENTTYPE_DESCRIPTION_MAX_SIZE = 30;
}
1 change: 1 addition & 0 deletions src/TeamUp.Domain/Aggregates/Events/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public static Result<Event> Create(
return fromUtc
.Ensure(from => from < toUtc, EventErrors.CannotEndBeforeStart)
.Ensure(from => from > dateTimeProvider.DateTimeOffsetUtcNow, EventErrors.CannotStartInPast)
.ThenEnsure(_ => description.Length, len => len <= EventConstants.EVENT_DESCRIPTION_MAX_SIZE, EventErrors.EventDescriptionMaxSize)
.Then(_ => new Event(
EventId.New(),
eventTypeId,
Expand Down
4 changes: 4 additions & 0 deletions src/TeamUp.Domain/Aggregates/Events/EventErrors.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
using TeamUp.Common;
using TeamUp.Contracts.Events;

namespace TeamUp.Domain.Aggregates.Events;

public static class EventErrors
{
public static readonly ValidationError EventDescriptionMaxSize = ValidationError.New($"Event's description must be shorter than {EventConstants.EVENT_DESCRIPTION_MAX_SIZE} characters.", "Events.DescriptionMaxSize");

public static readonly ValidationError CannotEndBeforeStart = ValidationError.New("Event cannot end before it starts.", "Events.CannotEndBeforeStart");
public static readonly ValidationError CannotStartInPast = ValidationError.New("Cannot create event in the past.", "Events.CannotStartInPast");

public static readonly DomainError NotOpenForResponses = DomainError.New("Event is not open for responses.", "Events.NotOpenForResponses");
public static readonly DomainError TimeForResponsesExpired = DomainError.New("Time for responses expired.", "Events.TimeForResponsesExpired");

public static readonly NotFoundError EventNotFound = NotFoundError.New("Event not found.", "Events.NotFound");

}
10 changes: 7 additions & 3 deletions src/TeamUp.Domain/Aggregates/Teams/EventType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using TeamUp.Contracts.Teams;
using TeamUp.Common;
using TeamUp.Contracts.Teams;
using TeamUp.Domain.Abstractions;

namespace TeamUp.Domain.Aggregates.Teams;
Expand All @@ -20,9 +21,12 @@ private EventType(EventTypeId id, string name, string description, Team team) :
TeamId = team.Id;
}

public static EventType Create(string name, string description, Team team)
public static Result<EventType> Create(string name, string description, Team team)
{
return new(EventTypeId.New(), name, description, team);
return name
.Ensure(TeamRules.EventTypeNameMinSize, TeamRules.EventTypeNameMaxSize)
.ThenEnsure(_ => description, TeamRules.EventTypeDescriptionMaxSize)
.Then(_ => new EventType(EventTypeId.New(), name, description, team));
}

internal void UpdateName(string name)
Expand Down
3 changes: 3 additions & 0 deletions src/TeamUp.Domain/Aggregates/Teams/TeamErrors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ public static class TeamErrors
public static readonly ValidationError TeamNameMaxSize = ValidationError.New($"Name must be shorter than {TeamConstants.TEAM_NAME_MAX_SIZE} characters.", "Teams.NameMaxSize");
public static readonly ValidationError NicknameMinSize = ValidationError.New($"Nickname must be atleast {TeamConstants.NICKNAME_MIN_SIZE} characters long.", "Teams.Members.NicknameMinSize");
public static readonly ValidationError NicknameMaxSize = ValidationError.New($"Nickname must be shorter than {TeamConstants.NICKNAME_MAX_SIZE} characters.", "Teams.Members.NicknameMaxSize");
public static readonly ValidationError EventTypeNameMinSize = ValidationError.New($"EventType's name must be atleast {TeamConstants.EVENTTYPE_NAME_MIN_SIZE} characters long.", "Teams.EventTypes.NameMinSize");
public static readonly ValidationError EventTypeNameMaxSize = ValidationError.New($"EventType's name must be shorter than {TeamConstants.EVENTTYPE_NAME_MAX_SIZE} characters.", "Teams.EventTypes.NameMaxSize");
public static readonly ValidationError EventTypeDescriptionMaxSize = ValidationError.New($"EventType's description must be shorter than {TeamConstants.EVENTTYPE_NAME_MAX_SIZE} characters.", "Teams.EventTypes.DescriptionMaxSize");

public static readonly AuthorizationError NotMemberOfTeam = AuthorizationError.New("Not member of the team.", "Teams.NotMember");
public static readonly AuthorizationError UnauthorizedToChangeTeamName = AuthorizationError.New("Not allowed to change team name.", "Teams.NotAllowedToChangeName");
Expand Down
8 changes: 8 additions & 0 deletions src/TeamUp.Domain/Aggregates/Teams/TeamRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public static class TeamRules
static readonly Rule<string> TeamNameMaxSizeRule = name => name.Length <= TeamConstants.TEAM_NAME_MAX_SIZE;
static readonly Rule<string> NicknameMinSizeRule = nickname => nickname.Length >= TeamConstants.NICKNAME_MIN_SIZE;
static readonly Rule<string> NicknameMaxSizeRule = nickname => nickname.Length <= TeamConstants.NICKNAME_MAX_SIZE;
static readonly Rule<string> EventTypeNameMinSizeRule = eventTypeName => eventTypeName.Length >= TeamConstants.EVENTTYPE_NAME_MIN_SIZE;
static readonly Rule<string> EventTypeNameMaxSizeRule = eventTypeName => eventTypeName.Length <= TeamConstants.EVENTTYPE_NAME_MAX_SIZE;
static readonly Rule<string> EventTypeDescriptionMaxRule = eventTypeDescription => eventTypeDescription.Length <= TeamConstants.EVENTTYPE_DESCRIPTION_MAX_SIZE;

public static readonly Rule<TeamRole> RoleIsNotOwner = role => !role.IsOwner();
public static readonly Rule<TeamMember> MemberIsNotTeamOwner = member => !member.Role.IsOwner();
Expand All @@ -23,6 +26,11 @@ public static class TeamRules
public static readonly RuleWithError<string> NicknameMinSize = new(NicknameMinSizeRule, TeamErrors.NicknameMinSize);
public static readonly RuleWithError<string> NicknameMaxSize = new(NicknameMaxSizeRule, TeamErrors.NicknameMaxSize);

public static readonly RuleWithError<string> EventTypeNameMinSize = new(EventTypeNameMinSizeRule, TeamErrors.EventTypeNameMinSize);
public static readonly RuleWithError<string> EventTypeNameMaxSize = new(EventTypeNameMaxSizeRule, TeamErrors.EventTypeNameMaxSize);

public static readonly RuleWithError<string> EventTypeDescriptionMaxSize = new(EventTypeDescriptionMaxRule, TeamErrors.EventTypeDescriptionMaxSize);

public static readonly RuleWithError<TeamMember> MemberCanUpdateTeamRoles = new(MemberCanUpdateTeamRolesRule, TeamErrors.UnauthorizedToUpdateTeamRoles);
public static readonly RuleWithError<TeamMember> MemberCanChangeOwnership = new(MemberIsOwner, TeamErrors.UnauthorizedToChangeTeamOwnership);
public static readonly RuleWithError<TeamMember> MemberCanChangeTeamName = new(MemberIsOwner, TeamErrors.UnauthorizedToChangeTeamName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static EventGenerator WithRandomEventResponses(this EventGenerator genera
.DropMicroSeconds()
.AsUtc())
.RuleFor(er => er.ReplyType, f => f.Random.ArrayElement([ReplyType.Yes, ReplyType.No, ReplyType.Maybe, ReplyType.Delay]))
.RuleFor(er => er.Message, (f, er) => er.ReplyType == ReplyType.Yes ? string.Empty : f.Random.AlphaNumeric(30))
.RuleFor(er => er.Message, (f, er) => er.ReplyType == ReplyType.Yes ? string.Empty : f.Random.Text(1, EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE))
.Generate())
.ToList());
}
Expand All @@ -38,7 +38,7 @@ public static EventGenerator WithEventResponses(this EventGenerator generator, L
.DropMicroSeconds()
.AsUtc())
.RuleFor(er => er.ReplyType, response.Type)
.RuleFor(er => er.Message, (f, er) => er.ReplyType == ReplyType.Yes ? string.Empty : f.Random.AlphaNumeric(30))
.RuleFor(er => er.Message, (f, er) => er.ReplyType == ReplyType.Yes ? string.Empty : f.Random.Text(1, EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE))
.Generate())
.ToList());
}
Expand Down
17 changes: 8 additions & 9 deletions tests/TeamUp.EndToEndTests/DataGenerators/EventGenerators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public sealed class EventGenerators : BaseGenerator
public static readonly EventGenerator Event = new EventGenerator(binder: EventBinder)
.UsePrivateConstructor()
.RuleFor(e => e.Id, f => EventId.FromGuid(f.Random.Guid()))
.RuleFor(e => e.Description, f => f.Random.AlphaNumeric(20))
.RuleFor(e => e.Description, f => f.Random.Text(1, EventConstants.EVENT_DESCRIPTION_MAX_SIZE))
.RuleFor(e => e.MeetTime, f => f.Date.Timespan(TimeSpan.FromHours(24)).DropMicroSeconds())
.RuleFor(e => e.ReplyClosingTimeBeforeMeetTime, f => f.Date.Timespan(TimeSpan.FromHours(24)).DropMicroSeconds())
.RuleFor(e => e.FromUtc, f => f.Date.Between(DateTime.UtcNow.AddDays(3), DateTime.UtcNow.AddMonths(6)).DropMicroSeconds().AsUtc())
Expand All @@ -36,7 +36,7 @@ public sealed class EventGenerators : BaseGenerator
.RuleFor(er => er.Id, f => EventResponseId.FromGuid(f.Random.Guid()));

public static readonly Faker<CreateEventRequest> ValidCreateEventRequest = new Faker<CreateEventRequest>()
.RuleFor(x => x.Description, f => f.Random.AlphaNumeric(20))
.RuleFor(x => x.Description, f => f.Random.Text(1, EventConstants.EVENT_DESCRIPTION_MAX_SIZE))
.RuleFor(x => x.MeetTime, f => f.Date.Timespan(TimeSpan.FromHours(24)).DropMicroSeconds())
.RuleFor(x => x.ReplyClosingTimeBeforeMeetTime, f => f.Date.Timespan(TimeSpan.FromHours(24)).DropMicroSeconds());

Expand All @@ -45,16 +45,15 @@ public sealed class EventGenerators : BaseGenerator
.RuleFor(r => r.Message, (f, r) => r.ReplyType switch
{
ReplyType.Yes => string.Empty,
ReplyType.Maybe => f.Random.Text(1, 80),
ReplyType.Delay => f.Random.Text(0, 80),
ReplyType.No or _ => f.Random.Text(1, 80),
ReplyType.Delay => f.Random.Text(0, EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE),
ReplyType.Maybe or ReplyType.No or _ => f.Random.Text(1, EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE),
});

public sealed class InvalidCreateEventRequest : TheoryData<InvalidRequest<CreateEventRequest>>
{
public InvalidCreateEventRequest()
{
//short description
//empty description
this.Add(x => x.Description, new CreateEventRequest
{
EventTypeId = EventTypeId.FromGuid(default),
Expand Down Expand Up @@ -158,21 +157,21 @@ public InvalidUpsertEventReplyRequest()
this.Add(x => x.Message, new UpsertEventReplyRequest
{
ReplyType = ReplyType.Maybe,
Message = F.Random.AlphaNumeric(100)
Message = F.Random.AlphaNumeric(EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE + 1)
});

//long message
this.Add(x => x.Message, new UpsertEventReplyRequest
{
ReplyType = ReplyType.No,
Message = F.Random.AlphaNumeric(100)
Message = F.Random.AlphaNumeric(EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE + 1)
});

//long message
this.Add(x => x.Message, new UpsertEventReplyRequest
{
ReplyType = ReplyType.Delay,
Message = F.Random.AlphaNumeric(100)
Message = F.Random.AlphaNumeric(EventConstants.EVENT_REPLY_MESSAGE_MAX_SIZE + 1)
});
}
}
Expand Down
12 changes: 6 additions & 6 deletions tests/TeamUp.EndToEndTests/DataGenerators/TeamGenerators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ public sealed class TeamGenerators : BaseGenerator
public static readonly Faker<EventType> EventType = new Faker<EventType>(binder: EventTypeBinder)
.UsePrivateConstructor()
.RuleFor(et => et.Id, f => EventTypeId.FromGuid(f.Random.Guid()))
.RuleFor(et => et.Name, f => f.Random.AlphaNumeric(10))
.RuleFor(et => et.Description, f => f.Random.AlphaNumeric(30));
.RuleFor(et => et.Name, f => f.Random.Text(TeamConstants.EVENTTYPE_NAME_MIN_SIZE, TeamConstants.EVENTTYPE_NAME_MAX_SIZE))
.RuleFor(et => et.Description, f => f.Random.Text(0, TeamConstants.EVENTTYPE_DESCRIPTION_MAX_SIZE));

public static readonly Faker<UpsertEventTypeRequest> ValidUpsertEventTypeRequest = new Faker<UpsertEventTypeRequest>()
.RuleFor(r => r.Name, f => f.Random.AlphaNumeric(10))
.RuleFor(r => r.Description, f => f.Random.AlphaNumeric(40));
.RuleFor(r => r.Name, f => f.Random.Text(TeamConstants.EVENTTYPE_NAME_MIN_SIZE, TeamConstants.EVENTTYPE_NAME_MAX_SIZE))
.RuleFor(r => r.Description, f => f.Random.Text(0, TeamConstants.EVENTTYPE_DESCRIPTION_MAX_SIZE));

public static string GenerateValidTeamName() => F.Random.AlphaNumeric(10);
public static string GenerateValidNickname() => F.Random.AlphaNumeric(10);
public static string GenerateValidTeamName() => F.Random.Text(TeamConstants.TEAM_NAME_MIN_SIZE, TeamConstants.TEAM_NAME_MAX_SIZE);
public static string GenerateValidNickname() => F.Random.Text(TeamConstants.NICKNAME_MIN_SIZE, TeamConstants.NICKNAME_MAX_SIZE);

public sealed class InvalidUpsertEventTypeRequest : TheoryData<InvalidRequest<UpsertEventTypeRequest>>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public InvalidRegisterUserRequests()
this.Add(x => x.Name, new RegisterUserRequest
{
Email = F.Internet.Email(),
Name = F.Lorem.Random.AlphaNumeric(TeamConstants.TEAM_NAME_MAX_SIZE + 1),
Name = F.Random.AlphaNumeric(TeamConstants.TEAM_NAME_MAX_SIZE + 1),
Password = GenerateValidPassword()
});

Expand Down
Loading