@multitenantkit/domain 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/organization-memberships/index.d.ts +3 -0
- package/dist/organization-memberships/index.d.ts.map +1 -0
- package/dist/organization-memberships/index.js +3 -0
- package/dist/organization-memberships/index.js.map +1 -0
- package/dist/organization-memberships/use-cases/accept-organization-invitation/AcceptOrganizationInvitation.d.ts +26 -0
- package/dist/organization-memberships/use-cases/accept-organization-invitation/AcceptOrganizationInvitation.d.ts.map +1 -0
- package/dist/organization-memberships/use-cases/accept-organization-invitation/AcceptOrganizationInvitation.js +85 -0
- package/dist/organization-memberships/use-cases/accept-organization-invitation/AcceptOrganizationInvitation.js.map +1 -0
- package/dist/organization-memberships/use-cases/add-organization-member/AddOrganizationMember.d.ts +19 -0
- package/dist/organization-memberships/use-cases/add-organization-member/AddOrganizationMember.d.ts.map +1 -0
- package/dist/organization-memberships/use-cases/add-organization-member/AddOrganizationMember.js +126 -0
- package/dist/organization-memberships/use-cases/add-organization-member/AddOrganizationMember.js.map +1 -0
- package/dist/organization-memberships/use-cases/index.d.ts +6 -0
- package/dist/organization-memberships/use-cases/index.d.ts.map +1 -0
- package/dist/organization-memberships/use-cases/index.js +8 -0
- package/dist/organization-memberships/use-cases/index.js.map +1 -0
- package/dist/organization-memberships/use-cases/leave-organization/LeaveOrganization.d.ts +15 -0
- package/dist/organization-memberships/use-cases/leave-organization/LeaveOrganization.d.ts.map +1 -0
- package/dist/organization-memberships/use-cases/leave-organization/LeaveOrganization.js +64 -0
- package/dist/organization-memberships/use-cases/leave-organization/LeaveOrganization.js.map +1 -0
- package/dist/organization-memberships/use-cases/remove-organization-member/RemoveOrganizationMember.d.ts +16 -0
- package/dist/organization-memberships/use-cases/remove-organization-member/RemoveOrganizationMember.d.ts.map +1 -0
- package/dist/organization-memberships/use-cases/remove-organization-member/RemoveOrganizationMember.js +87 -0
- package/dist/organization-memberships/use-cases/remove-organization-member/RemoveOrganizationMember.js.map +1 -0
- package/dist/organization-memberships/use-cases/update-organization-member-role/UpdateOrganizationMemberRole.d.ts +16 -0
- package/dist/organization-memberships/use-cases/update-organization-member-role/UpdateOrganizationMemberRole.d.ts.map +1 -0
- package/dist/organization-memberships/use-cases/update-organization-member-role/UpdateOrganizationMemberRole.js +85 -0
- package/dist/organization-memberships/use-cases/update-organization-member-role/UpdateOrganizationMemberRole.js.map +1 -0
- package/dist/organizations/index.d.ts +2 -0
- package/dist/organizations/index.d.ts.map +1 -0
- package/dist/organizations/index.js +3 -0
- package/dist/organizations/index.js.map +1 -0
- package/dist/organizations/use-cases/archive-organization/ArchiveOrganization.d.ts +38 -0
- package/dist/organizations/use-cases/archive-organization/ArchiveOrganization.d.ts.map +1 -0
- package/dist/organizations/use-cases/archive-organization/ArchiveOrganization.js +117 -0
- package/dist/organizations/use-cases/archive-organization/ArchiveOrganization.js.map +1 -0
- package/dist/organizations/use-cases/create-organization/CreateOrganization.d.ts +19 -0
- package/dist/organizations/use-cases/create-organization/CreateOrganization.d.ts.map +1 -0
- package/dist/organizations/use-cases/create-organization/CreateOrganization.js +96 -0
- package/dist/organizations/use-cases/create-organization/CreateOrganization.js.map +1 -0
- package/dist/organizations/use-cases/delete-organization/DeleteOrganization.d.ts +34 -0
- package/dist/organizations/use-cases/delete-organization/DeleteOrganization.d.ts.map +1 -0
- package/dist/organizations/use-cases/delete-organization/DeleteOrganization.js +103 -0
- package/dist/organizations/use-cases/delete-organization/DeleteOrganization.js.map +1 -0
- package/dist/organizations/use-cases/get-organization/GetOrganization.d.ts +18 -0
- package/dist/organizations/use-cases/get-organization/GetOrganization.d.ts.map +1 -0
- package/dist/organizations/use-cases/get-organization/GetOrganization.js +51 -0
- package/dist/organizations/use-cases/get-organization/GetOrganization.js.map +1 -0
- package/dist/organizations/use-cases/index.d.ts +9 -0
- package/dist/organizations/use-cases/index.d.ts.map +1 -0
- package/dist/organizations/use-cases/index.js +11 -0
- package/dist/organizations/use-cases/index.js.map +1 -0
- package/dist/organizations/use-cases/list-organization-members/ListOrganizationMembers.d.ts +46 -0
- package/dist/organizations/use-cases/list-organization-members/ListOrganizationMembers.d.ts.map +1 -0
- package/dist/organizations/use-cases/list-organization-members/ListOrganizationMembers.js +130 -0
- package/dist/organizations/use-cases/list-organization-members/ListOrganizationMembers.js.map +1 -0
- package/dist/organizations/use-cases/restore-organization/RestoreOrganization.d.ts +30 -0
- package/dist/organizations/use-cases/restore-organization/RestoreOrganization.d.ts.map +1 -0
- package/dist/organizations/use-cases/restore-organization/RestoreOrganization.js +98 -0
- package/dist/organizations/use-cases/restore-organization/RestoreOrganization.js.map +1 -0
- package/dist/organizations/use-cases/transfer-organization-ownership/TransferOrganizationOwnership.d.ts +30 -0
- package/dist/organizations/use-cases/transfer-organization-ownership/TransferOrganizationOwnership.d.ts.map +1 -0
- package/dist/organizations/use-cases/transfer-organization-ownership/TransferOrganizationOwnership.js +139 -0
- package/dist/organizations/use-cases/transfer-organization-ownership/TransferOrganizationOwnership.js.map +1 -0
- package/dist/organizations/use-cases/update-organization/UpdateOrganization.d.ts +22 -0
- package/dist/organizations/use-cases/update-organization/UpdateOrganization.d.ts.map +1 -0
- package/dist/organizations/use-cases/update-organization/UpdateOrganization.js +100 -0
- package/dist/organizations/use-cases/update-organization/UpdateOrganization.js.map +1 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +4 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/result/Result.d.ts +30 -0
- package/dist/shared/result/Result.d.ts.map +1 -0
- package/dist/shared/result/Result.js +69 -0
- package/dist/shared/result/Result.js.map +1 -0
- package/dist/shared/result/index.d.ts +4 -0
- package/dist/shared/result/index.d.ts.map +1 -0
- package/dist/shared/result/index.js +2 -0
- package/dist/shared/result/index.js.map +1 -0
- package/dist/shared/use-case/BaseUseCase.d.ts +164 -0
- package/dist/shared/use-case/BaseUseCase.d.ts.map +1 -0
- package/dist/shared/use-case/BaseUseCase.js +366 -0
- package/dist/shared/use-case/BaseUseCase.js.map +1 -0
- package/dist/shared/use-case/UseCaseHelpers.d.ts +43 -0
- package/dist/shared/use-case/UseCaseHelpers.d.ts.map +1 -0
- package/dist/shared/use-case/UseCaseHelpers.js +56 -0
- package/dist/shared/use-case/UseCaseHelpers.js.map +1 -0
- package/dist/shared/use-case/index.d.ts +3 -0
- package/dist/shared/use-case/index.d.ts.map +1 -0
- package/dist/shared/use-case/index.js +4 -0
- package/dist/shared/use-case/index.js.map +1 -0
- package/dist/users/index.d.ts +2 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +3 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/use-cases/create-user/CreateUser.d.ts +21 -0
- package/dist/users/use-cases/create-user/CreateUser.d.ts.map +1 -0
- package/dist/users/use-cases/create-user/CreateUser.js +81 -0
- package/dist/users/use-cases/create-user/CreateUser.js.map +1 -0
- package/dist/users/use-cases/delete-user/DeleteUser.d.ts +35 -0
- package/dist/users/use-cases/delete-user/DeleteUser.d.ts.map +1 -0
- package/dist/users/use-cases/delete-user/DeleteUser.js +120 -0
- package/dist/users/use-cases/delete-user/DeleteUser.js.map +1 -0
- package/dist/users/use-cases/get-user/GetUser.d.ts +18 -0
- package/dist/users/use-cases/get-user/GetUser.d.ts.map +1 -0
- package/dist/users/use-cases/get-user/GetUser.js +29 -0
- package/dist/users/use-cases/get-user/GetUser.js.map +1 -0
- package/dist/users/use-cases/index.d.ts +6 -0
- package/dist/users/use-cases/index.d.ts.map +1 -0
- package/dist/users/use-cases/index.js +8 -0
- package/dist/users/use-cases/index.js.map +1 -0
- package/dist/users/use-cases/list-user-organizations/ListUserOrganizations.d.ts +20 -0
- package/dist/users/use-cases/list-user-organizations/ListUserOrganizations.d.ts.map +1 -0
- package/dist/users/use-cases/list-user-organizations/ListUserOrganizations.js +68 -0
- package/dist/users/use-cases/list-user-organizations/ListUserOrganizations.js.map +1 -0
- package/dist/users/use-cases/update-user/UpdateUser.d.ts +19 -0
- package/dist/users/use-cases/update-user/UpdateUser.d.ts.map +1 -0
- package/dist/users/use-cases/update-user/UpdateUser.js +73 -0
- package/dist/users/use-cases/update-user/UpdateUser.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { UpdateOrganizationMemberRoleInputSchema, UpdateOrganizationMemberRoleOutputSchema } from '@multitenantkit/domain-contracts/organization-memberships';
|
|
2
|
+
import { NotFoundError, UnauthorizedError, ValidationError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
3
|
+
import { Result } from '../../../shared/result/Result';
|
|
4
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
5
|
+
/**
|
|
6
|
+
* UpdateOrganizationMemberRole use case
|
|
7
|
+
* Handles business logic for updating a organization member's role
|
|
8
|
+
*/
|
|
9
|
+
export class UpdateOrganizationMemberRole extends BaseUseCase {
|
|
10
|
+
constructor(adapters, toolkitOptions) {
|
|
11
|
+
super('organizationMembership-updateOrganizationMemberRole', adapters, toolkitOptions, UpdateOrganizationMemberRoleInputSchema, UpdateOrganizationMemberRoleOutputSchema, 'Failed to update organization member role');
|
|
12
|
+
}
|
|
13
|
+
async authorize(input, _context) {
|
|
14
|
+
const organization = await this.adapters.persistence.organizationRepository.findById(input.organizationId);
|
|
15
|
+
if (!organization) {
|
|
16
|
+
return Result.fail(new NotFoundError('Organization', input.organizationId));
|
|
17
|
+
}
|
|
18
|
+
// Check if organization is archived (cannot update member roles in archived organization)
|
|
19
|
+
if (organization.archivedAt) {
|
|
20
|
+
return Result.fail(new ValidationError('Cannot update member roles in an archived organization', 'organizationId'));
|
|
21
|
+
}
|
|
22
|
+
const getUserResult = await this.getUserFromExternalId(input.principalExternalId);
|
|
23
|
+
if (getUserResult.isFailure) {
|
|
24
|
+
return Result.fail(getUserResult.getError());
|
|
25
|
+
}
|
|
26
|
+
const existingUser = getUserResult.getValue();
|
|
27
|
+
// Target membership must exist and be active
|
|
28
|
+
const targetMembership = await this.adapters.persistence.organizationMembershipRepository.findByUserIdAndOrganizationId(input.targetUserId, input.organizationId);
|
|
29
|
+
if (!targetMembership || targetMembership.leftAt || targetMembership.deletedAt) {
|
|
30
|
+
return Result.fail(new NotFoundError('OrganizationMembership', `${input.targetUserId}:${input.organizationId}`));
|
|
31
|
+
}
|
|
32
|
+
// Prevent changing owner role
|
|
33
|
+
if (organization.ownerUserId === input.targetUserId) {
|
|
34
|
+
return Result.fail(new ValidationError('Organization owner role cannot be changed. Use transfer ownership instead.'));
|
|
35
|
+
}
|
|
36
|
+
// Owner can assign any role; admin can only assign member
|
|
37
|
+
const actorMembership = await this.adapters.persistence.organizationMembershipRepository.findByUserIdAndOrganizationId(existingUser.id, input.organizationId);
|
|
38
|
+
const isOwner = organization.ownerUserId === existingUser.id;
|
|
39
|
+
const isAdmin = !!actorMembership &&
|
|
40
|
+
actorMembership.roleCode === 'admin' &&
|
|
41
|
+
!!actorMembership.joinedAt &&
|
|
42
|
+
!actorMembership.leftAt &&
|
|
43
|
+
!actorMembership.deletedAt;
|
|
44
|
+
const canAssignRole = input.roleCode === 'member' || isOwner;
|
|
45
|
+
if (!isOwner && !(isAdmin && canAssignRole)) {
|
|
46
|
+
return Result.fail(new UnauthorizedError('Insufficient permissions to assign this role'));
|
|
47
|
+
}
|
|
48
|
+
return Result.ok(undefined);
|
|
49
|
+
}
|
|
50
|
+
async executeBusinessLogic(input, context) {
|
|
51
|
+
// Re-fetch target membership to get its ID for deletion
|
|
52
|
+
// Note: This check is redundant with authorize() but serves as defensive programming
|
|
53
|
+
// against race conditions and ensures executeBusinessLogic can be safely called independently
|
|
54
|
+
const targetMembership = await this.adapters.persistence.organizationMembershipRepository.findByUserIdAndOrganizationId(input.targetUserId, input.organizationId);
|
|
55
|
+
if (!targetMembership) {
|
|
56
|
+
return Result.fail(new NotFoundError('OrganizationMembership', `${input.targetUserId}:${input.organizationId}`));
|
|
57
|
+
}
|
|
58
|
+
const now = this.adapters.system.clock.now();
|
|
59
|
+
const updatedMembership = {
|
|
60
|
+
...targetMembership,
|
|
61
|
+
roleCode: input.roleCode,
|
|
62
|
+
updatedAt: now
|
|
63
|
+
};
|
|
64
|
+
const auditContext = {
|
|
65
|
+
...context,
|
|
66
|
+
organizationId: input.organizationId,
|
|
67
|
+
auditAction: 'UPDATE_ORGANIZATION_MEMBER_ROLE'
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
await this.adapters.persistence.uow.transaction(async (repos) => {
|
|
71
|
+
await repos.organizationMemberships.update(updatedMembership, auditContext);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return Result.fail(new ValidationError('Failed to update organization member role', undefined, {
|
|
76
|
+
originalError: error
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
const output = UpdateOrganizationMemberRoleOutputSchema.parse({
|
|
80
|
+
...updatedMembership
|
|
81
|
+
});
|
|
82
|
+
return Result.ok(output);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=UpdateOrganizationMemberRole.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UpdateOrganizationMemberRole.js","sourceRoot":"","sources":["../../../../src/organization-memberships/use-cases/update-organization-member-role/UpdateOrganizationMemberRole.ts"],"names":[],"mappings":"AAMA,OAAO,EAEH,uCAAuC,EACvC,wCAAwC,EAC3C,MAAM,2DAA2D,CAAC;AAEnE,OAAO,EAEH,aAAa,EACb,iBAAiB,EACjB,eAAe,EAClB,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;GAGG;AACH,MAAM,OAAO,4BAQT,SAAQ,WAOP;IAGD,YACI,QAIC,EACD,cAIC;QAED,KAAK,CACD,qDAAqD,EACrD,QAAQ,EACR,cAAc,EACd,uCAAuC,EACvC,wCAAgH,EAChH,2CAA2C,CAC9C,CAAC;IACN,CAAC;IAES,KAAK,CAAC,SAAS,CACrB,KAAwC,EACxC,QAA0B;QAE1B,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,sBAAsB,CAAC,QAAQ,CAChF,KAAK,CAAC,cAAc,CACvB,CAAC;QACF,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,cAAc,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;QAChF,CAAC;QAED,0FAA0F;QAC1F,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC,IAAI,CACd,IAAI,eAAe,CACf,wDAAwD,EACxD,gBAAgB,CACnB,CACJ,CAAC;QACN,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAElF,IAAI,aAAa,CAAC,SAAS,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,YAAY,GAAG,aAAa,CAAC,QAAQ,EAAE,CAAC;QAC9C,6CAA6C;QAC7C,MAAM,gBAAgB,GAClB,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,gCAAgC,CAAC,6BAA6B,CAC1F,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,cAAc,CACvB,CAAC;QACN,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,IAAI,gBAAgB,CAAC,SAAS,EAAE,CAAC;YAC7E,OAAO,MAAM,CAAC,IAAI,CACd,IAAI,aAAa,CACb,wBAAwB,EACxB,GAAG,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,cAAc,EAAE,CAClD,CACJ,CAAC;QACN,CAAC;QACD,8BAA8B;QAC9B,IAAI,YAAY,CAAC,WAAW,KAAK,KAAK,CAAC,YAAY,EAAE,CAAC;YAClD,OAAO,MAAM,CAAC,IAAI,CACd,IAAI,eAAe,CACf,4EAA4E,CAC/E,CACJ,CAAC;QACN,CAAC;QACD,0DAA0D;QAC1D,MAAM,eAAe,GACjB,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,gCAAgC,CAAC,6BAA6B,CAC1F,YAAY,CAAC,EAAE,EACf,KAAK,CAAC,cAAc,CACvB,CAAC;QACN,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,KAAK,YAAY,CAAC,EAAE,CAAC;QAC7D,MAAM,OAAO,GACT,CAAC,CAAC,eAAe;YACjB,eAAe,CAAC,QAAQ,KAAK,OAAO;YACpC,CAAC,CAAC,eAAe,CAAC,QAAQ;YAC1B,CAAC,eAAe,CAAC,MAAM;YACvB,CAAC,eAAe,CAAC,SAAS,CAAC;QAC/B,MAAM,aAAa,GAAG,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC;QAE7D,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,aAAa,CAAC,EAAE,CAAC;YAC1C,OAAO,MAAM,CAAC,IAAI,CACd,IAAI,iBAAiB,CAAC,8CAA8C,CAAC,CACxE,CAAC;QACN,CAAC;QAED,OAAO,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;IAES,KAAK,CAAC,oBAAoB,CAChC,KAAwC,EACxC,OAAyB;QAEzB,wDAAwD;QACxD,qFAAqF;QACrF,8FAA8F;QAC9F,MAAM,gBAAgB,GAClB,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,gCAAgC,CAAC,6BAA6B,CAC1F,KAAK,CAAC,YAAY,EAClB,KAAK,CAAC,cAAc,CACvB,CAAC;QACN,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACpB,OAAO,MAAM,CAAC,IAAI,CACd,IAAI,aAAa,CACb,wBAAwB,EACxB,GAAG,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,cAAc,EAAE,CAClD,CACJ,CAAC;QACN,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7C,MAAM,iBAAiB,GAA2B;YAC9C,GAAG,gBAAgB;YACnB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,GAAG;SACS,CAAC;QAC5B,MAAM,YAAY,GAAqB;YACnC,GAAG,OAAO;YACV,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW,EAAE,iCAAiC;SACjD,CAAC;QACF,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBAC5D,MAAM,KAAK,CAAC,uBAAuB,CAAC,MAAM,CAAC,iBAAwB,EAAE,YAAY,CAAC,CAAC;YACvF,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,MAAM,CAAC,IAAI,CACd,IAAI,eAAe,CAAC,2CAA2C,EAAE,SAAS,EAAE;gBACxE,aAAa,EAAE,KAAK;aACvB,CAAC,CACL,CAAC;QACN,CAAC;QAED,MAAM,MAAM,GAAG,wCAAwC,CAAC,KAAK,CAAC;YAC1D,GAAG,iBAAiB;SACvB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;CACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/organizations/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/organizations/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ArchiveOrganizationInput, IArchiveOrganization, OperationContext, Organization, ToolkitOptions } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { type Adapters, NotFoundError, UnauthorizedError, ValidationError } from '@multitenantkit/domain-contracts';
|
|
3
|
+
import type { DomainError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
4
|
+
import { Result } from '../../../shared/result';
|
|
5
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
6
|
+
/**
|
|
7
|
+
* ArchiveOrganization use case
|
|
8
|
+
* Handles the business logic for archiving an organization
|
|
9
|
+
* Sets the archivedAt timestamp without removing the organization from the database
|
|
10
|
+
*
|
|
11
|
+
* Authorization:
|
|
12
|
+
* - Only organization owners and admins can archive an organization
|
|
13
|
+
*
|
|
14
|
+
* Cascade behavior:
|
|
15
|
+
* - Does NOT modify organization memberships when archiving an organization
|
|
16
|
+
* This preserves the state of memberships for future restore:
|
|
17
|
+
* * Active memberships (deletedAt=null) can remain active
|
|
18
|
+
* * Deleted memberships (deletedAt set) were explicitly removed
|
|
19
|
+
* * Left memberships (leftAt set) show historical voluntary departures
|
|
20
|
+
*
|
|
21
|
+
* Semantic distinction:
|
|
22
|
+
* - organization.archivedAt = organization was archived (can be restored)
|
|
23
|
+
* - organization.deletedAt = organization was soft deleted (permanent)
|
|
24
|
+
* - membership.deletedAt = membership was explicitly deleted (RemoveOrganizationMember use case)
|
|
25
|
+
* - membership.leftAt = user voluntarily left the organization (LeaveOrganization use case)
|
|
26
|
+
*
|
|
27
|
+
* Generic support for custom fields:
|
|
28
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
29
|
+
* @template TUserCustomFields - User custom fields (for toolkit options compatibility)
|
|
30
|
+
* @template TOrganizationMembershipCustomFields - Membership custom fields (for toolkit options compatibility)
|
|
31
|
+
*/
|
|
32
|
+
export declare class ArchiveOrganization<TOrganizationCustomFields = {}, TUserCustomFields = {}, TOrganizationMembershipCustomFields = {}> extends BaseUseCase<ArchiveOrganizationInput, Organization & TOrganizationCustomFields, ValidationError | NotFoundError | UnauthorizedError, TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields> implements IArchiveOrganization {
|
|
33
|
+
private readonly customSchema?;
|
|
34
|
+
constructor(adapters: Adapters<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>, toolkitOptions?: ToolkitOptions<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>);
|
|
35
|
+
protected authorize(input: ArchiveOrganizationInput, _context: OperationContext): Promise<Result<void, DomainError>>;
|
|
36
|
+
protected executeBusinessLogic(input: ArchiveOrganizationInput, context: OperationContext): Promise<Result<Organization & TOrganizationCustomFields, ValidationError | NotFoundError>>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=ArchiveOrganization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArchiveOrganization.d.ts","sourceRoot":"","sources":["../../../../src/organizations/use-cases/archive-organization/ArchiveOrganization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACH,KAAK,QAAQ,EAEb,aAAa,EAEb,iBAAiB,EACjB,eAAe,EAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sDAAsD,CAAC;AAExF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAkB,MAAM,0BAA0B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,mBAAmB,CAExB,yBAAyB,GAAG,EAAE,EAE9B,iBAAiB,GAAG,EAAE,EAEtB,mCAAmC,GAAG,EAAE,CAE5C,SAAQ,WAAW,CACf,wBAAwB,EACxB,YAAY,GAAG,yBAAyB,EACxC,eAAe,GAAG,aAAa,GAAG,iBAAiB,EACnD,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CAEvC,YAAW,oBAAoB;IAE/B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAmB;gBAG7C,QAAQ,EAAE,QAAQ,CACd,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC,EACD,cAAc,CAAC,EAAE,cAAc,CAC3B,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC;cA2BW,SAAS,CACrB,KAAK,EAAE,wBAAwB,EAC/B,QAAQ,EAAE,gBAAgB,GAC3B,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;cA6CrB,oBAAoB,CAChC,KAAK,EAAE,wBAAwB,EAC/B,OAAO,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,GAAG,yBAAyB,EAAE,eAAe,GAAG,aAAa,CAAC,CAAC;CA4EhG"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ArchiveOrganizationInputSchema, NotFoundError, OrganizationSchema, UnauthorizedError, ValidationError } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { Result } from '../../../shared/result';
|
|
3
|
+
import { BaseUseCase, UseCaseHelpers } from '../../../shared/use-case';
|
|
4
|
+
/**
|
|
5
|
+
* ArchiveOrganization use case
|
|
6
|
+
* Handles the business logic for archiving an organization
|
|
7
|
+
* Sets the archivedAt timestamp without removing the organization from the database
|
|
8
|
+
*
|
|
9
|
+
* Authorization:
|
|
10
|
+
* - Only organization owners and admins can archive an organization
|
|
11
|
+
*
|
|
12
|
+
* Cascade behavior:
|
|
13
|
+
* - Does NOT modify organization memberships when archiving an organization
|
|
14
|
+
* This preserves the state of memberships for future restore:
|
|
15
|
+
* * Active memberships (deletedAt=null) can remain active
|
|
16
|
+
* * Deleted memberships (deletedAt set) were explicitly removed
|
|
17
|
+
* * Left memberships (leftAt set) show historical voluntary departures
|
|
18
|
+
*
|
|
19
|
+
* Semantic distinction:
|
|
20
|
+
* - organization.archivedAt = organization was archived (can be restored)
|
|
21
|
+
* - organization.deletedAt = organization was soft deleted (permanent)
|
|
22
|
+
* - membership.deletedAt = membership was explicitly deleted (RemoveOrganizationMember use case)
|
|
23
|
+
* - membership.leftAt = user voluntarily left the organization (LeaveOrganization use case)
|
|
24
|
+
*
|
|
25
|
+
* Generic support for custom fields:
|
|
26
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
27
|
+
* @template TUserCustomFields - User custom fields (for toolkit options compatibility)
|
|
28
|
+
* @template TOrganizationMembershipCustomFields - Membership custom fields (for toolkit options compatibility)
|
|
29
|
+
*/
|
|
30
|
+
export class ArchiveOrganization extends BaseUseCase {
|
|
31
|
+
customSchema;
|
|
32
|
+
constructor(adapters, toolkitOptions) {
|
|
33
|
+
// Extract custom schema from toolkit options
|
|
34
|
+
const customSchema = toolkitOptions?.organizations?.customFields?.customSchema;
|
|
35
|
+
// Extend output schema with custom fields if provided
|
|
36
|
+
const outputSchema = customSchema
|
|
37
|
+
? OrganizationSchema.merge(customSchema)
|
|
38
|
+
: OrganizationSchema;
|
|
39
|
+
super('organization-archiveOrganization', adapters, toolkitOptions, ArchiveOrganizationInputSchema, outputSchema, 'Failed to archive organization');
|
|
40
|
+
this.customSchema = customSchema;
|
|
41
|
+
}
|
|
42
|
+
async authorize(input, _context) {
|
|
43
|
+
// 1. Ensure organization exists for permission checks
|
|
44
|
+
const organization = await this.adapters.persistence.organizationRepository.findById(input.organizationId);
|
|
45
|
+
if (!organization) {
|
|
46
|
+
return Result.fail(new NotFoundError('Organization', input.organizationId));
|
|
47
|
+
}
|
|
48
|
+
// 2. Get the user making the request
|
|
49
|
+
const getUserResult = await this.getUserFromExternalId(input.principalExternalId);
|
|
50
|
+
if (getUserResult.isFailure) {
|
|
51
|
+
return Result.fail(getUserResult.getError());
|
|
52
|
+
}
|
|
53
|
+
const user = getUserResult.getValue();
|
|
54
|
+
// 3. Check if user is the owner
|
|
55
|
+
const isOwner = organization.ownerUserId === user.id;
|
|
56
|
+
// 4. Check if user is an admin
|
|
57
|
+
const actorMembership = await this.adapters.persistence.organizationMembershipRepository.findByUserIdAndOrganizationId(user.id, input.organizationId);
|
|
58
|
+
const isAdmin = !!actorMembership &&
|
|
59
|
+
actorMembership.roleCode === 'admin' &&
|
|
60
|
+
!!actorMembership.joinedAt &&
|
|
61
|
+
!actorMembership.leftAt &&
|
|
62
|
+
!actorMembership.deletedAt;
|
|
63
|
+
// 5. Authorize: Only owners and admins can archive
|
|
64
|
+
if (!isOwner && !isAdmin) {
|
|
65
|
+
return Result.fail(new UnauthorizedError('Only organization owners and admins can archive the organization'));
|
|
66
|
+
}
|
|
67
|
+
return Result.ok(undefined);
|
|
68
|
+
}
|
|
69
|
+
async executeBusinessLogic(input, context) {
|
|
70
|
+
// 1. Find existing organization
|
|
71
|
+
const organizationResult = await UseCaseHelpers.findByIdOrFail(this.adapters.persistence.organizationRepository, input.organizationId, 'Organization');
|
|
72
|
+
if (organizationResult.isFailure) {
|
|
73
|
+
return organizationResult;
|
|
74
|
+
}
|
|
75
|
+
const existingOrganization = organizationResult.getValue();
|
|
76
|
+
// 2. Check if organization is already archived
|
|
77
|
+
if (existingOrganization.archivedAt) {
|
|
78
|
+
return Result.fail(new ValidationError('Organization is already archived', 'organizationId'));
|
|
79
|
+
}
|
|
80
|
+
// 3. Check if organization is deleted (cannot archive a deleted organization)
|
|
81
|
+
if (existingOrganization.deletedAt) {
|
|
82
|
+
return Result.fail(new ValidationError('Cannot archive a deleted organization', 'organizationId'));
|
|
83
|
+
}
|
|
84
|
+
// 4. Build archived organization data
|
|
85
|
+
const now = this.adapters.system.clock.now();
|
|
86
|
+
const archivedOrganizationData = {
|
|
87
|
+
...existingOrganization,
|
|
88
|
+
archivedAt: now,
|
|
89
|
+
updatedAt: now
|
|
90
|
+
};
|
|
91
|
+
// 5. Validate the updated data
|
|
92
|
+
const validationSchema = this.customSchema
|
|
93
|
+
? OrganizationSchema.strip().and(this.customSchema.strip())
|
|
94
|
+
: OrganizationSchema.strip();
|
|
95
|
+
const validationResult = validationSchema.safeParse(archivedOrganizationData);
|
|
96
|
+
if (!validationResult.success) {
|
|
97
|
+
const firstError = validationResult.error.errors[0];
|
|
98
|
+
return Result.fail(new ValidationError(firstError.message, firstError.path.join('.')));
|
|
99
|
+
}
|
|
100
|
+
// Use validated and potentially transformed data from Zod
|
|
101
|
+
const archivedOrganization = validationResult.data;
|
|
102
|
+
// 6. Persist the archived organization using Unit of Work (transaction) with audit context
|
|
103
|
+
const auditContext = UseCaseHelpers.enrichAuditContext(context, 'ARCHIVE_ORGANIZATION', input.organizationId);
|
|
104
|
+
await this.adapters.persistence.uow.transaction(async (repos) => {
|
|
105
|
+
// 6.1. Save the archived organization
|
|
106
|
+
await repos.organizations.update(archivedOrganization, auditContext);
|
|
107
|
+
// 6.2. Do NOT modify organization memberships
|
|
108
|
+
// Rationale: Preserving membership state allows the organization to be restored later
|
|
109
|
+
// - Active memberships remain active
|
|
110
|
+
// - Deleted memberships remain deleted
|
|
111
|
+
// - Left memberships remain as historical records
|
|
112
|
+
});
|
|
113
|
+
// 7. Return archived organization
|
|
114
|
+
return Result.ok(archivedOrganization);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=ArchiveOrganization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArchiveOrganization.js","sourceRoot":"","sources":["../../../../src/organizations/use-cases/archive-organization/ArchiveOrganization.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Adapters } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import type { CreateOrganizationInput, CreateOrganizationOutput, ICreateOrganization } from '@multitenantkit/domain-contracts/organizations';
|
|
3
|
+
import type { OperationContext, ToolkitOptions } from '@multitenantkit/domain-contracts/shared';
|
|
4
|
+
import { type DomainError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
5
|
+
import { Result } from '../../../shared/result/Result';
|
|
6
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
7
|
+
/**
|
|
8
|
+
* CreateOrganization use case
|
|
9
|
+
* Handles the business logic for creating a new organization
|
|
10
|
+
*
|
|
11
|
+
* Generic support for custom fields:
|
|
12
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
13
|
+
*/
|
|
14
|
+
export declare class CreateOrganization<TUserCustomFields = {}, TOrganizationCustomFields = {}, TOrganizationMembershipCustomFields = {}> extends BaseUseCase<CreateOrganizationInput & TOrganizationCustomFields, CreateOrganizationOutput, DomainError, TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields> implements ICreateOrganization {
|
|
15
|
+
private readonly customSchema?;
|
|
16
|
+
constructor(adapters: Adapters<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>, toolkitOptions?: ToolkitOptions<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>);
|
|
17
|
+
protected executeBusinessLogic(input: CreateOrganizationInput & TOrganizationCustomFields, context: OperationContext): Promise<Result<CreateOrganizationOutput, DomainError>>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=CreateOrganization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateOrganization.d.ts","sourceRoot":"","sources":["../../../../src/organizations/use-cases/create-organization/CreateOrganization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAEjE,OAAO,KAAK,EACR,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EAEtB,MAAM,gDAAgD,CAAC;AAKxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EACH,KAAK,WAAW,EAEnB,MAAM,sDAAsD,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;;;;GAMG;AACH,qBAAa,kBAAkB,CAEvB,iBAAiB,GAAG,EAAE,EAEtB,yBAAyB,GAAG,EAAE,EAE9B,mCAAmC,GAAG,EAAE,CAE5C,SAAQ,WAAW,CACf,uBAAuB,GAAG,yBAAyB,EACnD,wBAAwB,EACxB,WAAW,EACX,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CAEvC,YAAW,mBAAmB;IAE9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAmB;gBAG7C,QAAQ,EAAE,QAAQ,CACd,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC,EACD,cAAc,CAAC,EAAE,cAAc,CAC3B,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC;cA+BW,oBAAoB,CAChC,KAAK,EAAE,uBAAuB,GAAG,yBAAyB,EAC1D,OAAO,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,WAAW,CAAC,CAAC;CAkF5D"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { CreateOrganizationInputSchema, CreateOrganizationOutputSchema } from '@multitenantkit/domain-contracts/organizations';
|
|
2
|
+
import { ValidationError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
3
|
+
import { Result } from '../../../shared/result/Result';
|
|
4
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
5
|
+
/**
|
|
6
|
+
* CreateOrganization use case
|
|
7
|
+
* Handles the business logic for creating a new organization
|
|
8
|
+
*
|
|
9
|
+
* Generic support for custom fields:
|
|
10
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
11
|
+
*/
|
|
12
|
+
export class CreateOrganization extends BaseUseCase {
|
|
13
|
+
customSchema;
|
|
14
|
+
constructor(adapters, toolkitOptions) {
|
|
15
|
+
// Extract organization custom schema from toolkit options
|
|
16
|
+
const customSchema = toolkitOptions?.organizations?.customFields?.customSchema;
|
|
17
|
+
// Build input schema with custom fields if provided
|
|
18
|
+
const inputSchema = (customSchema
|
|
19
|
+
? CreateOrganizationInputSchema.and(customSchema.partial())
|
|
20
|
+
: CreateOrganizationInputSchema);
|
|
21
|
+
// Build output schema with custom fields if provided
|
|
22
|
+
const outputSchema = (customSchema
|
|
23
|
+
? CreateOrganizationOutputSchema.merge(customSchema)
|
|
24
|
+
: CreateOrganizationOutputSchema);
|
|
25
|
+
super('organization-createOrganization', adapters, toolkitOptions, inputSchema, outputSchema, 'Failed to create organization');
|
|
26
|
+
this.customSchema = customSchema;
|
|
27
|
+
}
|
|
28
|
+
async executeBusinessLogic(input, context) {
|
|
29
|
+
// 1. Business validation - e.g., ensure no conflicting organization constraints
|
|
30
|
+
// Placeholder for name uniqueness etc. when name present in custom fields
|
|
31
|
+
const getUserResult = await this.getUserFromExternalId(input.principalExternalId);
|
|
32
|
+
if (getUserResult.isFailure) {
|
|
33
|
+
return Result.fail(getUserResult.getError());
|
|
34
|
+
}
|
|
35
|
+
const existingUser = getUserResult.getValue();
|
|
36
|
+
// 2. Generate ID and timestamp
|
|
37
|
+
const organizationId = this.adapters.system.uuid.generate();
|
|
38
|
+
const now = this.adapters.system.clock.now();
|
|
39
|
+
// 3. Create Organization entity
|
|
40
|
+
const organization = {
|
|
41
|
+
id: organizationId,
|
|
42
|
+
...input,
|
|
43
|
+
ownerUserId: existingUser.id,
|
|
44
|
+
createdAt: now,
|
|
45
|
+
updatedAt: now
|
|
46
|
+
};
|
|
47
|
+
// 4. Validate the organization data (base + custom)
|
|
48
|
+
const organizationSchema = this.customSchema
|
|
49
|
+
? CreateOrganizationOutputSchema.strip().and(this.customSchema.strip())
|
|
50
|
+
: CreateOrganizationOutputSchema.strip();
|
|
51
|
+
const validation = organizationSchema.safeParse(organization);
|
|
52
|
+
if (!validation.success) {
|
|
53
|
+
const firstError = validation.error.errors[0];
|
|
54
|
+
return Result.fail(new ValidationError(firstError.message, firstError.path.join('.')));
|
|
55
|
+
}
|
|
56
|
+
const validatedOrganization = validation.data;
|
|
57
|
+
// 5. Persist organization and owner membership using Unit of Work
|
|
58
|
+
const auditContext = {
|
|
59
|
+
...context,
|
|
60
|
+
organizationId: validatedOrganization.id,
|
|
61
|
+
auditAction: 'CREATE_ORGANIZATION'
|
|
62
|
+
};
|
|
63
|
+
const membershipAuditContext = {
|
|
64
|
+
...context,
|
|
65
|
+
organizationId: validatedOrganization.id,
|
|
66
|
+
auditAction: 'ADD_ORGANIZATION_MEMBER'
|
|
67
|
+
};
|
|
68
|
+
try {
|
|
69
|
+
await this.adapters.persistence.uow.transaction(async (repos) => {
|
|
70
|
+
await repos.organizations.insert(validatedOrganization, auditContext);
|
|
71
|
+
const ownerMembership = {
|
|
72
|
+
id: this.adapters.system.uuid.generate(),
|
|
73
|
+
organizationId: validatedOrganization.id,
|
|
74
|
+
userId: validatedOrganization.ownerUserId,
|
|
75
|
+
username: existingUser.username,
|
|
76
|
+
roleCode: 'owner',
|
|
77
|
+
invitedAt: undefined,
|
|
78
|
+
joinedAt: now,
|
|
79
|
+
leftAt: undefined,
|
|
80
|
+
deletedAt: undefined,
|
|
81
|
+
createdAt: now,
|
|
82
|
+
updatedAt: now
|
|
83
|
+
};
|
|
84
|
+
await repos.organizationMemberships.insert(ownerMembership, membershipAuditContext);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return Result.fail(new ValidationError('Failed to save organization', undefined, {
|
|
89
|
+
originalError: error
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
// 6. Return success; BaseUseCase will parse with output schema
|
|
93
|
+
return Result.ok({ ...validatedOrganization });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=CreateOrganization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateOrganization.js","sourceRoot":"","sources":["../../../../src/organizations/use-cases/create-organization/CreateOrganization.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DeleteOrganizationInput, IDeleteOrganization, OperationContext, Organization, ToolkitOptions } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { type Adapters, NotFoundError, UnauthorizedError, ValidationError } from '@multitenantkit/domain-contracts';
|
|
3
|
+
import type { DomainError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
4
|
+
import { Result } from '../../../shared/result';
|
|
5
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
6
|
+
/**
|
|
7
|
+
* DeleteOrganization use case
|
|
8
|
+
* Handles the business logic for soft deleting a organization
|
|
9
|
+
* Sets the deletedAt timestamp without removing the organization from the database
|
|
10
|
+
*
|
|
11
|
+
* Cascade behavior:
|
|
12
|
+
* - Does NOT modify organization memberships when deleting a organization
|
|
13
|
+
* This preserves the state of memberships for future restore:
|
|
14
|
+
* * Active memberships (deletedAt=null) can be restored
|
|
15
|
+
* * Deleted memberships (deletedAt set) were explicitly removed and should not be restored
|
|
16
|
+
* * Left memberships (leftAt set) show historical voluntary departures
|
|
17
|
+
*
|
|
18
|
+
* Semantic distinction:
|
|
19
|
+
* - organization.deletedAt = organization was soft deleted
|
|
20
|
+
* - membership.deletedAt = membership was explicitly deleted (RemoveOrganizationMember use case)
|
|
21
|
+
* - membership.leftAt = user voluntarily left the organization (LeaveOrganization use case)
|
|
22
|
+
*
|
|
23
|
+
* Generic support for custom fields:
|
|
24
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
25
|
+
* @template TUserCustomFields - User custom fields (for toolkit options compatibility)
|
|
26
|
+
* @template TOrganizationMembershipCustomFields - Membership custom fields (for toolkit options compatibility)
|
|
27
|
+
*/
|
|
28
|
+
export declare class DeleteOrganization<TOrganizationCustomFields = {}, TUserCustomFields = {}, TOrganizationMembershipCustomFields = {}> extends BaseUseCase<DeleteOrganizationInput, Organization & TOrganizationCustomFields, ValidationError | NotFoundError | UnauthorizedError, TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields> implements IDeleteOrganization {
|
|
29
|
+
private readonly customSchema?;
|
|
30
|
+
constructor(adapters: Adapters<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>, toolkitOptions?: ToolkitOptions<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>);
|
|
31
|
+
protected authorize(input: DeleteOrganizationInput, _context: OperationContext): Promise<Result<void, DomainError>>;
|
|
32
|
+
protected executeBusinessLogic(input: DeleteOrganizationInput, context: OperationContext): Promise<Result<Organization & TOrganizationCustomFields, ValidationError | NotFoundError>>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=DeleteOrganization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeleteOrganization.d.ts","sourceRoot":"","sources":["../../../../src/organizations/use-cases/delete-organization/DeleteOrganization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACH,KAAK,QAAQ,EAEb,aAAa,EAEb,iBAAiB,EACjB,eAAe,EAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sDAAsD,CAAC;AAExF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAkB,MAAM,0BAA0B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,kBAAkB,CAEvB,yBAAyB,GAAG,EAAE,EAE9B,iBAAiB,GAAG,EAAE,EAEtB,mCAAmC,GAAG,EAAE,CAE5C,SAAQ,WAAW,CACf,uBAAuB,EACvB,YAAY,GAAG,yBAAyB,EACxC,eAAe,GAAG,aAAa,GAAG,iBAAiB,EACnD,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CAEvC,YAAW,mBAAmB;IAE9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAmB;gBAG7C,QAAQ,EAAE,QAAQ,CACd,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC,EACD,cAAc,CAAC,EAAE,cAAc,CAC3B,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC;cA2BW,SAAS,CACrB,KAAK,EAAE,uBAAuB,EAC9B,QAAQ,EAAE,gBAAgB,GAC3B,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;cA+BrB,oBAAoB,CAChC,KAAK,EAAE,uBAAuB,EAC9B,OAAO,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,GAAG,yBAAyB,EAAE,eAAe,GAAG,aAAa,CAAC,CAAC;CAqEhG"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { DeleteOrganizationInputSchema, NotFoundError, OrganizationSchema, UnauthorizedError, ValidationError } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { Result } from '../../../shared/result';
|
|
3
|
+
import { BaseUseCase, UseCaseHelpers } from '../../../shared/use-case';
|
|
4
|
+
/**
|
|
5
|
+
* DeleteOrganization use case
|
|
6
|
+
* Handles the business logic for soft deleting a organization
|
|
7
|
+
* Sets the deletedAt timestamp without removing the organization from the database
|
|
8
|
+
*
|
|
9
|
+
* Cascade behavior:
|
|
10
|
+
* - Does NOT modify organization memberships when deleting a organization
|
|
11
|
+
* This preserves the state of memberships for future restore:
|
|
12
|
+
* * Active memberships (deletedAt=null) can be restored
|
|
13
|
+
* * Deleted memberships (deletedAt set) were explicitly removed and should not be restored
|
|
14
|
+
* * Left memberships (leftAt set) show historical voluntary departures
|
|
15
|
+
*
|
|
16
|
+
* Semantic distinction:
|
|
17
|
+
* - organization.deletedAt = organization was soft deleted
|
|
18
|
+
* - membership.deletedAt = membership was explicitly deleted (RemoveOrganizationMember use case)
|
|
19
|
+
* - membership.leftAt = user voluntarily left the organization (LeaveOrganization use case)
|
|
20
|
+
*
|
|
21
|
+
* Generic support for custom fields:
|
|
22
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
23
|
+
* @template TUserCustomFields - User custom fields (for toolkit options compatibility)
|
|
24
|
+
* @template TOrganizationMembershipCustomFields - Membership custom fields (for toolkit options compatibility)
|
|
25
|
+
*/
|
|
26
|
+
export class DeleteOrganization extends BaseUseCase {
|
|
27
|
+
customSchema;
|
|
28
|
+
constructor(adapters, toolkitOptions) {
|
|
29
|
+
// Extract custom schema from toolkit options
|
|
30
|
+
const customSchema = toolkitOptions?.organizations?.customFields?.customSchema;
|
|
31
|
+
// Extend output schema with custom fields if provided
|
|
32
|
+
const outputSchema = customSchema
|
|
33
|
+
? OrganizationSchema.merge(customSchema)
|
|
34
|
+
: OrganizationSchema;
|
|
35
|
+
super('organization-deleteOrganization', adapters, toolkitOptions, DeleteOrganizationInputSchema, outputSchema, 'Failed to delete organization');
|
|
36
|
+
this.customSchema = customSchema;
|
|
37
|
+
}
|
|
38
|
+
async authorize(input, _context) {
|
|
39
|
+
// 1. Ensure organization exists for permission checks
|
|
40
|
+
const organization = await this.adapters.persistence.organizationRepository.findById(input.organizationId);
|
|
41
|
+
if (!organization) {
|
|
42
|
+
return Result.fail(new NotFoundError('Organization', input.organizationId));
|
|
43
|
+
}
|
|
44
|
+
// 2. Get the user making the request
|
|
45
|
+
const getUserResult = await this.getUserFromExternalId(input.principalExternalId);
|
|
46
|
+
if (getUserResult.isFailure) {
|
|
47
|
+
return Result.fail(getUserResult.getError());
|
|
48
|
+
}
|
|
49
|
+
const user = getUserResult.getValue();
|
|
50
|
+
// 3. Check if user is the owner
|
|
51
|
+
const isOwner = organization.ownerUserId === user.id;
|
|
52
|
+
// 4. Authorize: ONLY the owner can delete the organization
|
|
53
|
+
// Note: Unlike archive-organization, delete is more critical and restrictive
|
|
54
|
+
if (!isOwner) {
|
|
55
|
+
return Result.fail(new UnauthorizedError('Only the organization owner can delete the organization'));
|
|
56
|
+
}
|
|
57
|
+
return Result.ok(undefined);
|
|
58
|
+
}
|
|
59
|
+
async executeBusinessLogic(input, context) {
|
|
60
|
+
// 1. Find existing organization
|
|
61
|
+
const organizationResult = await UseCaseHelpers.findByIdOrFail(this.adapters.persistence.organizationRepository, input.organizationId, 'Organization');
|
|
62
|
+
if (organizationResult.isFailure) {
|
|
63
|
+
return organizationResult;
|
|
64
|
+
}
|
|
65
|
+
const existingOrganization = organizationResult.getValue();
|
|
66
|
+
// 2. Check if organization is already deleted
|
|
67
|
+
if (existingOrganization.deletedAt) {
|
|
68
|
+
return Result.fail(new ValidationError('Organization is already deleted', 'organizationId'));
|
|
69
|
+
}
|
|
70
|
+
// 3. Build deleted organization data (soft delete)
|
|
71
|
+
const now = this.adapters.system.clock.now();
|
|
72
|
+
const deletedOrganizationData = {
|
|
73
|
+
...existingOrganization,
|
|
74
|
+
deletedAt: now,
|
|
75
|
+
updatedAt: now
|
|
76
|
+
};
|
|
77
|
+
// 4. Validate the updated data
|
|
78
|
+
const validationSchema = this.customSchema
|
|
79
|
+
? OrganizationSchema.strip().and(this.customSchema.strip())
|
|
80
|
+
: OrganizationSchema.strip();
|
|
81
|
+
const validationResult = validationSchema.safeParse(deletedOrganizationData);
|
|
82
|
+
if (!validationResult.success) {
|
|
83
|
+
const firstError = validationResult.error.errors[0];
|
|
84
|
+
return Result.fail(new ValidationError(firstError.message, firstError.path.join('.')));
|
|
85
|
+
}
|
|
86
|
+
// Use validated and potentially transformed data from Zod
|
|
87
|
+
const deletedOrganization = validationResult.data;
|
|
88
|
+
// 5. Persist the deleted organization using Unit of Work (transaction) with audit context
|
|
89
|
+
const auditContext = UseCaseHelpers.enrichAuditContext(context, 'DELETE_ORGANIZATION', input.organizationId);
|
|
90
|
+
await this.adapters.persistence.uow.transaction(async (repos) => {
|
|
91
|
+
// 5.1. Save the soft-deleted organization
|
|
92
|
+
await repos.organizations.update(deletedOrganization, auditContext);
|
|
93
|
+
// 5.2. Do NOT modify organization memberships
|
|
94
|
+
// Rationale: Preserving membership state allows future restore to know:
|
|
95
|
+
// - Which users were active members at deletion time (joinedAt set, leftAt null)
|
|
96
|
+
// - Which users had pending invitations (invitedAt set, joinedAt null)
|
|
97
|
+
// - Which users had left voluntarily (leftAt set) → historical record preserved
|
|
98
|
+
});
|
|
99
|
+
// 6. Return deleted organization
|
|
100
|
+
return Result.ok(deletedOrganization);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=DeleteOrganization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeleteOrganization.js","sourceRoot":"","sources":["../../../../src/organizations/use-cases/delete-organization/DeleteOrganization.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Adapters } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import type { GetOrganizationInput, GetOrganizationOutput, IGetOrganization } from '@multitenantkit/domain-contracts/organizations';
|
|
3
|
+
import type { OperationContext, ToolkitOptions } from '@multitenantkit/domain-contracts/shared';
|
|
4
|
+
import { type DomainError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
5
|
+
import { Result } from '../../../shared/result/Result';
|
|
6
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
7
|
+
/**
|
|
8
|
+
* GetOrganization use case
|
|
9
|
+
* Handles the business logic for retrieving a organization by ID
|
|
10
|
+
*
|
|
11
|
+
* Generic support for custom fields:
|
|
12
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
13
|
+
*/
|
|
14
|
+
export declare class GetOrganization<TOrganizationCustomFields = {}, TUserCustomFields = {}, TOrganizationMembershipCustomFields = {}> extends BaseUseCase<GetOrganizationInput, GetOrganizationOutput & TOrganizationCustomFields, DomainError, TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields> implements IGetOrganization {
|
|
15
|
+
constructor(adapters: Adapters<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>, toolkitOptions?: ToolkitOptions<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>);
|
|
16
|
+
protected executeBusinessLogic(input: GetOrganizationInput, _context: OperationContext): Promise<Result<GetOrganizationOutput & TOrganizationCustomFields, DomainError>>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=GetOrganization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GetOrganization.d.ts","sourceRoot":"","sources":["../../../../src/organizations/use-cases/get-organization/GetOrganization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EACR,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EACnB,MAAM,gDAAgD,CAAC;AAKxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAChG,OAAO,EACH,KAAK,WAAW,EAGnB,MAAM,sDAAsD,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;;;;GAMG;AACH,qBAAa,eAAe,CAEpB,yBAAyB,GAAG,EAAE,EAE9B,iBAAiB,GAAG,EAAE,EAEtB,mCAAmC,GAAG,EAAE,CAE5C,SAAQ,WAAW,CACf,oBAAoB,EACpB,qBAAqB,GAAG,yBAAyB,EACjD,WAAW,EACX,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CAEvC,YAAW,gBAAgB;gBAGvB,QAAQ,EAAE,QAAQ,CACd,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC,EACD,cAAc,CAAC,EAAE,cAAc,CAC3B,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CACtC;cAwBW,oBAAoB,CAChC,KAAK,EAAE,oBAAoB,EAC3B,QAAQ,EAAE,gBAAgB,GAC3B,OAAO,CAAC,MAAM,CAAC,qBAAqB,GAAG,yBAAyB,EAAE,WAAW,CAAC,CAAC;CA+CrF"}
|