From 7a7ba146fc00a274517d7e482687d8f4cd658fd3 Mon Sep 17 00:00:00 2001 From: andrea rota Date: Mon, 9 Oct 2023 13:27:52 +0100 Subject: [PATCH] group project Marxan summaries-related endpoints within their own controller and OpenAPI category --- .../modules/projects/projects.controller.ts | 49 +------------ .../src/modules/projects/projects.module.ts | 2 + .../projects/projects.summaries.controller.ts | 71 +++++++++++++++++++ 3 files changed, 76 insertions(+), 46 deletions(-) create mode 100644 api/apps/api/src/modules/projects/projects.summaries.controller.ts diff --git a/api/apps/api/src/modules/projects/projects.controller.ts b/api/apps/api/src/modules/projects/projects.controller.ts index 78ffb1e139..96ef0c6dd3 100644 --- a/api/apps/api/src/modules/projects/projects.controller.ts +++ b/api/apps/api/src/modules/projects/projects.controller.ts @@ -3,22 +3,16 @@ import { Controller, Delete, ForbiddenException, - Get, - Header, - InternalServerErrorException, Param, ParseUUIDPipe, + Get, InternalServerErrorException, Param, ParseUUIDPipe, Patch, Post, - Req, - Res, UseGuards + Req, UseGuards } from '@nestjs/common'; import { projectResource, ProjectResultSingular } from './project.api.entity'; import { ApiBearerAuth, ApiOkResponse, - ApiOperation, - ApiParam, - ApiProduces, - ApiTags + ApiOperation, ApiTags } from '@nestjs/swagger'; import { apiGlobalPrefixes } from '@marxan-api/api.config'; import { JwtAuthGuard } from '@marxan-api/guards/jwt-auth.guard'; @@ -34,7 +28,6 @@ import { ProjectJobsStatusDto } from './dto/project-jobs-status.dto'; import { JobStatusSerializer } from './dto/job-status.serializer'; import { isLeft } from 'fp-ts/Either'; import { asyncJobTag } from '@marxan-api/dto/async-job-tag'; -import { Response } from 'express'; import { ImplementsAcl, IsMissingAclImplementation, @@ -42,7 +35,6 @@ import { import { ScenarioLockResultPlural } from '@marxan-api/modules/access-control/scenarios-acl/locks/dto/scenario.lock.dto'; import { mapAclDomainToHttpError } from '@marxan-api/utils/acl.utils'; import { deleteProjectFailed } from './delete-project/delete-project.command'; -import { outputProjectSummaryResource } from './output-project-summaries/output-project-summary.api.entity'; @UseGuards(JwtAuthGuard) @ApiBearerAuth() @@ -144,41 +136,6 @@ export class ProjectsController { return this.jobsStatusSerializer.serialize(projectId, projectWithScenarios); } - @ImplementsAcl() - @ApiOperation({ - description: - "Returns a zip file containing CSVs with summary information for the execution of all the Project's Scenarios", - }) - @ApiParam({ name: 'projectId', description: 'Id of the Project' }) - @ApiProduces('application/zip') - @Header('Content-Type', 'application/zip') - @Get(':projectId/output-summary') - async getOutputSummary( - @Param('projectId') projectId: string, - @Req() req: RequestWithAuthenticatedUser, - @Res() response: Response, - ) { - const result = await this.projectsService.getOutputSummary( - req.user.id, - projectId, - ); - - if (isLeft(result)) { - throw mapAclDomainToHttpError(result.left, { - resourceType: outputProjectSummaryResource.name.singular, - projectId, - userId: req.user.id, - }); - } - - response.set( - 'Content-Disposition', - `attachment; filename="output-summary-${projectId}"`, - ); - - response.send(result.right); // @debt should refactored to use StreameableFile or at least use streams, but doesn't seem to work right away - } - @ImplementsAcl() @Get(':projectId/editing-locks') @ApiOperation({ summary: 'Get all locks by project' }) diff --git a/api/apps/api/src/modules/projects/projects.module.ts b/api/apps/api/src/modules/projects/projects.module.ts index 3e3538018a..55c2618538 100644 --- a/api/apps/api/src/modules/projects/projects.module.ts +++ b/api/apps/api/src/modules/projects/projects.module.ts @@ -60,6 +60,7 @@ import { ProjectFeaturesController } from './projects.project-features.controlle import { ProjectCloningController } from './projects.cloning.controller'; import { ProjectPlanningAreaAndGridController } from './projects.planning-area-and-grid.controller'; import { ProjectBLMController } from './projects.blm.controller'; +import { ProjectSummariesController } from './projects.summaries.controller'; @Module({ imports: [ @@ -134,6 +135,7 @@ import { ProjectBLMController } from './projects.blm.controller'; ProjectPlanningAreaAndGridController, ProjectProtectedAreasController, ProjectScenarioComparisonController, + ProjectSummariesController, ], // @ToDo Remove TypeOrmModule after project publish will stop use the ProjectRepository exports: [ProjectsCrudService, TypeOrmModule, ProjectsService], diff --git a/api/apps/api/src/modules/projects/projects.summaries.controller.ts b/api/apps/api/src/modules/projects/projects.summaries.controller.ts new file mode 100644 index 0000000000..a3071f2947 --- /dev/null +++ b/api/apps/api/src/modules/projects/projects.summaries.controller.ts @@ -0,0 +1,71 @@ +import { + Controller, Get, + Header, Param, Req, + Res, UseGuards +} from '@nestjs/common'; + +import { + ApiBearerAuth, ApiOperation, + ApiParam, + ApiProduces, + ApiTags +} from '@nestjs/swagger'; +import { apiGlobalPrefixes } from '@marxan-api/api.config'; +import { JwtAuthGuard } from '@marxan-api/guards/jwt-auth.guard'; + +import { RequestWithAuthenticatedUser } from '@marxan-api/app.controller'; +import { + ProjectsService +} from './projects.service'; +import { isLeft } from 'fp-ts/Either'; +import { Response } from 'express'; +import { + ImplementsAcl +} from '@marxan-api/decorators/acl.decorator'; +import { mapAclDomainToHttpError } from '@marxan-api/utils/acl.utils'; +import { outputProjectSummaryResource } from './output-project-summaries/output-project-summary.api.entity'; + +@UseGuards(JwtAuthGuard) +@ApiBearerAuth() +@ApiTags('Project - Marxan summaries') +@Controller(`${apiGlobalPrefixes.v1}/projects`) +export class ProjectSummariesController { + constructor( + private readonly projectsService: ProjectsService, + ) {} + + @ImplementsAcl() + @ApiOperation({ + description: + "Returns a zip file containing CSVs with summary information for the execution of all the Project's Scenarios", + }) + @ApiParam({ name: 'projectId', description: 'Id of the Project' }) + @ApiProduces('application/zip') + @Header('Content-Type', 'application/zip') + @Get(':projectId/output-summary') + async getOutputSummary( + @Param('projectId') projectId: string, + @Req() req: RequestWithAuthenticatedUser, + @Res() response: Response, + ) { + const result = await this.projectsService.getOutputSummary( + req.user.id, + projectId, + ); + + if (isLeft(result)) { + throw mapAclDomainToHttpError(result.left, { + resourceType: outputProjectSummaryResource.name.singular, + projectId, + userId: req.user.id, + }); + } + + response.set( + 'Content-Disposition', + `attachment; filename="output-summary-${projectId}"`, + ); + + response.send(result.right); // @debt should refactored to use StreameableFile or at least use streams, but doesn't seem to work right away + } +}