diff --git a/src/authorization/services/UserValidation.service.ts b/src/authorization/services/UserValidation.service.ts index 613fe80a..1cc8ef74 100644 --- a/src/authorization/services/UserValidation.service.ts +++ b/src/authorization/services/UserValidation.service.ts @@ -51,7 +51,14 @@ export class UserValidationService { user: User, authProviderPermissions: string[] | undefined ): RequestUser { - console.debug({list: this.config.superUserIds}, "super user ids"); + console.debug( + { + list: this.config.superUserIds, + userId: user.uuid, + userMail: user.email, + }, + "super user ids" + ); if (!user?.id) { throw new Error( "Unable to authenticate and register a valid user with this auth0 user" diff --git a/src/invitations/invitation.service.ts b/src/invitations/invitation.service.ts index 2b8da634..0be67cd1 100644 --- a/src/invitations/invitation.service.ts +++ b/src/invitations/invitation.service.ts @@ -280,6 +280,6 @@ export class InvitationService { orgId: invitation.organisationMembership.organisation.id, user: currentUser, }); - return this.invitationRepository.remove(invitation); + return this.invitationRepository.softRemove(invitation); } } diff --git a/src/organisation-memberships/entities/organisation-membership.entity.ts b/src/organisation-memberships/entities/organisation-membership.entity.ts index d2ff6176..f535c508 100644 --- a/src/organisation-memberships/entities/organisation-membership.entity.ts +++ b/src/organisation-memberships/entities/organisation-membership.entity.ts @@ -4,7 +4,6 @@ import {Type} from "class-transformer"; import { Column, CreateDateColumn, - DeleteDateColumn, Entity, Generated, Index, @@ -73,6 +72,7 @@ export class OrganisationMembership { @OneToMany(() => MembershipRole, (role) => role.membership, { eager: true, cascade: ["insert", "update"], + onDelete: "CASCADE", orphanedRowAction: "delete", }) @Type(() => MembershipRole) @@ -85,8 +85,4 @@ export class OrganisationMembership { @UpdateDateColumn() @ApiProperty() updateDate!: Date; - - @DeleteDateColumn() - @ApiPropertyOptional() - deletedDate?: Date; } diff --git a/src/organisation-memberships/organisation-memberships.controller.ts b/src/organisation-memberships/organisation-memberships.controller.ts index 0a3757e0..bda22d7f 100644 --- a/src/organisation-memberships/organisation-memberships.controller.ts +++ b/src/organisation-memberships/organisation-memberships.controller.ts @@ -54,17 +54,7 @@ export class OrganisationMembershipsController { @Param("membershipUuid") membershipUuid: string, @Request() request: RequestWithUser ): Promise { - const deleteResult = await this.omService.remove( - orgUuid, - membershipUuid, - request.user.id - ); - return { - result: - deleteResult !== undefined && - deleteResult.affected !== undefined && - deleteResult?.affected !== null && - deleteResult?.affected > 0, - }; + await this.omService.remove(orgUuid, membershipUuid, request.user.id); + return {result: true}; } } diff --git a/src/organisation-memberships/organisation-memberships.module.ts b/src/organisation-memberships/organisation-memberships.module.ts index 5164c7b7..887396a1 100644 --- a/src/organisation-memberships/organisation-memberships.module.ts +++ b/src/organisation-memberships/organisation-memberships.module.ts @@ -5,6 +5,7 @@ import {OrganisationMembershipsController} from "./organisation-memberships.cont import {OrganisationMembershipsService} from "./organisation-memberships.service.js"; import {Organisation} from "../organisation/entities/organisation.entity.js"; import {MembershipRole} from "../organisation/entities/member-role.entity.js"; +import {Invitation} from "../invitations/entities/invitation.entity.js"; @Module({ imports: [ @@ -12,6 +13,7 @@ import {MembershipRole} from "../organisation/entities/member-role.entity.js"; Organisation, OrganisationMembership, MembershipRole, + Invitation, ]), ], controllers: [OrganisationMembershipsController], diff --git a/src/organisation-memberships/organisation-memberships.service.ts b/src/organisation-memberships/organisation-memberships.service.ts index 7c917757..46f3986a 100644 --- a/src/organisation-memberships/organisation-memberships.service.ts +++ b/src/organisation-memberships/organisation-memberships.service.ts @@ -1,6 +1,7 @@ import {Injectable, NotFoundException} from "@nestjs/common"; import {InjectRepository} from "@nestjs/typeorm"; -import {DeleteResult, Repository} from "typeorm"; +import {Repository} from "typeorm"; +import {Invitation} from "../invitations/entities/invitation.entity.js"; import {Roles} from "../organisation/dto/RolesEnum.js"; import {MembershipRole} from "../organisation/entities/member-role.entity.js"; import {Organisation} from "../organisation/entities/organisation.entity.js"; @@ -13,7 +14,9 @@ export class OrganisationMembershipsService { @InjectRepository(Organisation) private orgRepo: Repository, @InjectRepository(OrganisationMembership) - private membershipRepo: Repository + private membershipRepo: Repository, + @InjectRepository(Invitation) + private invitationRepo: Repository ) {} private notFoundMessage = "Organisation not found or you are not owner of it"; @@ -39,7 +42,7 @@ export class OrganisationMembershipsService { if (!isAMember) { throw new Error("You are not a member of this organisation"); } - console.log("memberships", memberships); + return memberships; } async createOrUpdate( @@ -116,7 +119,7 @@ export class OrganisationMembershipsService { orgUuid: string, membershipUuid: string, currentUserId: number - ): Promise { + ): Promise { // find the org const org = await this.orgRepo.findOne({ where: { @@ -141,6 +144,10 @@ export class OrganisationMembershipsService { if (!membership) { throw new Error("Membership not found"); } - return await this.membershipRepo.delete(membership.id); + if (membership.invitations) { + await this.invitationRepo.softRemove(membership.invitations); + } + + await this.membershipRepo.remove(membership); } } diff --git a/src/organisation-subscriptions/organisation-subscriptions.service.ts b/src/organisation-subscriptions/organisation-subscriptions.service.ts index fc0f3e29..3ffc9473 100644 --- a/src/organisation-subscriptions/organisation-subscriptions.service.ts +++ b/src/organisation-subscriptions/organisation-subscriptions.service.ts @@ -211,14 +211,14 @@ export class OrganisationSubscriptionService { if (!result) { throw new NotFoundException("Subscription not found"); } + + await this.orgSubRepository.softRemove(result); await this.queue.add({ organisationUuid: result.organisation.uuid, subscriptionUuid: result.uuid, productKey: result.internalSku, active: false, }); - - await this.orgSubRepository.delete(result); return true; } } diff --git a/src/organisation/organisation.controller.ts b/src/organisation/organisation.controller.ts index 4a725dbe..4048267a 100644 --- a/src/organisation/organisation.controller.ts +++ b/src/organisation/organisation.controller.ts @@ -61,16 +61,7 @@ export class OrganisationController { @Param("uuid") uuid: string, @Request() request: RequestWithUser ): Promise { - const deleteResult = await this.organisationService.remove( - uuid, - request.user.id - ); - return { - result: - deleteResult !== undefined && - deleteResult.affected !== undefined && - deleteResult?.affected !== null && - deleteResult?.affected > 0, - }; + await this.organisationService.remove(uuid, request.user.id); + return {result: true}; } } diff --git a/src/organisation/organisation.service.ts b/src/organisation/organisation.service.ts index 989b8035..7ce8a97b 100644 --- a/src/organisation/organisation.service.ts +++ b/src/organisation/organisation.service.ts @@ -1,6 +1,6 @@ import {Injectable, NotFoundException} from "@nestjs/common"; import {InjectRepository} from "@nestjs/typeorm"; -import {DeleteResult, Repository, UpdateResult} from "typeorm"; +import {Repository, UpdateResult} from "typeorm"; import {OrganisationMembership} from "../organisation-memberships/entities/organisation-membership.entity.js"; import {CreateOrganisationDto} from "./dto/create-organisation.dto.js"; import {Roles} from "./dto/RolesEnum.js"; @@ -105,8 +105,8 @@ export class OrganisationService { return this.repository.update(ownerOfOrg.id, ownerOfOrg); } - async remove(uuid: string, currentUserId: number): Promise { - const ownerOfOrg = await this.repository.findOneOrFail({ + async remove(uuid: string, currentUserId: number): Promise { + const ownedOrg = await this.repository.findOneOrFail({ where: { uuid, memberships: { @@ -120,8 +120,6 @@ export class OrganisationService { }, }); - return this.repository.delete({ - id: ownerOfOrg.id, - }); + await this.repository.softRemove(ownedOrg); } } diff --git a/src/payment-sessions/payment-session.entity.ts b/src/payment-sessions/payment-session.entity.ts index 44dda539..da958e44 100644 --- a/src/payment-sessions/payment-session.entity.ts +++ b/src/payment-sessions/payment-session.entity.ts @@ -27,11 +27,14 @@ export class PaymentSessionReference { @ApiProperty() createdDate!: Date; + // note that this is not a database relation @Column({nullable: true}) organisationUuid?: string; // This is nullable because it we have a sample "not authenticated" controller + // in this set of modules. // but you most likely want to make this non-nullable + // also note this is not a relation, it's just a string @Column({nullable: true}) userUuid?: string; } diff --git a/src/user-api-key/user-apikey.service.ts b/src/user-api-key/user-apikey.service.ts index 83a8b1d2..de883101 100644 --- a/src/user-api-key/user-apikey.service.ts +++ b/src/user-api-key/user-apikey.service.ts @@ -78,8 +78,8 @@ export class UserApiKeyService { if (!userApiKey) { throw new NotFoundException(); } - - const result = await this.repository.remove(userApiKey); - return result.deletedDate !== undefined; + // note, no soft delete here. Once a key is gone it should be gone + await this.repository.remove(userApiKey); + return true; } } diff --git a/src/user-api-key/userApiKey.entity.ts b/src/user-api-key/userApiKey.entity.ts index 5a9c02a3..13ac6b51 100644 --- a/src/user-api-key/userApiKey.entity.ts +++ b/src/user-api-key/userApiKey.entity.ts @@ -1,8 +1,7 @@ -import {ApiProperty, ApiPropertyOptional} from "@nestjs/swagger"; +import {ApiProperty} from "@nestjs/swagger"; import { Column, CreateDateColumn, - DeleteDateColumn, Entity, Generated, Index, @@ -49,8 +48,4 @@ export class UserApiKey { @UpdateDateColumn() @ApiProperty() updateDate!: Date; - - @DeleteDateColumn() - @ApiPropertyOptional() - deletedDate?: Date; } diff --git a/src/user/dto/userResponseDto.ts b/src/user/dto/userResponseDto.ts index 4b413aaa..5b0c8afc 100644 --- a/src/user/dto/userResponseDto.ts +++ b/src/user/dto/userResponseDto.ts @@ -44,6 +44,9 @@ export class UserDto { @ApiProperty({type: () => OrganisationMembership, isArray: true}) memberships!: OrganisationMembership[]; + @ApiProperty({type: String, isArray: true}) + activeSubscriptionProductKeys!: string[]; + @ApiProperty() createdDate!: Date; diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 0778b493..ee6f4e4c 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -59,8 +59,15 @@ export class UserController { uuid, request.user ); + const activePaidForProducts = new Set( + result.memberships + ?.flatMap((m) => m.organisation.subscriptionRecords || []) + ?.filter((s) => s && s.validUntil > new Date()) + ?.map((s) => s?.internalSku) || [] + ); return { ...result, + activeSubscriptionProductKeys: [...activePaidForProducts], memberships: request.user.memberships ?? [], }; } @@ -78,6 +85,9 @@ export class UserController { @Patch(":uuid") @ApiOkResponse({type: BooleanResult}) + @ApiOperation({ + description: "Limited to Super Admin role or the user themselves.", + }) async update( @Param("uuid") uuid: string, @Body() updateUserDto: UpdateUserDto, @@ -92,6 +102,9 @@ export class UserController { } @Delete(":uuid") + @ApiOperation({ + description: "Limited to Super Admin role or the user themselves.", + }) @ApiOkResponse({type: BooleanResult}) async remove( @Param("uuid") uuid: string, diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 250273f1..d511a150 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -130,12 +130,12 @@ export class UserService { ) ) { throw new Error( - "Can't remove the owner of an organisation. Un-assign a new owner first." + "Can't remove the owner of an organisation. Assign a new owner first." ); } } - return this.repository.remove(user); + return this.repository.softRemove(user); } private isCurrentUserGuard(