diff --git a/apps/server-asset-sg/src/app.module.ts b/apps/server-asset-sg/src/app.module.ts index 2512ceb7..11aa94fb 100644 --- a/apps/server-asset-sg/src/app.module.ts +++ b/apps/server-asset-sg/src/app.module.ts @@ -11,6 +11,7 @@ import { JwtMiddleware } from '@/core/middleware/jwt.middleware'; import { PrismaService } from '@/core/prisma.service'; import { AssetEditController } from '@/features/asset-edit/asset-edit.controller'; import { AssetEditRepo } from '@/features/asset-edit/asset-edit.repo'; +import { AssetEditService } from '@/features/asset-edit/asset-edit.service'; import { AssetInfoRepo } from '@/features/assets/asset-info.repo'; import { AssetRepo } from '@/features/assets/asset.repo'; import { AssetsController } from '@/features/assets/assets.controller'; @@ -51,6 +52,7 @@ import { WorkgroupsController } from '@/features/workgroups/workgroups.controlle provideElasticsearch, AssetEditRepo, AssetInfoRepo, + AssetEditService, AssetRepo, AssetSearchService, AssetSyncService, diff --git a/apps/server-asset-sg/src/features/asset-edit/asset-edit.controller.ts b/apps/server-asset-sg/src/features/asset-edit/asset-edit.controller.ts index f505b30d..b283a68c 100644 --- a/apps/server-asset-sg/src/features/asset-edit/asset-edit.controller.ts +++ b/apps/server-asset-sg/src/features/asset-edit/asset-edit.controller.ts @@ -17,11 +17,16 @@ import { authorize } from '@/core/authorize'; import { CurrentUser } from '@/core/decorators/current-user.decorator'; import { ParseBody } from '@/core/decorators/parse.decorator'; import { AssetEditRepo } from '@/features/asset-edit/asset-edit.repo'; +import { AssetEditService } from '@/features/asset-edit/asset-edit.service'; import { AssetSearchService } from '@/features/assets/search/asset-search.service'; @Controller('/asset-edit') export class AssetEditController { - constructor(private readonly assetEditRepo: AssetEditRepo, private readonly assetSearchService: AssetSearchService) {} + constructor( + private readonly assetEditRepo: AssetEditRepo, + private readonly assetEditService: AssetEditService, + private readonly assetSearchService: AssetSearchService + ) {} @Get('/:id') async show(@Param('id', ParseIntPipe) id: number, @CurrentUser() user: User): Promise { @@ -38,6 +43,8 @@ export class AssetEditController { authorize(AssetEditPolicy, user).canCreate(); validatePatch(user, patch); + await this.assetEditService.validateReferencesOrThrow({ user, patch }); + const asset = await this.assetEditRepo.create({ user, patch }); await this.assetSearchService.register(asset); return AssetEditDetail.encode(asset); @@ -56,6 +63,7 @@ export class AssetEditController { authorize(AssetEditPolicy, user).canUpdate(record); validatePatch(user, patch, record); + await this.assetEditService.validateReferencesOrThrow({ user, patch }, id); const asset = await this.assetEditRepo.update(record.assetId, { user, patch }); if (asset === null) { diff --git a/apps/server-asset-sg/src/features/asset-edit/asset-edit.repo.ts b/apps/server-asset-sg/src/features/asset-edit/asset-edit.repo.ts index 9397621d..bf14a32d 100644 --- a/apps/server-asset-sg/src/features/asset-edit/asset-edit.repo.ts +++ b/apps/server-asset-sg/src/features/asset-edit/asset-edit.repo.ts @@ -49,7 +49,6 @@ export class AssetEditRepo implements Repo { - await this.validateReferencesOrThrow(data); const asset = await this.prismaService.asset.create({ select: { assetId: true }, data: { @@ -112,9 +111,6 @@ export class AssetEditRepo implements Repo { @@ -320,45 +316,6 @@ export class AssetEditRepo implements Repo { - // check if any of the siblings are in another workgroup - for (const assetYId of data.patch.siblingAssetIds) { - const siblingCandidate = await this.prismaService.asset.findUnique({ - where: { assetId: assetYId }, - select: { workgroupId: true }, - }); - if (siblingCandidate?.workgroupId !== data.patch.workgroupId) { - throw new Error('Sibling assets must be in the same workgroup as the edited asset'); - } - } - - // check if the parent asset is in another workgroup - const assetMainId = O.toUndefined(data.patch.assetMainId); - if (assetMainId) { - const assetMain = await this.prismaService.asset.findUnique({ - where: { assetId: assetMainId }, - select: { workgroupId: true }, - }); - if (assetMain?.workgroupId !== data.patch.workgroupId) { - throw new Error('Cannot assign parent asset from different workgroup'); - } - } - - // check if any of the subordinate assets are in another workgroup for exisiting assets - if (id) { - const childAssets = await this.prismaService.asset.findMany({ - where: { assetMainId: id }, - select: { workgroupId: true }, - }); - - for (const child of childAssets) { - if (child.workgroupId !== data.patch.workgroupId) { - throw new Error('Child assets must be in the same workgroup as the parent asset'); - } - } - } - } } /** diff --git a/apps/server-asset-sg/src/features/asset-edit/asset-edit.service.ts b/apps/server-asset-sg/src/features/asset-edit/asset-edit.service.ts index 82a943b9..911d35b7 100644 --- a/apps/server-asset-sg/src/features/asset-edit/asset-edit.service.ts +++ b/apps/server-asset-sg/src/features/asset-edit/asset-edit.service.ts @@ -1,53 +1,55 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import * as O from 'fp-ts/Option'; -import { isNotNull, unknownToUnknownError } from '@asset-sg/core'; -import { BaseAssetEditDetail, PatchAsset } from '@asset-sg/shared'; -import { User } from '@asset-sg/shared/v2'; -import { Injectable } from '@nestjs/common'; -import { pipe } from 'fp-ts/function'; -import * as TE from 'fp-ts/TaskEither'; -import * as C from 'io-ts/Codec'; - -import { AssetEditRepo } from './asset-edit.repo'; -import { AssetSearchService } from '@/features/assets/search/asset-search.service'; -import { notFoundError } from '@/utils/errors'; - -export const AssetEditDetail = C.struct({ - ...BaseAssetEditDetail, - studies: C.array(C.struct({ assetId: C.number, studyId: C.string, geomText: C.string })), -}); -export type AssetEditDetail = C.TypeOf; +import { AssetEditData } from './asset-edit.repo'; +import { PrismaService } from '@/core/prisma.service'; @Injectable() export class AssetEditService { - constructor(private readonly assetEditRepo: AssetEditRepo, private readonly assetSearchService: AssetSearchService) {} + constructor(private readonly prismaService: PrismaService) {} - public createAsset(user: User, patch: PatchAsset) { - return pipe( - TE.tryCatch( - () => this.assetEditRepo.create({ user, patch }), - (e) => e as Error - ), - TE.chain(({ assetId }) => - TE.tryCatch( - () => this.assetEditRepo.find(assetId), - (e) => e as Error - ) - ), - TE.chainW(TE.fromPredicate(isNotNull, notFoundError)), - TE.tap((asset) => TE.tryCatch(() => this.assetSearchService.register(asset), unknownToUnknownError)), - TE.map((asset) => AssetEditDetail.encode(asset)) - ); - } + public async validateReferencesOrThrow(data: AssetEditData, id?: number): Promise { + // check if any of the siblings are in another workgroup + for (const assetYId of data.patch.siblingAssetIds) { + const siblingCandidate = await this.prismaService.asset.findUnique({ + where: { assetId: assetYId }, + select: { workgroupId: true }, + }); + if (siblingCandidate?.workgroupId !== data.patch.workgroupId) { + throw new HttpException( + 'Sibling assets must be in the same workgroup as the edited asset', + HttpStatus.UNPROCESSABLE_ENTITY + ); + } + } + + // check if the parent asset is in another workgroup + const assetMainId = O.toUndefined(data.patch.assetMainId); + if (assetMainId) { + const assetMain = await this.prismaService.asset.findUnique({ + where: { assetId: assetMainId }, + select: { workgroupId: true }, + }); + if (assetMain?.workgroupId !== data.patch.workgroupId) { + throw new HttpException('Cannot assign parent asset from different workgroup', HttpStatus.UNPROCESSABLE_ENTITY); + } + } + + // check if any of the subordinate assets are in another workgroup for exisiting assets + if (id) { + const childAssets = await this.prismaService.asset.findMany({ + where: { assetMainId: id }, + select: { workgroupId: true }, + }); - public updateAsset(user: User, assetId: number, patch: PatchAsset) { - return pipe( - TE.tryCatch( - () => this.assetEditRepo.update(assetId, { user, patch }), - (e) => e as Error - ), - TE.chainW(TE.fromPredicate(isNotNull, notFoundError)), - TE.tap((asset) => TE.tryCatch(() => this.assetSearchService.register(asset), unknownToUnknownError)), - TE.map((asset) => AssetEditDetail.encode(asset)) - ); + for (const child of childAssets) { + if (child.workgroupId !== data.patch.workgroupId) { + throw new HttpException( + 'Child assets must be in the same workgroup as the parent asset', + HttpStatus.UNPROCESSABLE_ENTITY + ); + } + } + } } }