Skip to content

Commit

Permalink
Added delete organization API
Browse files Browse the repository at this point in the history
  • Loading branch information
jezzsantos committed Jun 18, 2024
1 parent 9e9b431 commit ce42678
Show file tree
Hide file tree
Showing 36 changed files with 431 additions and 74 deletions.
9 changes: 9 additions & 0 deletions src/Application.Interfaces/Audits.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Application.Interfaces/Audits.resx
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,7 @@
<data name="SubscriptionsApplication_BuyerTransferred" xml:space="preserve">
<value>Subscription.BuyerTransferred</value>
</data>
<data name="OrganizationApplication_OrganizationDeleted" xml:space="preserve">
<value>Organization.Deleted</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Application.Services.Shared;
/// <summary>
/// Defines a service for asserting the permissions of an owning entity of a billing subscription.
/// </summary>
public interface IOwningEntityService
public interface ISubscriptionOwningEntityService
{
/// <summary>
/// Whether the subscription can be cancelled by the <see cref="cancellerId" />
Expand Down
1 change: 1 addition & 0 deletions src/Domain.Common/ValueObjects/SingleValueObjectBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ protected SingleValueObjectBase(TValue value)

TValue ISingleValueObject<TValue>.Value => Value;

[DebuggerStepThrough]
public static implicit operator TValue(SingleValueObjectBase<TValueObject, TValue> valueObject)
{
return valueObject.NotExists() || valueObject.Value.NotExists()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ public BillingSubscribed()

public required string SubscriberId { get; set; }

public required string SubscriptionId { get; set; }
}
17 changes: 17 additions & 0 deletions src/Domain.Events.Shared/Subscriptions/Deleted.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Domain.Common;
using Domain.Common.ValueObjects;
using JetBrains.Annotations;

namespace Domain.Events.Shared.Subscriptions;

public sealed class Deleted : TombstoneDomainEvent
{
public Deleted(Identifier id, Identifier deletedById) : base(id, deletedById)
{
}

[UsedImplicitly]
public Deleted()
{
}
}
53 changes: 53 additions & 0 deletions src/Domain.Shared/Subscriptions/BillingSubscriber.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Common;
using Common.Extensions;
using Domain.Common.ValueObjects;
using Domain.Interfaces;

namespace Domain.Shared.Subscriptions;

public sealed class BillingSubscriber : ValueObjectBase<BillingSubscriber>
{
public static Result<BillingSubscriber, Error> Create(string subscriptionId, string subscriberId)
{
if (subscriptionId.IsNotValuedParameter(nameof(subscriptionId), out var error1))
{
return error1;
}

if (subscriberId.IsNotValuedParameter(nameof(subscriberId), out var error2))
{
return error2;
}

return new BillingSubscriber(subscriptionId, subscriberId);
}

private BillingSubscriber(string subscriptionId, string subscriberId)
{
SubscriptionId = subscriptionId;
SubscriberId = subscriberId;
}

public string SubscriberId { get; }

public string SubscriptionId { get; }

public static ValueObjectFactory<BillingSubscriber> Rehydrate()
{
return (property, _) =>
{
var parts = RehydrateToList(property, false);
return new BillingSubscriber(parts[0]!, parts[1]!);
};
}

protected override IEnumerable<object?> GetAtomicValues()
{
return new object[] { SubscriptionId, SubscriberId };
}

public BillingSubscriber ChangeSubscriber(Identifier subscriberId)
{
return new BillingSubscriber(SubscriptionId, subscriberId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Domain.Shared.Subscriptions;
using EndUsersDomain;
using Created = Domain.Events.Shared.Organizations.Created;
using Deleted = Domain.Events.Shared.Organizations.Deleted;

namespace EndUsersApplication;

Expand All @@ -26,7 +27,7 @@ public async Task<Result<Error>> HandleOrganizationCreatedAsync(ICallerContext c
public async Task<Result<Error>> HandleOrganizationDeletedAsync(ICallerContext caller,
Deleted domainEvent, CancellationToken cancellationToken)
{
return await RemoveMembershipFromDeletedOrganizationAsync(caller, domainEvent.RootId.ToId(),
return await RemoveOwnerMembershipFromDeletedOrganizationAsync(caller, domainEvent.RootId.ToId(),
domainEvent.DeletedById.ToId(), cancellationToken);
}

Expand Down Expand Up @@ -54,7 +55,7 @@ public async Task<Result<Error>> HandleSubscriptionPlanChangedAsync(ICallerConte
domainEvent.OwningEntityId.ToId(), cancellationToken);
}

private async Task<Result<Error>> RemoveMembershipFromDeletedOrganizationAsync(ICallerContext caller,
private async Task<Result<Error>> RemoveOwnerMembershipFromDeletedOrganizationAsync(ICallerContext caller,
Identifier organizationId, Identifier deletedById, CancellationToken cancellationToken)
{
var retrievedDeleter = await _endUserRepository.LoadAsync(deletedById, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Domain.Events.Shared.Organizations;
using Domain.Events.Shared.Subscriptions;
using Created = Domain.Events.Shared.Organizations.Created;
using Deleted = Domain.Events.Shared.Organizations.Deleted;

namespace EndUsersApplication;

Expand All @@ -22,5 +23,4 @@ Task<Result<Error>> HandleOrganizationRoleUnassignedAsync(ICallerContext caller,

Task<Result<Error>> HandleSubscriptionPlanChangedAsync(ICallerContext caller, SubscriptionPlanChanged domainEvent,
CancellationToken cancellationToken);

}
4 changes: 2 additions & 2 deletions src/EndUsersDomain/EndUserRoot.RolesAndFeatures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public Result<Error> AssignMembershipFeatures(Identifier assignerId, Identifier
public Result<Membership, Error> AssignMembershipRoles(EndUserRoot assigner, Identifier organizationId,
Roles rolesToAssign, AssignTenantRolesAction onAssign)
{
if (!IsOrganizationOwner(assigner, organizationId))
if (!IsAnOrganizationOwner(assigner, organizationId))
{
return Error.RoleViolation(Resources.EndUserRoot_NotOrganizationOwner);
}
Expand Down Expand Up @@ -370,7 +370,7 @@ public Result<Membership, Error> UnassignMembershipFeatures(Identifier unassigne
public Result<Membership, Error> UnassignMembershipRoles(EndUserRoot unassigner, Identifier organizationId,
Roles rolesToUnassign, UnassignTenantRolesAction onUnassign)
{
if (!IsOrganizationOwner(unassigner, organizationId))
if (!IsAnOrganizationOwner(unassigner, organizationId))
{
return Error.RoleViolation(Resources.EndUserRoot_NotOrganizationOwner);
}
Expand Down
6 changes: 3 additions & 3 deletions src/EndUsersDomain/EndUserRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public Result<Error> AddMembership(EndUserRoot adder, OrganizationOwnership owne
var skipOwnershipCheck = adder.Id == Id;
if (!skipOwnershipCheck)
{
if (!IsOrganizationOwner(adder, organizationId))
if (!IsAnOrganizationOwner(adder, organizationId))
{
return Error.RoleViolation(Resources.EndUserRoot_NotOrganizationOwner);
}
Expand Down Expand Up @@ -565,7 +565,7 @@ public async Task<Result<Error>> ReInviteGuestAsync(ITokensService tokensService

public Result<Error> RemoveMembership(EndUserRoot remover, Identifier organizationId)
{
if (!IsOrganizationOwner(remover, organizationId))
if (!IsAnOrganizationOwner(remover, organizationId))
{
return Error.RoleViolation(Resources.EndUserRoot_NotOrganizationOwner);
}
Expand Down Expand Up @@ -647,7 +647,7 @@ private static bool IsPlatformOperator(Roles roles)
return roles.HasRole(PlatformRoles.Operations);
}

private static bool IsOrganizationOwner(EndUserRoot assigner, Identifier organizationId)
private static bool IsAnOrganizationOwner(EndUserRoot assigner, Identifier organizationId)
{
var retrieved = assigner.Memberships.FindByOrganizationId(organizationId);
if (!retrieved.HasValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ public OrganizationsApplicationDomainEventHandlersSpec()
_repository.Setup(ar => ar.SaveAsync(It.IsAny<OrganizationRoot>(), It.IsAny<CancellationToken>()))
.Returns((OrganizationRoot root, CancellationToken _) =>
Task.FromResult<Result<OrganizationRoot, Error>>(root));
var subscriptionService = new Mock<ISubscriptionsService>();

_application = new OrganizationsApplication(_recorder.Object, _identifierFactory.Object,
_tenantSettingsService.Object, _tenantSettingService.Object, endUsersService.Object, _imagesService.Object,
_repository.Object);
subscriptionService.Object, _repository.Object);
}

[Fact]
Expand Down Expand Up @@ -192,7 +193,8 @@ public async Task WhenHandleSubscriptionCreatedAsyncAsync_ThenTransfersBillingSu

result.Should().BeSuccess();
_repository.Verify(rep => rep.SaveAsync(It.Is<OrganizationRoot>(root =>
root.BillingSubscriberId == "abuyerid"
root.BillingSubscriber.Value.SubscriberId == "abuyerid"
&& root.BillingSubscriber.Value.SubscriptionId == "asubscriptionid"
), It.IsAny<CancellationToken>()));
}

Expand All @@ -206,7 +208,7 @@ public async Task WhenHandleSubscriptionTransferredAsync_ThenTransfersBillingSub
var org = OrganizationRoot.Create(_recorder.Object, _identifierFactory.Object, _tenantSettingService.Object,
OrganizationOwnership.Shared, "anownerid".ToId(), UserClassification.Person,
DisplayName.Create("aname").Value).Value;
org.SubscribeBilling("atransfererid".ToId());
org.SubscribeBilling("asubscriptionid".ToId(), "atransfererid".ToId());
_repository.Setup(rep => rep.LoadAsync(It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(org);

Expand All @@ -215,7 +217,9 @@ public async Task WhenHandleSubscriptionTransferredAsync_ThenTransfersBillingSub

result.Should().BeSuccess();
_repository.Verify(rep => rep.SaveAsync(It.Is<OrganizationRoot>(root =>
root.BillingSubscriberId == "atransfereeid"
root.BillingSubscriber.Value.SubscriberId == "atransfereeid"
&& root.BillingSubscriber.Value.SubscriptionId == "asubscriptionid"
), It.IsAny<CancellationToken>()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ public OrganizationsApplicationSubscriptionOwningEntitySpec()
_repository.Setup(ar => ar.SaveAsync(It.IsAny<OrganizationRoot>(), It.IsAny<CancellationToken>()))
.Returns((OrganizationRoot root, CancellationToken _) =>
Task.FromResult<Result<OrganizationRoot, Error>>(root));
var subscriptionService = new Mock<ISubscriptionsService>();

_application = new OrganizationsApplication(_recorder.Object, _identifierFactory.Object,
tenantSettingsService.Object, _tenantSettingService.Object, _endUsersService.Object, imagesService.Object,
_repository.Object);
subscriptionService.Object, _repository.Object);
}

[Fact]
Expand Down Expand Up @@ -105,7 +106,7 @@ public async Task WhenCanCancelSubscriptionAsync_ThenReturnsPermission()
var organization = OrganizationRoot.Create(_recorder.Object, _identifierFactory.Object,
_tenantSettingService.Object, OrganizationOwnership.Personal, "auserid".ToId(), UserClassification.Person,
DisplayName.Create("aname").Value).Value;
organization.SubscribeBilling("acancellerid".ToId());
organization.SubscribeBilling("asubscriptionid".ToId(), "acancellerid".ToId());
_repository.Setup(rep => rep.LoadAsync(It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(organization);
_endUsersService.Setup(eus =>
Expand Down Expand Up @@ -178,7 +179,7 @@ public async Task WhenCanChangeSubscriptionPlanAsync_ThenReturnsPermission()
var organization = OrganizationRoot.Create(_recorder.Object, _identifierFactory.Object,
_tenantSettingService.Object, OrganizationOwnership.Personal, "auserid".ToId(), UserClassification.Person,
DisplayName.Create("aname").Value).Value;
organization.SubscribeBilling("amodifierid".ToId());
organization.SubscribeBilling("asubscriptionid".ToId(), "amodifierid".ToId());
_repository.Setup(rep => rep.LoadAsync(It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(organization);
_endUsersService.Setup(eus =>
Expand Down Expand Up @@ -253,7 +254,7 @@ public async Task WhenCanTransferSubscriptionAsync_ThenReturnsPermission()
var organization = OrganizationRoot.Create(_recorder.Object, _identifierFactory.Object,
_tenantSettingService.Object, OrganizationOwnership.Personal, "auserid".ToId(), UserClassification.Person,
DisplayName.Create("aname").Value).Value;
organization.SubscribeBilling("atransfererid".ToId());
organization.SubscribeBilling("asubscriptionid".ToId(), "atransfererid".ToId());
_repository.Setup(rep => rep.LoadAsync(It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(organization);
_endUsersService.Setup(eus =>
Expand Down Expand Up @@ -304,7 +305,7 @@ public async Task WhenCanUnsubscribeAsync_ThenReturnsPermission()
var organization = OrganizationRoot.Create(_recorder.Object, _identifierFactory.Object,
_tenantSettingService.Object, OrganizationOwnership.Personal, "auserid".ToId(), UserClassification.Person,
DisplayName.Create("aname").Value).Value;
organization.SubscribeBilling("anunsubsciberid".ToId());
organization.SubscribeBilling("asubscriptionid".ToId(), "anunsubsciberid".ToId());
_repository.Setup(rep => rep.LoadAsync(It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(organization);

Expand Down Expand Up @@ -359,7 +360,7 @@ public async Task WhenCanViewSubscriptionAsync_ThenReturnsPermission()
var organization = OrganizationRoot.Create(_recorder.Object, _identifierFactory.Object,
_tenantSettingService.Object, OrganizationOwnership.Personal, "auserid".ToId(), UserClassification.Person,
DisplayName.Create("aname").Value).Value;
organization.SubscribeBilling("aviewerid".ToId());
organization.SubscribeBilling("asubscriptionid".ToId(), "aviewerid".ToId());
_repository.Setup(rep => rep.LoadAsync(It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(organization);
_endUsersService.Setup(eus =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class OrganizationsApplicationSpec
private readonly Mock<IOrganizationRepository> _repository;
private readonly Mock<ITenantSettingService> _tenantSettingService;
private readonly Mock<ITenantSettingsService> _tenantSettingsService;
private readonly Mock<ISubscriptionsService> _subscriptionsService;

public OrganizationsApplicationSpec()
{
Expand All @@ -55,13 +56,14 @@ public OrganizationsApplicationSpec()
.Returns((string value) => value);
_endUsersService = new Mock<IEndUsersService>();
_imagesService = new Mock<IImagesService>();
_subscriptionsService = new Mock<ISubscriptionsService>();
_repository = new Mock<IOrganizationRepository>();
_repository.Setup(ar => ar.SaveAsync(It.IsAny<OrganizationRoot>(), It.IsAny<CancellationToken>()))
.Returns((OrganizationRoot root, CancellationToken _) =>
Task.FromResult<Result<OrganizationRoot, Error>>(root));

_application = new OrganizationsApplication(_recorder.Object, _idFactory.Object, _tenantSettingsService.Object,
_tenantSettingService.Object, _endUsersService.Object, _imagesService.Object,
_tenantSettingService.Object, _endUsersService.Object, _imagesService.Object, _subscriptionsService.Object,
_repository.Object);
}

Expand Down Expand Up @@ -776,11 +778,34 @@ public async Task WhenDeleteOrganizationAsync_ThenDeletes()
{
_caller.Setup(cc => cc.Roles)
.Returns(new ICallerContext.CallerRoles([], new[] { TenantRoles.Owner }));
_caller.Setup(cc => cc.CallerId)
.Returns("acallerid");
var org = OrganizationRoot.Create(_recorder.Object, _idFactory.Object, _tenantSettingService.Object,
OrganizationOwnership.Personal, "auserid".ToId(), UserClassification.Person,
DisplayName.Create("aname").Value).Value;
org.SubscribeBilling("asubscriptionid".ToId(), "acallerid".ToId());
_repository.Setup(rep => rep.LoadAsync(It.IsAny<Identifier>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(org);
_subscriptionsService.Setup(ss =>
ss.GetSubscriptionAsync(It.IsAny<ICallerContext>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new SubscriptionWithPlan
{
Id = "asubscriptionid",
Invoice = new InvoiceSummary
{
Currency = "acurrency"
},
PaymentMethod = new SubscriptionPaymentMethod(),
Period = new PlanPeriod(),
Plan = new SubscriptionPlan
{
Id = "aplanid"
},
SubscriptionId = "asubscriptionid",
BuyerId = "abuyerid",
OwningEntityId = "anowningentityid",
CanBeUnsubscribed = true
});

var result =
await _application.DeleteOrganizationAsync(_caller.Object, "anorganizationid", CancellationToken.None);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public async Task<Result<Error>> HandleSubscriptionCreatedAsync(ICallerContext c
Created domainEvent, CancellationToken cancellationToken)
{
return await HandleCreatedSubscriptionAsync(caller,
domainEvent.OwningEntityId.ToId(), domainEvent.BuyerId.ToId(), cancellationToken);
domainEvent.RootId.ToId(), domainEvent.OwningEntityId.ToId(), domainEvent.BuyerId.ToId(),
cancellationToken);
}

public async Task<Result<Error>> HandleSubscriptionTransferredAsync(ICallerContext caller,
Expand All @@ -64,7 +65,7 @@ public async Task<Result<Error>> HandleEndUserMembershipRemovedAsync(ICallerCont
domainEvent.OrganizationId.ToId(), cancellationToken);
}

private async Task<Result<Error>> HandleCreatedSubscriptionAsync(ICallerContext caller,
private async Task<Result<Error>> HandleCreatedSubscriptionAsync(ICallerContext caller, Identifier subscriptionId,
Identifier organizationId, Identifier billingSubscriberId, CancellationToken cancellationToken)
{
var retrieved = await _repository.LoadAsync(organizationId, cancellationToken);
Expand All @@ -74,7 +75,7 @@ private async Task<Result<Error>> HandleCreatedSubscriptionAsync(ICallerContext
}

var org = retrieved.Value;
var subscribed = org.SubscribeBilling(billingSubscriberId);
var subscribed = org.SubscribeBilling(subscriptionId, billingSubscriberId);
if (subscribed.IsFailure)
{
return subscribed.Error;
Expand Down
Loading

0 comments on commit ce42678

Please sign in to comment.