diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml index 492edb433..0a5b2acb0 100644 --- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml +++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml @@ -609,23 +609,24 @@ modules: use: downstream downstream: organizations-service/organizations auth: true + + - upstream: /{organizationId}/children + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + auth: true - - upstream: /organizer/{organizationId}/organizer + - upstream: /{organizationId}/organizer method: POST use: downstream downstream: organizations-service/organizations/{organizationId}/organizer auth: true - - upstream: /organizer/{organizationId}/organizer/{organizerId} + - upstream: /{organizationId}/organizer/{organizerId} method: DELETE use: downstream downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId} auth: true - - - upstream: / - method: GET - use: downstream - downstream: organizations-service/organizations - upstream: /{organizationId} method: GET @@ -647,6 +648,11 @@ modules: use: downstream downstream: organizations-service/organizations/{organizationId}/children + - upstream: /{organizationId}/children/all + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children/all + - upstream: /organizer/{organizerId} method: GET use: downstream diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs index b894543e7..28f5788a4 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs @@ -222,4 +222,4 @@ private void ChangeState(State state) public bool IsOrganizer(Guid organizerId) => Organizer.Id == organizerId; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs index a2a623dda..809763244 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs @@ -37,8 +37,11 @@ public static async Task Main(string[] args) .Get>("organizations/organizer/{organizerId}") .Get>("organizations/root") .Get>("organizations/{organizationId}/children") - .Post("organizations", + .Get>("organizations/{organizationId}/children/all") + .Post("organizations", afterDispatch: (cmd, ctx) => ctx.Response.Created($"organizations/root")) + .Post("organizations/{organizationId}/children", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"organizations/{cmd.OrganizationId}")) .Post("organizations/{organizationId}/organizer") .Delete("organizations/{organizationId}/organizer/{organizerId}") )) diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AddOrganizerToOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AddOrganizerToOrganization.cs index d50e78262..47077c04b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AddOrganizerToOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AddOrganizerToOrganization.cs @@ -4,11 +4,13 @@ namespace MiniSpace.Services.Organizations.Application.Commands { public class AddOrganizerToOrganization: ICommand { + public Guid RootOrganizationId { get; set; } public Guid OrganizationId { get; set; } public Guid OrganizerId { get; set; } - public AddOrganizerToOrganization(Guid organizationId, Guid organizerId) + public AddOrganizerToOrganization(Guid rootOrganizationId, Guid organizationId, Guid organizerId) { + RootOrganizationId = rootOrganizationId; OrganizationId = organizationId; OrganizerId = organizerId; } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AddOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganization.cs similarity index 65% rename from MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AddOrganization.cs rename to MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganization.cs index dca309f06..083448c53 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AddOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganization.cs @@ -2,16 +2,18 @@ namespace MiniSpace.Services.Organizations.Application.Commands { - public class AddOrganization: ICommand + public class CreateOrganization: ICommand { public Guid OrganizationId { get; } public string Name { get; } + public Guid RootId { get; } public Guid ParentId { get; } - public AddOrganization(Guid organizationId, string name, Guid parentId) + public CreateOrganization(Guid organizationId, string name, Guid rootId, Guid parentId) { OrganizationId = organizationId == Guid.Empty ? Guid.NewGuid() : organizationId; Name = name; + RootId = rootId; ParentId = parentId; } } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateRootOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateRootOrganization.cs new file mode 100644 index 000000000..a8e630776 --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateRootOrganization.cs @@ -0,0 +1,16 @@ +using Convey.CQRS.Commands; + +namespace MiniSpace.Services.Organizations.Application.Commands +{ + public class CreateRootOrganization: ICommand + { + public Guid OrganizationId { get; } + public string Name { get; } + + public CreateRootOrganization(Guid organizationId, string name) + { + OrganizationId = organizationId == Guid.Empty ? Guid.NewGuid() : organizationId; + Name = name; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AddOrganizerToOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AddOrganizerToOrganizationHandler.cs index d38e6b2a4..d5e3e59cb 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AddOrganizerToOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AddOrganizerToOrganizationHandler.cs @@ -31,8 +31,14 @@ public async Task HandleAsync(AddOrganizerToOrganization command, CancellationTo throw new Exceptions.UnauthorizedAccessException("admin"); } - var organization = await _organizationRepository.GetAsync(command.OrganizationId); - if (organization is null) + var root = await _organizationRepository.GetAsync(command.RootOrganizationId); + if (root is null) + { + throw new RootOrganizationNotFoundException(command.RootOrganizationId); + } + + var organization = root.GetSubOrganization(command.OrganizationId); + if (organization == null) { throw new OrganizationNotFoundException(command.OrganizationId); } @@ -44,7 +50,7 @@ public async Task HandleAsync(AddOrganizerToOrganization command, CancellationTo } organization.AddOrganizer(command.OrganizerId); - await _organizationRepository.UpdateAsync(organization); + await _organizationRepository.UpdateAsync(root); await _messageBroker.PublishAsync(new OrganizerAddedToOrganization(organization.Id, organizer.Id)); } } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs new file mode 100644 index 000000000..056f13b66 --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs @@ -0,0 +1,55 @@ +using Convey.CQRS.Commands; +using MiniSpace.Services.Organizations.Application.Events; +using MiniSpace.Services.Organizations.Application.Exceptions; +using MiniSpace.Services.Organizations.Application.Services; +using MiniSpace.Services.Organizations.Core.Entities; +using MiniSpace.Services.Organizations.Core.Repositories; + +namespace MiniSpace.Services.Organizations.Application.Commands.Handlers +{ + public class CreateOrganizationHandler : ICommandHandler + { + private readonly IOrganizationRepository _organizationRepository; + private readonly IAppContext _appContext; + private readonly IMessageBroker _messageBroker; + + public CreateOrganizationHandler(IOrganizationRepository organizationRepository, IAppContext appContext, + IMessageBroker messageBroker) + { + _organizationRepository = organizationRepository; + _appContext = appContext; + _messageBroker = messageBroker; + } + + public async Task HandleAsync(CreateOrganization command, CancellationToken cancellationToken) + { + var identity = _appContext.Identity; + if(identity.IsAuthenticated && !identity.IsAdmin) + { + throw new Exceptions.UnauthorizedAccessException("admin"); + } + + var root = await _organizationRepository.GetAsync(command.RootId); + if(root is null) + { + throw new RootOrganizationNotFoundException(command.RootId); + } + + var parent = root.GetSubOrganization(command.ParentId); + if(parent is null) + { + throw new ParentOrganizationNotFoundException(command.ParentId); + } + + if (string.IsNullOrWhiteSpace(command.Name)) + { + throw new InvalidOrganizationNameException(command.Name); + } + + var organization = new Organization(command.OrganizationId, command.Name); + parent.AddSubOrganization(organization); + await _organizationRepository.UpdateAsync(root); + await _messageBroker.PublishAsync(new OrganizationCreated(organization.Id, organization.Name, parent.Id)); + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AddOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateRootOrganizationHandler.cs similarity index 52% rename from MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AddOrganizationHandler.cs rename to MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateRootOrganizationHandler.cs index ede5a3d92..1d21c515b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AddOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateRootOrganizationHandler.cs @@ -1,41 +1,42 @@ using Convey.CQRS.Commands; +using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; +using MiniSpace.Services.Organizations.Application.Services; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; namespace MiniSpace.Services.Organizations.Application.Commands.Handlers { - public class AddOrganizationHandler : ICommandHandler + public class CreateRootOrganizationHandler : ICommandHandler { private readonly IOrganizationRepository _organizationRepository; private readonly IAppContext _appContext; + private readonly IMessageBroker _messageBroker; - public AddOrganizationHandler(IOrganizationRepository organizationRepository, IAppContext appContext) + public CreateRootOrganizationHandler(IOrganizationRepository organizationRepository, IAppContext appContext, + IMessageBroker messageBroker) { _organizationRepository = organizationRepository; _appContext = appContext; + _messageBroker = messageBroker; } - public async Task HandleAsync(AddOrganization command, CancellationToken cancellationToken) + public async Task HandleAsync(CreateRootOrganization command, CancellationToken cancellationToken) { var identity = _appContext.Identity; if(identity.IsAuthenticated && !identity.IsAdmin) { throw new Exceptions.UnauthorizedAccessException("admin"); } - - var organization = new Organization(command.OrganizationId, command.Name, command.ParentId); - if(command.ParentId != Guid.Empty) + + if (string.IsNullOrWhiteSpace(command.Name)) { - var parent = await _organizationRepository.GetAsync(command.ParentId); - if(parent is null) - { - throw new ParentOrganizationNotFoundException(command.ParentId); - } - parent.MakeParent(); - await _organizationRepository.UpdateAsync(parent); + throw new InvalidOrganizationNameException(command.Name); } + + var organization = new Organization(command.OrganizationId, command.Name); await _organizationRepository.AddAsync(organization); + await _messageBroker.PublishAsync(new RootOrganizationCreated(organization.Id, organization.Name)); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RemoveOrganizerFromOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RemoveOrganizerFromOrganizationHandler.cs index 341fca7c5..d20145909 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RemoveOrganizerFromOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RemoveOrganizerFromOrganizationHandler.cs @@ -29,8 +29,14 @@ public async Task HandleAsync(RemoveOrganizerFromOrganization command, Cancellat throw new Exceptions.UnauthorizedAccessException("admin"); } - var organization = await _organizationRepository.GetAsync(command.OrganizationId); - if(organization is null) + var root = await _organizationRepository.GetAsync(command.RootOrganizationId); + if (root is null) + { + throw new RootOrganizationNotFoundException(command.RootOrganizationId); + } + + var organization = root.GetSubOrganization(command.OrganizationId); + if (organization == null) { throw new OrganizationNotFoundException(command.OrganizationId); } @@ -42,7 +48,7 @@ public async Task HandleAsync(RemoveOrganizerFromOrganization command, Cancellat } organization.RemoveOrganizer(organizer.Id); - await _organizationRepository.UpdateAsync(organization); + await _organizationRepository.UpdateAsync(root); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RemoveOrganizerFromOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RemoveOrganizerFromOrganization.cs index 8f553940e..100ada5dd 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RemoveOrganizerFromOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RemoveOrganizerFromOrganization.cs @@ -4,11 +4,13 @@ namespace MiniSpace.Services.Organizations.Application.Commands { public class RemoveOrganizerFromOrganization : ICommand { + public Guid RootOrganizationId { get; set; } public Guid OrganizationId { get; set; } public Guid OrganizerId { get; set; } - public RemoveOrganizerFromOrganization(Guid organizationId, Guid organizerId) + public RemoveOrganizerFromOrganization(Guid rootOrganizationId, Guid organizationId, Guid organizerId) { + RootOrganizationId = rootOrganizationId; OrganizationId = organizationId; OrganizerId = organizerId; } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDetailsDto.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDetailsDto.cs index a25f6c34e..2c7d353bd 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDetailsDto.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDetailsDto.cs @@ -1,4 +1,5 @@ using System.Collections; +using MiniSpace.Services.Organizations.Core.Entities; namespace MiniSpace.Services.Organizations.Application.DTO { @@ -6,8 +7,18 @@ public class OrganizationDetailsDto { public Guid Id { get; set; } public string Name { get; set; } - public Guid ParentId { get; set; } - public bool IsLeaf { get; set; } public IEnumerable Organizers { get; set; } + + public OrganizationDetailsDto() + { + + } + + public OrganizationDetailsDto(Organization organization) + { + Id = organization.Id; + Name = organization.Name; + Organizers = organization.Organizers.Select(o => o.Id); + } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDto.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDto.cs index 50ee020cc..fc71ebfea 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDto.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Dto/OrganizationDto.cs @@ -1,11 +1,22 @@ +using MiniSpace.Services.Organizations.Core.Entities; + namespace MiniSpace.Services.Organizations.Application.DTO { public class OrganizationDto { public Guid Id { get; set; } public string Name { get; set; } - public Guid ParentId { get; set; } - public bool IsLeaf { get; set; } + + public OrganizationDto() + { + + } + + public OrganizationDto (Organization organization) + { + Id = organization.Id; + Name = organization.Name; + } } } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationCreated.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationCreated.cs new file mode 100644 index 000000000..7eebf8d42 --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationCreated.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace MiniSpace.Services.Organizations.Application.Events +{ + public class OrganizationCreated: IEvent + { + public Guid OrganizationId { get; } + public string Name { get; } + public Guid ParentId { get; } + + public OrganizationCreated(Guid organizationId, string name, Guid parentId) + { + OrganizationId = organizationId; + Name = name; + ParentId = parentId; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RootOrganizationCreated.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RootOrganizationCreated.cs new file mode 100644 index 000000000..c22ace7ba --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RootOrganizationCreated.cs @@ -0,0 +1,16 @@ +using Convey.CQRS.Events; + +namespace MiniSpace.Services.Organizations.Application.Events +{ + public class RootOrganizationCreated: IEvent + { + public Guid OrganizationId { get; } + public string Name { get; } + + public RootOrganizationCreated(Guid organizationId, string name) + { + OrganizationId = organizationId; + Name = name; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Exceptions/InvalidOrganizationNameException.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Exceptions/InvalidOrganizationNameException.cs new file mode 100644 index 000000000..14f29be86 --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Exceptions/InvalidOrganizationNameException.cs @@ -0,0 +1,13 @@ +namespace MiniSpace.Services.Organizations.Application.Exceptions +{ + public class InvalidOrganizationNameException : AppException + { + public override string Code { get; } = "invalid_organization_name"; + public string Name { get; } + + public InvalidOrganizationNameException(string name) : base($"Invalid organization name: {name}.") + { + Name = name; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Exceptions/RootOrganizationNotFoundException.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Exceptions/RootOrganizationNotFoundException.cs new file mode 100644 index 000000000..afe37377d --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Exceptions/RootOrganizationNotFoundException.cs @@ -0,0 +1,13 @@ +namespace MiniSpace.Services.Organizations.Application.Exceptions +{ + public class RootOrganizationNotFoundException : AppException + { + public override string Code { get; } = "root_organization_not_found"; + public Guid OrganizationId { get; } + + public RootOrganizationNotFoundException(Guid organizationId) : base($"Root organization with ID: '{organizationId}' was not found.") + { + OrganizationId = organizationId; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetAllChildrenOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetAllChildrenOrganizations.cs new file mode 100644 index 000000000..be66f4b12 --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetAllChildrenOrganizations.cs @@ -0,0 +1,11 @@ +using Convey.CQRS.Queries; +using MiniSpace.Services.Organizations.Application.DTO; + +namespace MiniSpace.Services.Organizations.Application.Queries +{ + public class GetAllChildrenOrganizations: IQuery> + { + public Guid OrganizationId { get; set; } + public Guid RootId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs index a71d10e0a..2212c82be 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs @@ -5,6 +5,7 @@ namespace MiniSpace.Services.Organizations.Application.Queries { public class GetChildrenOrganizations: IQuery> { - public Guid ParentId { get; set; } + public Guid OrganizationId { get; set; } + public Guid RootId { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs index cbcb88480..14b6b9b70 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs @@ -6,5 +6,6 @@ namespace MiniSpace.Services.Organizations.Application.Queries public class GetOrganization : IQuery { public Guid OrganizationId { get; set; } + public Guid RootId { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs index 46b266a07..abf38f0c6 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs @@ -6,5 +6,6 @@ namespace MiniSpace.Services.Organizations.Application.Queries public class GetOrganizationDetails : IQuery { public Guid OrganizationId { get; set; } + public Guid RootId { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizerOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizerOrganizations.cs index 327e8da53..b48d8f014 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizerOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizerOrganizations.cs @@ -6,6 +6,7 @@ namespace MiniSpace.Services.Organizations.Application.Queries public class GetOrganizerOrganizations: IQuery> { public Guid OrganizerId { get; set; } + public Guid RootId { get; set; } } } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Core/Entities/Organization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Core/Entities/Organization.cs index ff915096d..05ff15b83 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Core/Entities/Organization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Core/Entities/Organization.cs @@ -5,9 +5,8 @@ namespace MiniSpace.Services.Organizations.Core.Entities public class Organization : AggregateRoot { private ISet _organizers = new HashSet(); + private ISet _subOrganizations = new HashSet(); public string Name { get; private set; } - public Guid ParentId { get; private set; } - public bool IsLeaf { get; private set; } public IEnumerable Organizers { @@ -15,13 +14,28 @@ public IEnumerable Organizers private set => _organizers = new HashSet(value); } - public Organization(Guid id, string name, Guid parentId, bool isLeaf = true, IEnumerable organizers = null) + public IEnumerable SubOrganizations + { + get => _subOrganizations; + private set => _subOrganizations = new HashSet(value); + } + + public Organization(Guid id, string name, IEnumerable organizationOrganizers = null, + IEnumerable organizations = null) { Id = id; Name = name; - ParentId = parentId; - IsLeaf = isLeaf; - Organizers = organizers ?? Enumerable.Empty(); + Organizers = organizationOrganizers ?? Enumerable.Empty(); + SubOrganizations = organizations ?? Enumerable.Empty(); + } + + public void AddOrganizer(Guid organizerId) + { + if(Organizers.Any(x => x.Id == organizerId)) + { + throw new OrganizerAlreadyAddedToOrganizationException(organizerId, Id); + } + _organizers.Add(new Organizer(organizerId)); } public void RemoveOrganizer(Guid organizerId) @@ -34,18 +48,65 @@ public void RemoveOrganizer(Guid organizerId) _organizers.Remove(organizer); } - public void AddOrganizer(Guid organizerId) + public Organization GetSubOrganization(Guid id) { - if(Organizers.Any(x => x.Id == organizerId)) + if (Id == id) { - throw new OrganizerAlreadyAddedToOrganizationException(organizerId, Id); + return this; } - _organizers.Add(new Organizer(organizerId)); + + foreach (var subOrg in SubOrganizations) + { + var result = subOrg.GetSubOrganization(id); + if (result != null) + { + return result; + } + } + + return null; } + public void AddSubOrganization(Organization organization) + => _subOrganizations.Add(organization); + public static List FindOrganizations(Guid targetOrganizerId, Organization rootOrganization) + { + var organizations = new List(); + FindOrganizationsRecursive(targetOrganizerId, rootOrganization, organizations); + return organizations; + } + + private static void FindOrganizationsRecursive(Guid targetOrganizerId, Organization currentOrganization, + ICollection organizations) + { + if (currentOrganization.Organizers.Any(x => x.Id == targetOrganizerId)) + { + organizations.Add(currentOrganization); + } + + foreach (var subOrg in currentOrganization.SubOrganizations) + { + FindOrganizationsRecursive(targetOrganizerId, subOrg, organizations); + } + } - public void MakeParent() - => IsLeaf = false; + public static List FindAllChildrenOrganizations(Organization rootOrganization) + { + var organizations = new List(); + FindAllChildrenOrganizationsRecursive(rootOrganization, organizations); + return organizations; + } + + private static void FindAllChildrenOrganizationsRecursive(Organization currentOrganization, + ICollection organizations) + { + organizations.Add(currentOrganization.Id); + + foreach (var subOrg in currentOrganization.SubOrganizations) + { + FindAllChildrenOrganizationsRecursive(subOrg, organizations); + } + } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs index f020d0ce7..efbe7351e 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs @@ -88,7 +88,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app .UseMetrics() .UseCertificateAuthentication() .UseRabbitMq() - .SubscribeCommand() + .SubscribeCommand() .SubscribeCommand() .SubscribeCommand() .SubscribeEvent() diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs index 827297347..c104dfd46 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs @@ -9,7 +9,7 @@ internal static class Extensions { public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) { - var assembly = typeof(AddOrganization).Assembly; + var assembly = typeof(CreateOrganization).Assembly; builder.Services.AddSingleton(new MessageToLogTemplateMapper()); diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 598978f10..e95de21b2 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -10,9 +10,15 @@ private static IReadOnlyDictionary MessageTemplates => new Dictionary { { - typeof(AddOrganization), new HandlerLogTemplate + typeof(CreateRootOrganization), new HandlerLogTemplate { - After = "Added a new organization with id: {OrganizationId}." + After = "Created a new root organization with id: {OrganizationId}." + } + }, + { + typeof(CreateOrganization), new HandlerLogTemplate + { + After = "Added a new child organization with id: {OrganizationId} for parent with id: {ParentId}." } }, { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs index 6322ec5c9..343da89eb 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/Extensions.cs @@ -6,25 +6,22 @@ namespace MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents public static class Extensions { public static Organization AsEntity(this OrganizationDocument document) - => new Organization(document.Id, document.Name, document.ParentId, document.IsLeaf, document.Organizers); + => new Organization(document.Id, document.Name, document.Organizers, document.SubOrganizations.Select(o => o.AsEntity())); public static OrganizationDocument AsDocument(this Organization entity) => new OrganizationDocument() { Id = entity.Id, Name = entity.Name, - ParentId = entity.ParentId, - IsLeaf = entity.IsLeaf, - Organizers = entity.Organizers + Organizers = entity.Organizers, + SubOrganizations = entity.SubOrganizations.Select(o => o.AsDocument()) }; public static OrganizationDto AsDto(this OrganizationDocument document) => new OrganizationDto() { Id = document.Id, - Name = document.Name, - ParentId = document.ParentId, - IsLeaf = document.IsLeaf + Name = document.Name }; public static OrganizationDetailsDto AsDetailsDto(this OrganizationDocument document) @@ -32,8 +29,6 @@ public static OrganizationDetailsDto AsDetailsDto(this OrganizationDocument docu { Id = document.Id, Name = document.Name, - ParentId = document.ParentId, - IsLeaf = document.IsLeaf, Organizers = document.Organizers.Select(x => x.Id) }; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs index 24946e59b..5e7a31dcb 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs @@ -7,8 +7,7 @@ public class OrganizationDocument: IIdentifiable { public Guid Id { get; set; } public string Name { get; set; } - public Guid ParentId { get; set; } - public bool IsLeaf { get; set; } public IEnumerable Organizers { get; set; } + public IEnumerable SubOrganizations { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetAllChildrenOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetAllChildrenOrganizationsHandler.cs new file mode 100644 index 000000000..149cc4237 --- /dev/null +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetAllChildrenOrganizationsHandler.cs @@ -0,0 +1,29 @@ +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using MiniSpace.Services.Organizations.Application.Queries; +using MiniSpace.Services.Organizations.Core.Entities; +using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; + +namespace MiniSpace.Services.Organizations.Infrastructure.Mongo.Queries.Handlers +{ + public class GetAllChildrenOrganizationsHandler : IQueryHandler> + { + private readonly IMongoRepository _repository; + + public GetAllChildrenOrganizationsHandler(IMongoRepository repository) + => _repository = repository; + + public async Task> HandleAsync(GetAllChildrenOrganizations query, CancellationToken cancellationToken) + { + var root = await _repository.GetAsync(o => o.Id == query.RootId); + var organization = root?.AsEntity().GetSubOrganization(query.OrganizationId); + var result = new List(); + if (organization != null) + { + result.AddRange(Organization.FindAllChildrenOrganizations(organization)); + } + + return result; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs index ab868a7de..e8406e010 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs @@ -16,9 +16,15 @@ public GetChildrenOrganizationsHandler(IMongoRepository> HandleAsync(GetChildrenOrganizations query, CancellationToken cancellationToken) { - var organizations = await _repository.FindAsync(o => o.ParentId == query.ParentId); + var root = await _repository.GetAsync(o => o.Id == query.RootId); + if (root == null) + { + return Enumerable.Empty(); + } - return organizations.Select(o => o.AsDto()); + var parent = root.AsEntity().GetSubOrganization(query.OrganizationId); + return parent == null ? Enumerable.Empty() + : parent.SubOrganizations.Select(o => new OrganizationDto(o)); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs index dd249addd..f420f0238 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs @@ -18,9 +18,9 @@ public GetOrganizationDetailsHandler(IMongoRepository HandleAsync(GetOrganizationDetails query, CancellationToken cancellationToken) { - var organization = await _repository.GetAsync(query.OrganizationId); - - return organization?.AsDetailsDto(); + var root = await _repository.GetAsync(o => o.Id == query.RootId); + var organization = root?.AsEntity().GetSubOrganization(query.OrganizationId); + return organization == null ? null : new OrganizationDetailsDto(organization); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs index d8cb184f9..c01bbd2e9 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs @@ -17,9 +17,9 @@ public GetOrganizationHandler(IMongoRepository repos public async Task HandleAsync(GetOrganization query, CancellationToken cancellationToken) { - var organization = await _repository.GetAsync(query.OrganizationId); - - return organization?.AsDto(); + var root = await _repository.GetAsync(o => o.Id == query.RootId); + var organization = root?.AsEntity().GetSubOrganization(query.OrganizationId); + return organization == null ? null : new OrganizationDto(organization); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizerOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizerOrganizationsHandler.cs index 0a587fbf6..16977a82c 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizerOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizerOrganizationsHandler.cs @@ -27,10 +27,15 @@ public async Task> HandleAsync(GetOrganizerOrganiza return Enumerable.Empty(); } - var organizations = await _repository - .FindAsync(o => o.Organizers.Any(x => x.Id == query.OrganizerId)); + var roots = (await _repository.FindAsync(o => true)).Select(o =>o.AsEntity()); + var organizerOrganizations = new List(); + foreach (var root in roots) + { + var organizations = Organization.FindOrganizations(query.OrganizerId, root); + organizerOrganizations.AddRange(organizations); + } - return organizations.Select(o => o.AsDto()); + return organizerOrganizations.Select(o => new OrganizationDto(o)); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs index a4ca6c3c4..87f1ab43e 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs @@ -16,6 +16,6 @@ public GetRootOrganizationsHandler(IMongoRepository } public async Task> HandleAsync(GetRootOrganizations query, CancellationToken cancellationToken) - => (await _repository.FindAsync(o => o.ParentId == Guid.Empty)).Select(o => o.AsDto()); + => (await _repository.FindAsync(o => true)).Select(o =>o.AsDto()); } } \ No newline at end of file