@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,51 @@
|
|
|
1
|
+
import { GetOrganizationInputSchema, GetOrganizationOutputSchema } from '@multitenantkit/domain-contracts/organizations';
|
|
2
|
+
import { NotFoundError, UnauthorizedError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
3
|
+
import { Result } from '../../../shared/result/Result';
|
|
4
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
5
|
+
/**
|
|
6
|
+
* GetOrganization use case
|
|
7
|
+
* Handles the business logic for retrieving a organization by ID
|
|
8
|
+
*
|
|
9
|
+
* Generic support for custom fields:
|
|
10
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
11
|
+
*/
|
|
12
|
+
export class GetOrganization extends BaseUseCase {
|
|
13
|
+
constructor(adapters, toolkitOptions) {
|
|
14
|
+
// Extract custom schema
|
|
15
|
+
const customSchema = toolkitOptions?.organizations?.customFields?.customSchema;
|
|
16
|
+
// Build output schema with custom fields if provided
|
|
17
|
+
const outputSchema = (customSchema
|
|
18
|
+
? GetOrganizationOutputSchema.merge(customSchema)
|
|
19
|
+
: GetOrganizationOutputSchema);
|
|
20
|
+
super('organization-getOrganization', adapters, toolkitOptions, GetOrganizationInputSchema, outputSchema, 'Failed to retrieve organization');
|
|
21
|
+
}
|
|
22
|
+
async executeBusinessLogic(input, _context) {
|
|
23
|
+
// 1. Find organization by ID
|
|
24
|
+
const organization = await this.adapters.persistence.organizationRepository.findById(input.organizationId);
|
|
25
|
+
if (!organization) {
|
|
26
|
+
return Result.fail(new NotFoundError('Organization', input.organizationId));
|
|
27
|
+
}
|
|
28
|
+
const getUserResult = await this.getUserFromExternalId(input.principalExternalId);
|
|
29
|
+
if (getUserResult.isFailure) {
|
|
30
|
+
return Result.fail(getUserResult.getError());
|
|
31
|
+
}
|
|
32
|
+
const existingUser = getUserResult.getValue();
|
|
33
|
+
// 2. Verify permissions - organization owner or active organization members can access organization details
|
|
34
|
+
const isOwner = organization.ownerUserId === existingUser.id;
|
|
35
|
+
let isActiveMember = false;
|
|
36
|
+
if (!isOwner) {
|
|
37
|
+
const membershipDB = await this.adapters.persistence.organizationMembershipRepository.findByUserIdAndOrganizationId(existingUser.id, input.organizationId);
|
|
38
|
+
isActiveMember =
|
|
39
|
+
!!membershipDB &&
|
|
40
|
+
!!membershipDB.joinedAt &&
|
|
41
|
+
!membershipDB.leftAt &&
|
|
42
|
+
!membershipDB.deletedAt;
|
|
43
|
+
}
|
|
44
|
+
if (!isOwner && !isActiveMember) {
|
|
45
|
+
return Result.fail(new UnauthorizedError('Only organization owner or active organization members can access organization details'));
|
|
46
|
+
}
|
|
47
|
+
// 3. Return data; BaseUseCase will parse
|
|
48
|
+
return Result.ok(organization);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=GetOrganization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GetOrganization.js","sourceRoot":"","sources":["../../../../src/organizations/use-cases/get-organization/GetOrganization.ts"],"names":[],"mappings":"AAMA,OAAO,EACH,0BAA0B,EAC1B,2BAA2B,EAC9B,MAAM,gDAAgD,CAAC;AAExD,OAAO,EAEH,aAAa,EACb,iBAAiB,EACpB,MAAM,sDAAsD,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,OAAO,eAQT,SAAQ,WAOP;IAGD,YACI,QAIC,EACD,cAIC;QAED,wBAAwB;QACxB,MAAM,YAAY,GAAG,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,YAEnD,CAAC;QAEhB,qDAAqD;QACrD,MAAM,YAAY,GAAG,CAAC,YAAY;YAC9B,CAAC,CAAC,2BAA2B,CAAC,KAAK,CAAC,YAAY,CAAC;YACjD,CAAC,CAAC,2BAA2B,CAEhC,CAAC;QAEF,KAAK,CACD,8BAA8B,EAC9B,QAAQ,EACR,cAAc,EACd,0BAA0B,EAC1B,YAAY,EACZ,iCAAiC,CACpC,CAAC;IACN,CAAC;IAES,KAAK,CAAC,oBAAoB,CAChC,KAA2B,EAC3B,QAA0B;QAE1B,6BAA6B;QAC7B,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,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;QAE9C,4GAA4G;QAC5G,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,KAAK,YAAY,CAAC,EAAE,CAAC;QAE7D,IAAI,cAAc,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,YAAY,GACd,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,gCAAgC,CAAC,6BAA6B,CAC1F,YAAY,CAAC,EAAE,EACf,KAAK,CAAC,cAAc,CACvB,CAAC;YACN,cAAc;gBACV,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,YAAY,CAAC,QAAQ;oBACvB,CAAC,YAAY,CAAC,MAAM;oBACpB,CAAC,YAAY,CAAC,SAAS,CAAC;QAChC,CAAC;QAED,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;YAC9B,OAAO,MAAM,CAAC,IAAI,CACd,IAAI,iBAAiB,CACjB,wFAAwF,CAC3F,CACJ,CAAC;QACN,CAAC;QAED,yCAAyC;QACzC,OAAO,MAAM,CAAC,EAAE,CACZ,YAA4E,CAC/E,CAAC;IACN,CAAC;CACJ"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ArchiveOrganization } from './archive-organization/ArchiveOrganization';
|
|
2
|
+
export { CreateOrganization } from './create-organization/CreateOrganization';
|
|
3
|
+
export { DeleteOrganization } from './delete-organization/DeleteOrganization';
|
|
4
|
+
export { GetOrganization } from './get-organization/GetOrganization';
|
|
5
|
+
export { ListOrganizationMembers } from './list-organization-members/ListOrganizationMembers';
|
|
6
|
+
export { RestoreOrganization } from './restore-organization/RestoreOrganization';
|
|
7
|
+
export { TransferOrganizationOwnership } from './transfer-organization-ownership/TransferOrganizationOwnership';
|
|
8
|
+
export { UpdateOrganization } from './update-organization/UpdateOrganization';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/organizations/use-cases/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,iEAAiE,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Use Cases
|
|
2
|
+
export { ArchiveOrganization } from './archive-organization/ArchiveOrganization';
|
|
3
|
+
export { CreateOrganization } from './create-organization/CreateOrganization';
|
|
4
|
+
export { DeleteOrganization } from './delete-organization/DeleteOrganization';
|
|
5
|
+
export { GetOrganization } from './get-organization/GetOrganization';
|
|
6
|
+
export { ListOrganizationMembers } from './list-organization-members/ListOrganizationMembers';
|
|
7
|
+
export { RestoreOrganization } from './restore-organization/RestoreOrganization';
|
|
8
|
+
export { TransferOrganizationOwnership } from './transfer-organization-ownership/TransferOrganizationOwnership';
|
|
9
|
+
export { UpdateOrganization } from './update-organization/UpdateOrganization';
|
|
10
|
+
// DTOs
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/organizations/use-cases/index.ts"],"names":[],"mappings":"AAAA,YAAY;AAEZ,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AAC9F,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,iEAAiE,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAE9E,OAAO"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Adapters } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { OrganizationMembershipSchema } from '@multitenantkit/domain-contracts/organization-memberships';
|
|
3
|
+
import type { IListOrganizationMembers } from '@multitenantkit/domain-contracts/organizations';
|
|
4
|
+
import { ListOrganizationMembersInputSchema, OrganizationSchema } from '@multitenantkit/domain-contracts/organizations';
|
|
5
|
+
import type { OperationContext, ToolkitOptions } from '@multitenantkit/domain-contracts/shared';
|
|
6
|
+
import { type DomainError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
7
|
+
import { UserSchema } from '@multitenantkit/domain-contracts/users';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { Result } from '../../../shared/result/Result';
|
|
10
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
11
|
+
/**
|
|
12
|
+
* ListOrganizationMembers use case
|
|
13
|
+
* Handles business logic for listing organization members with optional status filter
|
|
14
|
+
*
|
|
15
|
+
* Generic support for custom fields:
|
|
16
|
+
* @template UCF - Zod schema for User custom fields (default: empty object schema)
|
|
17
|
+
* @template TCF - Zod schema for Organization custom fields (default: empty object schema)
|
|
18
|
+
* @template MCF - Zod schema for OrganizationMembership custom fields (default: empty object schema)
|
|
19
|
+
*/
|
|
20
|
+
export declare class ListOrganizationMembers<UCF extends z.AnyZodObject = z.ZodObject<{}>, TCF extends z.AnyZodObject = z.ZodObject<{}>, MCF extends z.AnyZodObject = z.ZodObject<{}>> extends BaseUseCase<z.infer<typeof ListOrganizationMembersInputSchema>, {
|
|
21
|
+
items: Array<(z.infer<typeof OrganizationMembershipSchema> & z.infer<MCF>) & {
|
|
22
|
+
user: (z.infer<typeof UserSchema> & z.infer<UCF>) | null;
|
|
23
|
+
organization: z.infer<typeof OrganizationSchema> & z.infer<TCF>;
|
|
24
|
+
}>;
|
|
25
|
+
pagination: {
|
|
26
|
+
total: number;
|
|
27
|
+
page: number;
|
|
28
|
+
pageSize: number;
|
|
29
|
+
totalPages: number;
|
|
30
|
+
};
|
|
31
|
+
}, DomainError, z.infer<UCF>, z.infer<TCF>, z.infer<MCF>> implements IListOrganizationMembers {
|
|
32
|
+
constructor(adapters: Adapters<z.infer<UCF>, z.infer<TCF>, z.infer<MCF>>, toolkitOptions?: ToolkitOptions<z.infer<UCF>, z.infer<TCF>, z.infer<MCF>>);
|
|
33
|
+
protected executeBusinessLogic(input: z.infer<typeof ListOrganizationMembersInputSchema>, _context: OperationContext): Promise<Result<{
|
|
34
|
+
items: Array<(z.infer<typeof OrganizationMembershipSchema> & z.infer<MCF>) & {
|
|
35
|
+
user: z.infer<typeof UserSchema> & z.infer<UCF>;
|
|
36
|
+
organization: z.infer<typeof OrganizationSchema> & z.infer<TCF>;
|
|
37
|
+
}>;
|
|
38
|
+
pagination: {
|
|
39
|
+
total: number;
|
|
40
|
+
page: number;
|
|
41
|
+
pageSize: number;
|
|
42
|
+
totalPages: number;
|
|
43
|
+
};
|
|
44
|
+
}, DomainError>>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=ListOrganizationMembers.d.ts.map
|
package/dist/organizations/use-cases/list-organization-members/ListOrganizationMembers.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListOrganizationMembers.d.ts","sourceRoot":"","sources":["../../../../src/organizations/use-cases/list-organization-members/ListOrganizationMembers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { OrganizationMembershipSchema } from '@multitenantkit/domain-contracts/organization-memberships';
|
|
2
|
+
import { ListOrganizationMembersInputSchema, OrganizationSchema } from '@multitenantkit/domain-contracts/organizations';
|
|
3
|
+
import { NotFoundError, UnauthorizedError, ValidationError } from '@multitenantkit/domain-contracts/shared/errors/index';
|
|
4
|
+
import { UserSchema } from '@multitenantkit/domain-contracts/users';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { Result } from '../../../shared/result/Result';
|
|
7
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
8
|
+
/**
|
|
9
|
+
* Helper to merge base Zod object with optional custom-fields Zod object.
|
|
10
|
+
* If custom schema is provided, it merges with base schema (custom fields win on conflicts).
|
|
11
|
+
* If custom schema is wrapped in ZodEffects, unwraps it first.
|
|
12
|
+
*/
|
|
13
|
+
const mergeCF = (base, cf) => {
|
|
14
|
+
if (!cf)
|
|
15
|
+
return base;
|
|
16
|
+
// Unwrap ZodEffects if present
|
|
17
|
+
const unwrapped = cf._def?.schema ?? cf;
|
|
18
|
+
return base.merge(unwrapped);
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* ListOrganizationMembers use case
|
|
22
|
+
* Handles business logic for listing organization members with optional status filter
|
|
23
|
+
*
|
|
24
|
+
* Generic support for custom fields:
|
|
25
|
+
* @template UCF - Zod schema for User custom fields (default: empty object schema)
|
|
26
|
+
* @template TCF - Zod schema for Organization custom fields (default: empty object schema)
|
|
27
|
+
* @template MCF - Zod schema for OrganizationMembership custom fields (default: empty object schema)
|
|
28
|
+
*/
|
|
29
|
+
export class ListOrganizationMembers extends BaseUseCase {
|
|
30
|
+
constructor(adapters, toolkitOptions) {
|
|
31
|
+
// 1) Read optional custom-field schemas from toolkitOptions
|
|
32
|
+
const userCF = toolkitOptions?.users?.customFields?.customSchema;
|
|
33
|
+
const organizationCF = toolkitOptions?.organizations?.customFields?.customSchema;
|
|
34
|
+
const membershipCF = toolkitOptions?.organizationMemberships?.customFields?.customSchema;
|
|
35
|
+
// 2) Merge base + custom schemas (custom wins on conflicting keys)
|
|
36
|
+
const UserWithCF = mergeCF(UserSchema, userCF);
|
|
37
|
+
const OrganizationWithCF = mergeCF(OrganizationSchema, organizationCF);
|
|
38
|
+
const MembershipWithCF = mergeCF(OrganizationMembershipSchema, membershipCF);
|
|
39
|
+
// 3) Build the final member schema including nested user/organization with their customs
|
|
40
|
+
const MemberWithAll = MembershipWithCF.extend({
|
|
41
|
+
user: UserWithCF.nullable(),
|
|
42
|
+
organization: OrganizationWithCF
|
|
43
|
+
});
|
|
44
|
+
// 4) Output schema is paginated result with items array and pagination metadata
|
|
45
|
+
const OutputSchema = z.object({
|
|
46
|
+
items: z.array(MemberWithAll),
|
|
47
|
+
pagination: z.object({
|
|
48
|
+
total: z.number().int().min(0),
|
|
49
|
+
page: z.number().int().min(1),
|
|
50
|
+
pageSize: z.number().int().min(1),
|
|
51
|
+
totalPages: z.number().int().min(0)
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
super('organization-listOrganizationMembers', adapters, toolkitOptions, ListOrganizationMembersInputSchema, OutputSchema, 'Failed to list organization members');
|
|
55
|
+
}
|
|
56
|
+
async executeBusinessLogic(input, _context) {
|
|
57
|
+
// 1. Validate organization exists
|
|
58
|
+
const organization = await this.adapters.persistence.organizationRepository.findById(input.organizationId);
|
|
59
|
+
if (!organization) {
|
|
60
|
+
return Result.fail(new NotFoundError('Organization', input.organizationId));
|
|
61
|
+
}
|
|
62
|
+
const getUserResult = await this.getUserFromExternalId(input.principalExternalId);
|
|
63
|
+
if (getUserResult.isFailure) {
|
|
64
|
+
return Result.fail(getUserResult.getError());
|
|
65
|
+
}
|
|
66
|
+
const existingUser = getUserResult.getValue();
|
|
67
|
+
// 2. Validate user has access to organization (must be a member or owner)
|
|
68
|
+
const userMembership = await this.adapters.persistence.organizationMembershipRepository.findByUserIdAndOrganizationId(existingUser.id, input.organizationId);
|
|
69
|
+
const isOwner = organization.ownerUserId === existingUser.id;
|
|
70
|
+
const isAdmin = !!userMembership &&
|
|
71
|
+
userMembership.roleCode === 'admin' &&
|
|
72
|
+
!!userMembership.joinedAt &&
|
|
73
|
+
!userMembership.leftAt &&
|
|
74
|
+
!userMembership.deletedAt;
|
|
75
|
+
const isActiveMember = !!userMembership &&
|
|
76
|
+
!!userMembership.joinedAt &&
|
|
77
|
+
!userMembership.leftAt &&
|
|
78
|
+
!userMembership.deletedAt;
|
|
79
|
+
if (!isOwner && !isActiveMember) {
|
|
80
|
+
return Result.fail(new UnauthorizedError('Only organization members can view organization members'));
|
|
81
|
+
}
|
|
82
|
+
// 3. Apply role-based filtering
|
|
83
|
+
// - Members: can only see active members
|
|
84
|
+
// - Admins and Owners: can see all (active + pending + removed)
|
|
85
|
+
const filterOptions = { ...input.options };
|
|
86
|
+
if (isOwner || isAdmin) {
|
|
87
|
+
// Owners and admins can see all member statuses if not specified
|
|
88
|
+
if (!filterOptions.includeActive &&
|
|
89
|
+
!filterOptions.includePending &&
|
|
90
|
+
!filterOptions.includeRemoved) {
|
|
91
|
+
filterOptions.includeActive = true;
|
|
92
|
+
filterOptions.includePending = true;
|
|
93
|
+
filterOptions.includeRemoved = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Regular members can only see active members
|
|
98
|
+
filterOptions.includeActive = true;
|
|
99
|
+
filterOptions.includePending = false;
|
|
100
|
+
filterOptions.includeRemoved = false;
|
|
101
|
+
}
|
|
102
|
+
// 4. Get organization members with user information (paginated)
|
|
103
|
+
try {
|
|
104
|
+
const paginatedResult = await this.adapters.persistence.organizationMembershipRepository.findByOrganizationWithUserInfoPaginated(input.organizationId, filterOptions);
|
|
105
|
+
// 4. Transform items and return paginated result
|
|
106
|
+
// Note: membersWithUserInfo has flat structure with membership fields at root + nested user/organization
|
|
107
|
+
const transformedItems = paginatedResult.items.map((memberInfo) => ({
|
|
108
|
+
...memberInfo, // Spread all fields (membership + user + organization + custom fields)
|
|
109
|
+
joinedAt: memberInfo.joinedAt ?? undefined,
|
|
110
|
+
leftAt: memberInfo.leftAt ?? undefined
|
|
111
|
+
}));
|
|
112
|
+
const output = {
|
|
113
|
+
items: transformedItems,
|
|
114
|
+
pagination: {
|
|
115
|
+
total: paginatedResult.total,
|
|
116
|
+
page: paginatedResult.page,
|
|
117
|
+
pageSize: paginatedResult.pageSize,
|
|
118
|
+
totalPages: paginatedResult.totalPages
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
return Result.ok(output);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return Result.fail(new ValidationError('Failed to list organization members', undefined, {
|
|
125
|
+
originalError: error
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=ListOrganizationMembers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListOrganizationMembers.js","sourceRoot":"","sources":["../../../../src/organizations/use-cases/list-organization-members/ListOrganizationMembers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { IRestoreOrganization, OperationContext, Organization, RestoreOrganizationInput, ToolkitOptions } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { type Adapters, type NotFoundError, ValidationError } from '@multitenantkit/domain-contracts';
|
|
3
|
+
import { Result } from '../../../shared/result';
|
|
4
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
5
|
+
/**
|
|
6
|
+
* RestoreOrganization use case
|
|
7
|
+
* Handles the business logic for restoring an archived organization
|
|
8
|
+
* Clears the archivedAt timestamp to make the organization active again
|
|
9
|
+
*
|
|
10
|
+
* Business rules:
|
|
11
|
+
* - Only the organization owner can restore the organization
|
|
12
|
+
* - The owner must be an active user (not soft-deleted)
|
|
13
|
+
* - The organization must be currently archived (archivedAt set)
|
|
14
|
+
* - Deleted organizations (deletedAt set) cannot be restored through this use case
|
|
15
|
+
*
|
|
16
|
+
* Membership behavior:
|
|
17
|
+
* - Does NOT modify organization memberships during restore
|
|
18
|
+
* Active memberships automatically become active again when organization is restored
|
|
19
|
+
*
|
|
20
|
+
* Generic support for custom fields:
|
|
21
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
22
|
+
* @template TUserCustomFields - User custom fields (for toolkit options compatibility)
|
|
23
|
+
* @template TOrganizationMembershipCustomFields - Membership custom fields (for toolkit options compatibility)
|
|
24
|
+
*/
|
|
25
|
+
export declare class RestoreOrganization<TOrganizationCustomFields = {}, TUserCustomFields = {}, TOrganizationMembershipCustomFields = {}> extends BaseUseCase<RestoreOrganizationInput, Organization & TOrganizationCustomFields, ValidationError | NotFoundError, TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields> implements IRestoreOrganization {
|
|
26
|
+
private readonly customSchema?;
|
|
27
|
+
constructor(adapters: Adapters<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>, toolkitOptions?: ToolkitOptions<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>);
|
|
28
|
+
protected executeBusinessLogic(input: RestoreOrganizationInput, context: OperationContext): Promise<Result<Organization & TOrganizationCustomFields, ValidationError | NotFoundError>>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=RestoreOrganization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RestoreOrganization.d.ts","sourceRoot":"","sources":["../../../../src/organizations/use-cases/restore-organization/RestoreOrganization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,wBAAwB,EACxB,cAAc,EACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACH,KAAK,QAAQ,EACb,KAAK,aAAa,EAGlB,eAAe,EAClB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAkB,MAAM,0BAA0B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;GAmBG;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,EAC/B,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,oBAAoB,CAChC,KAAK,EAAE,wBAAwB,EAC/B,OAAO,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,GAAG,yBAAyB,EAAE,eAAe,GAAG,aAAa,CAAC,CAAC;CAiHhG"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { OrganizationSchema, RestoreOrganizationInputSchema, ValidationError } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { Result } from '../../../shared/result';
|
|
3
|
+
import { BaseUseCase, UseCaseHelpers } from '../../../shared/use-case';
|
|
4
|
+
/**
|
|
5
|
+
* RestoreOrganization use case
|
|
6
|
+
* Handles the business logic for restoring an archived organization
|
|
7
|
+
* Clears the archivedAt timestamp to make the organization active again
|
|
8
|
+
*
|
|
9
|
+
* Business rules:
|
|
10
|
+
* - Only the organization owner can restore the organization
|
|
11
|
+
* - The owner must be an active user (not soft-deleted)
|
|
12
|
+
* - The organization must be currently archived (archivedAt set)
|
|
13
|
+
* - Deleted organizations (deletedAt set) cannot be restored through this use case
|
|
14
|
+
*
|
|
15
|
+
* Membership behavior:
|
|
16
|
+
* - Does NOT modify organization memberships during restore
|
|
17
|
+
* Active memberships automatically become active again when organization is restored
|
|
18
|
+
*
|
|
19
|
+
* Generic support for custom fields:
|
|
20
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
21
|
+
* @template TUserCustomFields - User custom fields (for toolkit options compatibility)
|
|
22
|
+
* @template TOrganizationMembershipCustomFields - Membership custom fields (for toolkit options compatibility)
|
|
23
|
+
*/
|
|
24
|
+
export class RestoreOrganization extends BaseUseCase {
|
|
25
|
+
customSchema;
|
|
26
|
+
constructor(adapters, toolkitOptions) {
|
|
27
|
+
// Extract custom schema from toolkit options
|
|
28
|
+
const customSchema = toolkitOptions?.organizations?.customFields?.customSchema;
|
|
29
|
+
// Extend output schema with custom fields if provided
|
|
30
|
+
const outputSchema = customSchema
|
|
31
|
+
? OrganizationSchema.merge(customSchema)
|
|
32
|
+
: OrganizationSchema;
|
|
33
|
+
super('organization-restoreOrganization', adapters, toolkitOptions, RestoreOrganizationInputSchema, outputSchema, 'Failed to restore organization');
|
|
34
|
+
this.customSchema = customSchema;
|
|
35
|
+
}
|
|
36
|
+
async executeBusinessLogic(input, context) {
|
|
37
|
+
// 1. Find existing organization
|
|
38
|
+
const organizationResult = await UseCaseHelpers.findByIdOrFail(this.adapters.persistence.organizationRepository, input.organizationId, 'Organization');
|
|
39
|
+
if (organizationResult.isFailure) {
|
|
40
|
+
return organizationResult;
|
|
41
|
+
}
|
|
42
|
+
const existingOrganization = organizationResult.getValue();
|
|
43
|
+
// 2. Check if organization is NOT archived (cannot restore a non-archived organization)
|
|
44
|
+
if (!existingOrganization.archivedAt) {
|
|
45
|
+
return Result.fail(new ValidationError('Organization is not archived', 'organizationId'));
|
|
46
|
+
}
|
|
47
|
+
// 3. Check if organization is deleted (deleted organizations cannot be restored)
|
|
48
|
+
if (existingOrganization.deletedAt) {
|
|
49
|
+
return Result.fail(new ValidationError('Deleted organizations cannot be restored through this use case', 'organizationId'));
|
|
50
|
+
}
|
|
51
|
+
// 4. Authorize: Only the owner can restore the organization
|
|
52
|
+
if (existingOrganization.ownerUserId !== context.actorUserId) {
|
|
53
|
+
return Result.fail(new ValidationError('Only the organization owner can restore the organization', 'actorUserId'));
|
|
54
|
+
}
|
|
55
|
+
// 5. Verify the owner is an active user (not soft-deleted)
|
|
56
|
+
const ownerResult = await UseCaseHelpers.findByIdOrFail(this.adapters.persistence.userRepository, existingOrganization.ownerUserId, 'User');
|
|
57
|
+
if (ownerResult.isFailure) {
|
|
58
|
+
return ownerResult;
|
|
59
|
+
}
|
|
60
|
+
const owner = ownerResult.getValue();
|
|
61
|
+
if (owner.deletedAt) {
|
|
62
|
+
return Result.fail(new ValidationError('Cannot restore organization: owner user is deleted', 'ownerUserId'));
|
|
63
|
+
}
|
|
64
|
+
// 6. Build restored organization data (clear archivedAt)
|
|
65
|
+
const now = this.adapters.system.clock.now();
|
|
66
|
+
const restoredOrganizationData = {
|
|
67
|
+
...existingOrganization,
|
|
68
|
+
archivedAt: undefined,
|
|
69
|
+
updatedAt: now
|
|
70
|
+
};
|
|
71
|
+
// 7. Validate the updated data
|
|
72
|
+
const validationSchema = this.customSchema
|
|
73
|
+
? OrganizationSchema.strip().and(this.customSchema.strip())
|
|
74
|
+
: OrganizationSchema.strip();
|
|
75
|
+
const validationResult = validationSchema.safeParse(restoredOrganizationData);
|
|
76
|
+
if (!validationResult.success) {
|
|
77
|
+
const firstError = validationResult.error.errors[0];
|
|
78
|
+
return Result.fail(new ValidationError(firstError.message, firstError.path.join('.')));
|
|
79
|
+
}
|
|
80
|
+
// Use validated and potentially transformed data from Zod
|
|
81
|
+
const restoredOrganization = validationResult.data;
|
|
82
|
+
// 8. Persist the restored organization using Unit of Work (transaction) with audit context
|
|
83
|
+
const auditContext = UseCaseHelpers.enrichAuditContext(context, 'RESTORE_ORGANIZATION', input.organizationId);
|
|
84
|
+
await this.adapters.persistence.uow.transaction(async (repos) => {
|
|
85
|
+
// 8.1. Save the restored organization
|
|
86
|
+
await repos.organizations.update(restoredOrganization, auditContext);
|
|
87
|
+
// 8.2. Do NOT modify organization memberships
|
|
88
|
+
// Rationale: Memberships with deletedAt=null will automatically become active
|
|
89
|
+
// Active memberships automatically become active again when organization is restored
|
|
90
|
+
// - Active memberships (deletedAt=null) → become active again automatically
|
|
91
|
+
// - Deleted memberships (deletedAt set) → remain deleted
|
|
92
|
+
// - Left memberships (leftAt set) → remain as historical records
|
|
93
|
+
});
|
|
94
|
+
// 9. Return restored organization
|
|
95
|
+
return Result.ok(restoredOrganization);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=RestoreOrganization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RestoreOrganization.js","sourceRoot":"","sources":["../../../../src/organizations/use-cases/restore-organization/RestoreOrganization.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ITransferOrganizationOwnership, OperationContext, ToolkitOptions, TransferOrganizationOwnershipInput, TransferOrganizationOwnershipOutput } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { type Adapters, type NotFoundError, ValidationError } from '@multitenantkit/domain-contracts';
|
|
3
|
+
import { Result } from '../../../shared/result';
|
|
4
|
+
import { BaseUseCase } from '../../../shared/use-case';
|
|
5
|
+
/**
|
|
6
|
+
* TransferOrganizationOwnership use case
|
|
7
|
+
* Handles the business logic for transferring organization ownership from one user to another
|
|
8
|
+
*
|
|
9
|
+
* Business rules:
|
|
10
|
+
* - Only the current owner can transfer ownership
|
|
11
|
+
* - Both current and new owner must be active users (not soft-deleted)
|
|
12
|
+
* - Organization must be active (not soft-deleted)
|
|
13
|
+
* - Both users must have active memberships in the organization (not deleted, not left)
|
|
14
|
+
* - New owner must be different from current owner
|
|
15
|
+
*
|
|
16
|
+
* Effects:
|
|
17
|
+
* - Updates organization.ownerUserId to the new owner
|
|
18
|
+
* - Updates old owner's membership roleCode to 'member'
|
|
19
|
+
* - Updates new owner's membership roleCode to 'owner'
|
|
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 declare class TransferOrganizationOwnership<TOrganizationCustomFields = {}, TUserCustomFields = {}, TOrganizationMembershipCustomFields = {}> extends BaseUseCase<TransferOrganizationOwnershipInput, TransferOrganizationOwnershipOutput, ValidationError | NotFoundError, TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields> implements ITransferOrganizationOwnership {
|
|
27
|
+
constructor(adapters: Adapters<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>, toolkitOptions?: ToolkitOptions<TUserCustomFields, TOrganizationCustomFields, TOrganizationMembershipCustomFields>);
|
|
28
|
+
protected executeBusinessLogic(input: TransferOrganizationOwnershipInput, context: OperationContext): Promise<Result<TransferOrganizationOwnershipOutput, ValidationError | NotFoundError>>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=TransferOrganizationOwnership.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TransferOrganizationOwnership.d.ts","sourceRoot":"","sources":["../../../../src/organizations/use-cases/transfer-organization-ownership/TransferOrganizationOwnership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,8BAA8B,EAC9B,gBAAgB,EAGhB,cAAc,EACd,kCAAkC,EAClC,mCAAmC,EACtC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACH,KAAK,QAAQ,EACb,KAAK,aAAa,EAKlB,eAAe,EAClB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAkB,MAAM,0BAA0B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,6BAA6B,CAElC,yBAAyB,GAAG,EAAE,EAE9B,iBAAiB,GAAG,EAAE,EAEtB,mCAAmC,GAAG,EAAE,CAE5C,SAAQ,WAAW,CACf,kCAAkC,EAClC,mCAAmC,EACnC,eAAe,GAAG,aAAa,EAC/B,iBAAiB,EACjB,yBAAyB,EACzB,mCAAmC,CAEvC,YAAW,8BAA8B;gBAGrC,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,kCAAkC,EACzC,OAAO,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,mCAAmC,EAAE,eAAe,GAAG,aAAa,CAAC,CAAC;CAyM3F"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { OrganizationMembershipSchema, OrganizationSchema, TransferOrganizationOwnershipInputSchema, TransferOrganizationOwnershipOutputSchema, ValidationError } from '@multitenantkit/domain-contracts';
|
|
2
|
+
import { Result } from '../../../shared/result';
|
|
3
|
+
import { BaseUseCase, UseCaseHelpers } from '../../../shared/use-case';
|
|
4
|
+
/**
|
|
5
|
+
* TransferOrganizationOwnership use case
|
|
6
|
+
* Handles the business logic for transferring organization ownership from one user to another
|
|
7
|
+
*
|
|
8
|
+
* Business rules:
|
|
9
|
+
* - Only the current owner can transfer ownership
|
|
10
|
+
* - Both current and new owner must be active users (not soft-deleted)
|
|
11
|
+
* - Organization must be active (not soft-deleted)
|
|
12
|
+
* - Both users must have active memberships in the organization (not deleted, not left)
|
|
13
|
+
* - New owner must be different from current owner
|
|
14
|
+
*
|
|
15
|
+
* Effects:
|
|
16
|
+
* - Updates organization.ownerUserId to the new owner
|
|
17
|
+
* - Updates old owner's membership roleCode to 'member'
|
|
18
|
+
* - Updates new owner's membership roleCode to 'owner'
|
|
19
|
+
*
|
|
20
|
+
* Generic support for custom fields:
|
|
21
|
+
* @template TOrganizationCustomFields - Custom fields added to Organization
|
|
22
|
+
* @template TUserCustomFields - User custom fields (for toolkit options compatibility)
|
|
23
|
+
* @template TOrganizationMembershipCustomFields - Membership custom fields (for toolkit options compatibility)
|
|
24
|
+
*/
|
|
25
|
+
export class TransferOrganizationOwnership extends BaseUseCase {
|
|
26
|
+
constructor(adapters, toolkitOptions) {
|
|
27
|
+
// Extract custom schema if provided
|
|
28
|
+
const customSchema = toolkitOptions?.organizations?.customFields?.customSchema;
|
|
29
|
+
// Build output schema with custom fields if provided
|
|
30
|
+
const outputSchema = (customSchema
|
|
31
|
+
? TransferOrganizationOwnershipOutputSchema.merge(customSchema)
|
|
32
|
+
: TransferOrganizationOwnershipOutputSchema);
|
|
33
|
+
super('organization-transferOrganizationOwnership', adapters, toolkitOptions, TransferOrganizationOwnershipInputSchema, outputSchema, 'Failed to transfer organization ownership');
|
|
34
|
+
}
|
|
35
|
+
async executeBusinessLogic(input, context) {
|
|
36
|
+
// 1. Find existing organization
|
|
37
|
+
const organizationResult = await UseCaseHelpers.findByIdOrFail(this.adapters.persistence.organizationRepository, input.organizationId, 'Organization');
|
|
38
|
+
if (organizationResult.isFailure) {
|
|
39
|
+
return organizationResult;
|
|
40
|
+
}
|
|
41
|
+
const existingOrganization = organizationResult.getValue();
|
|
42
|
+
// 2. Check if organization is deleted (cannot transfer ownership of deleted organization)
|
|
43
|
+
if (existingOrganization.deletedAt) {
|
|
44
|
+
return Result.fail(new ValidationError('Cannot transfer ownership of a deleted organization', 'organizationId'));
|
|
45
|
+
}
|
|
46
|
+
// 3. Check if organization is archived (cannot transfer ownership of archived organization)
|
|
47
|
+
if (existingOrganization.archivedAt) {
|
|
48
|
+
return Result.fail(new ValidationError('Cannot transfer ownership of an archived organization', 'organizationId'));
|
|
49
|
+
}
|
|
50
|
+
// 4. Authorize: Only the current owner can transfer ownership
|
|
51
|
+
if (existingOrganization.ownerUserId !== context.actorUserId) {
|
|
52
|
+
return Result.fail(new ValidationError('Only the current organization owner can transfer ownership', 'actorUserId'));
|
|
53
|
+
}
|
|
54
|
+
// 5. Validate: New owner must be different from current owner
|
|
55
|
+
if (input.newOwnerId === existingOrganization.ownerUserId) {
|
|
56
|
+
return Result.fail(new ValidationError('New owner must be different from current owner', 'newOwnerId'));
|
|
57
|
+
}
|
|
58
|
+
// 6. Verify the current owner is an active user (not soft-deleted)
|
|
59
|
+
const currentOwnerResult = await UseCaseHelpers.findByIdOrFail(this.adapters.persistence.userRepository, existingOrganization.ownerUserId, 'User');
|
|
60
|
+
if (currentOwnerResult.isFailure) {
|
|
61
|
+
return currentOwnerResult;
|
|
62
|
+
}
|
|
63
|
+
const currentOwner = currentOwnerResult.getValue();
|
|
64
|
+
if (currentOwner.deletedAt) {
|
|
65
|
+
return Result.fail(new ValidationError('Current owner user is deleted', 'ownerUserId'));
|
|
66
|
+
}
|
|
67
|
+
// 7. Verify the new owner is an active user (not soft-deleted)
|
|
68
|
+
const newOwnerResult = await UseCaseHelpers.findByIdOrFail(this.adapters.persistence.userRepository, input.newOwnerId, 'User');
|
|
69
|
+
if (newOwnerResult.isFailure) {
|
|
70
|
+
return newOwnerResult;
|
|
71
|
+
}
|
|
72
|
+
const newOwner = newOwnerResult.getValue();
|
|
73
|
+
if (newOwner.deletedAt) {
|
|
74
|
+
return Result.fail(new ValidationError('New owner user is deleted', 'newOwnerId'));
|
|
75
|
+
}
|
|
76
|
+
// 8. Verify current owner has an active membership
|
|
77
|
+
const currentOwnerMemberships = await this.adapters.persistence.organizationMembershipRepository.findByUser(existingOrganization.ownerUserId);
|
|
78
|
+
const currentOwnerMembership = currentOwnerMemberships.find((m) => m.organizationId === input.organizationId && !m.deletedAt && !m.leftAt);
|
|
79
|
+
if (!currentOwnerMembership) {
|
|
80
|
+
return Result.fail(new ValidationError('Current owner does not have an active membership in the organization', 'ownerUserId'));
|
|
81
|
+
}
|
|
82
|
+
// 9. Verify new owner has an active membership
|
|
83
|
+
const newOwnerMemberships = await this.adapters.persistence.organizationMembershipRepository.findByUser(input.newOwnerId);
|
|
84
|
+
const newOwnerMembership = newOwnerMemberships.find((m) => m.organizationId === input.organizationId && !m.deletedAt && !m.leftAt);
|
|
85
|
+
if (!newOwnerMembership) {
|
|
86
|
+
return Result.fail(new ValidationError('New owner does not have an active membership in the organization', 'newOwnerId'));
|
|
87
|
+
}
|
|
88
|
+
// 10. Build updated organization data
|
|
89
|
+
const now = this.adapters.system.clock.now();
|
|
90
|
+
const updatedOrganizationData = {
|
|
91
|
+
...existingOrganization,
|
|
92
|
+
ownerUserId: input.newOwnerId,
|
|
93
|
+
updatedAt: now
|
|
94
|
+
};
|
|
95
|
+
// 11. Validate the updated organization data
|
|
96
|
+
const validationResult = OrganizationSchema.strip().safeParse(updatedOrganizationData);
|
|
97
|
+
if (!validationResult.success) {
|
|
98
|
+
const firstError = validationResult.error.errors[0];
|
|
99
|
+
return Result.fail(new ValidationError(firstError.message, firstError.path.join('.')));
|
|
100
|
+
}
|
|
101
|
+
const updatedOrganization = validationResult.data;
|
|
102
|
+
// 12. Build updated memberships
|
|
103
|
+
const updatedOldOwnerMembership = {
|
|
104
|
+
...currentOwnerMembership,
|
|
105
|
+
roleCode: 'member',
|
|
106
|
+
updatedAt: now
|
|
107
|
+
};
|
|
108
|
+
const updatedNewOwnerMembership = {
|
|
109
|
+
...newOwnerMembership,
|
|
110
|
+
roleCode: 'owner',
|
|
111
|
+
updatedAt: now
|
|
112
|
+
};
|
|
113
|
+
// Validate membership data
|
|
114
|
+
const oldOwnerMembershipValidation = OrganizationMembershipSchema.strip().safeParse(updatedOldOwnerMembership);
|
|
115
|
+
if (!oldOwnerMembershipValidation.success) {
|
|
116
|
+
const firstError = oldOwnerMembershipValidation.error.errors[0];
|
|
117
|
+
return Result.fail(new ValidationError(firstError.message, firstError.path.join('.')));
|
|
118
|
+
}
|
|
119
|
+
const newOwnerMembershipValidation = OrganizationMembershipSchema.strip().safeParse(updatedNewOwnerMembership);
|
|
120
|
+
if (!newOwnerMembershipValidation.success) {
|
|
121
|
+
const firstError = newOwnerMembershipValidation.error.errors[0];
|
|
122
|
+
return Result.fail(new ValidationError(firstError.message, firstError.path.join('.')));
|
|
123
|
+
}
|
|
124
|
+
// 13. Persist all changes using Unit of Work (transaction) with audit context
|
|
125
|
+
const auditContext = UseCaseHelpers.enrichAuditContext(context, 'TRANSFER_ORGANIZATION_OWNERSHIP', input.organizationId);
|
|
126
|
+
await this.adapters.persistence.uow.transaction(async (repos) => {
|
|
127
|
+
// 13.1. Update organization with new owner
|
|
128
|
+
await repos.organizations.update(updatedOrganization, auditContext);
|
|
129
|
+
// 13.2. Update old owner membership to 'member' role
|
|
130
|
+
await repos.organizationMemberships.update(updatedOldOwnerMembership, auditContext);
|
|
131
|
+
// 13.3. Update new owner membership to 'owner' role
|
|
132
|
+
await repos.organizationMemberships.update(updatedNewOwnerMembership, auditContext);
|
|
133
|
+
});
|
|
134
|
+
// 14. Return success response with updated organization
|
|
135
|
+
// BaseUseCase will parse with output schema
|
|
136
|
+
return Result.ok(updatedOrganization);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=TransferOrganizationOwnership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TransferOrganizationOwnership.js","sourceRoot":"","sources":["../../../../src/organizations/use-cases/transfer-organization-ownership/TransferOrganizationOwnership.ts"],"names":[],"mappings":"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"}
|