Skip to content

Commit aaff6e6

Browse files
committed
Add some guards around updating a review once the challenge is complete, and refactor the review-type controller into a service class for business logic (PM-1968)
1 parent 2cc67dc commit aaff6e6

File tree

6 files changed

+397
-168
lines changed

6 files changed

+397
-168
lines changed

src/api/api.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ReviewController } from './review/review.controller';
1111
import { ProjectResultModule } from './project-result/projectResult.module';
1212

1313
import { ReviewTypeController } from './review-type/review-type.controller';
14+
import { ReviewTypeService } from './review-type/review-type.service';
1415
import { SubmissionController } from './submission/submission.controller';
1516
import { ReviewSummationController } from './review-summation/review-summation.controller';
1617
import { ReviewOpportunityController } from './review-opportunity/reviewOpportunity.controller';
@@ -71,6 +72,7 @@ import { ProjectResultController } from './project-result/projectResult.controll
7172
ReviewSummationService,
7273
AiWorkflowService,
7374
ProjectResultService,
75+
ReviewTypeService,
7476
],
7577
})
7678
export class ApiModule {}

src/api/review-type/review-type.controller.ts

Lines changed: 11 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import {
88
Body,
99
Param,
1010
Query,
11-
NotFoundException,
12-
InternalServerErrorException,
1311
HttpCode,
1412
HttpStatus,
1513
} from '@nestjs/common';
@@ -32,24 +30,15 @@ import {
3230
ReviewTypeRequestDto,
3331
ReviewTypeUpdateRequestDto,
3432
} from 'src/dto/reviewType.dto';
35-
import { PrismaService } from '../../shared/modules/global/prisma.service';
36-
import { LoggerService } from '../../shared/modules/global/logger.service';
3733
import { PaginationDto } from '../../dto/pagination.dto';
3834
import { SortDto } from '../../dto/sort.dto';
39-
import { PrismaErrorService } from '../../shared/modules/global/prisma-error.service';
35+
import { ReviewTypeService } from './review-type.service';
4036

4137
@ApiTags('ReviewTypes')
4238
@ApiBearerAuth()
4339
@Controller('/reviewTypes')
4440
export class ReviewTypeController {
45-
private readonly logger: LoggerService;
46-
47-
constructor(
48-
private readonly prisma: PrismaService,
49-
private readonly prismaErrorService: PrismaErrorService,
50-
) {
51-
this.logger = LoggerService.forRoot('ReviewTypeController');
52-
}
41+
constructor(private readonly reviewTypeService: ReviewTypeService) {}
5342

5443
@Post()
5544
@Roles(UserRole.Admin, UserRole.Copilot)
@@ -67,25 +56,7 @@ export class ReviewTypeController {
6756
async createReviewType(
6857
@Body() body: ReviewTypeRequestDto,
6958
): Promise<ReviewTypeResponseDto> {
70-
this.logger.log(
71-
`Creating review type with request boy: ${JSON.stringify(body)}`,
72-
);
73-
try {
74-
const data = await this.prisma.reviewType.create({
75-
data: body,
76-
});
77-
this.logger.log(`Review type created with ID: ${data.id}`);
78-
return data as ReviewTypeResponseDto;
79-
} catch (error) {
80-
const errorResponse = this.prismaErrorService.handleError(
81-
error,
82-
'creating review type',
83-
);
84-
throw new InternalServerErrorException({
85-
message: errorResponse.message,
86-
code: errorResponse.code,
87-
});
88-
}
59+
return this.reviewTypeService.createReviewType(body);
8960
}
9061

9162
@Patch('/:reviewTypeId')
@@ -113,7 +84,7 @@ export class ReviewTypeController {
11384
@Param('reviewTypeId') reviewTypeId: string,
11485
@Body() body: ReviewTypeUpdateRequestDto,
11586
): Promise<ReviewTypeResponseDto> {
116-
return this._updateReviewType(reviewTypeId, body);
87+
return this.reviewTypeService.updateReviewType(reviewTypeId, body);
11788
}
11889

11990
@Put('/:reviewTypeId')
@@ -138,31 +109,7 @@ export class ReviewTypeController {
138109
@Param('reviewTypeId') reviewTypeId: string,
139110
@Body() body: ReviewTypeRequestDto,
140111
): Promise<ReviewTypeResponseDto> {
141-
return this._updateReviewType(reviewTypeId, body);
142-
}
143-
144-
/**
145-
* The inner update method for entity
146-
*/
147-
async _updateReviewType(
148-
reviewTypeId: string,
149-
body: ReviewTypeUpdateRequestDto,
150-
): Promise<ReviewTypeResponseDto> {
151-
this.logger.log(`Updating review type with ID: ${reviewTypeId}`);
152-
try {
153-
const data = await this.prisma.reviewType.update({
154-
where: { id: reviewTypeId },
155-
data: body,
156-
});
157-
this.logger.log(`Review type updated successfully: ${reviewTypeId}`);
158-
return data as ReviewTypeResponseDto;
159-
} catch (error) {
160-
throw this._rethrowError(
161-
error,
162-
reviewTypeId,
163-
`updating review type ${reviewTypeId}`,
164-
);
165-
}
112+
return this.reviewTypeService.updateReviewType(reviewTypeId, body);
166113
}
167114

168115
@Get()
@@ -183,63 +130,11 @@ export class ReviewTypeController {
183130
@Query() paginationDto?: PaginationDto,
184131
@Query() sortDto?: SortDto,
185132
): Promise<ReviewTypeResponseDto[]> {
186-
this.logger.log(
187-
`Getting review types with filters - ${JSON.stringify(queryDto)}`,
133+
return this.reviewTypeService.listReviewTypes(
134+
queryDto,
135+
paginationDto,
136+
sortDto,
188137
);
189-
190-
const { page = 1, perPage = 10 } = paginationDto || {};
191-
const skip = (page - 1) * perPage;
192-
let orderBy;
193-
194-
if (sortDto && sortDto.orderBy && sortDto.sortBy) {
195-
orderBy = {
196-
[sortDto.sortBy]: sortDto.orderBy.toLowerCase(),
197-
};
198-
}
199-
200-
try {
201-
// Build the where clause for review types based on available filter parameters
202-
const reviewTypeWhereClause: any = {};
203-
if (queryDto.name) {
204-
reviewTypeWhereClause.name = queryDto.name;
205-
}
206-
if (queryDto.isActive !== undefined) {
207-
reviewTypeWhereClause.isActive =
208-
queryDto.isActive.toLowerCase() === 'true';
209-
}
210-
211-
// find entities by filters
212-
const reviewTypes = await this.prisma.reviewType.findMany({
213-
where: {
214-
...reviewTypeWhereClause,
215-
},
216-
skip,
217-
take: perPage,
218-
orderBy,
219-
});
220-
221-
// Count total entities matching the filter for pagination metadata
222-
const totalCount = await this.prisma.reviewType.count({
223-
where: {
224-
...reviewTypeWhereClause,
225-
},
226-
});
227-
228-
this.logger.log(
229-
`Found ${reviewTypes.length} review types (page ${page} of ${Math.ceil(totalCount / perPage)})`,
230-
);
231-
232-
return reviewTypes as ReviewTypeResponseDto[];
233-
} catch (error) {
234-
const errorResponse = this.prismaErrorService.handleError(
235-
error,
236-
'fetching review types',
237-
);
238-
throw new InternalServerErrorException({
239-
message: errorResponse.message,
240-
code: errorResponse.code,
241-
});
242-
}
243138
}
244139

245140
@Get('/:reviewTypeId')
@@ -262,21 +157,7 @@ export class ReviewTypeController {
262157
async getReviewType(
263158
@Param('reviewTypeId') reviewTypeId: string,
264159
): Promise<ReviewTypeResponseDto> {
265-
this.logger.log(`Getting review type with ID: ${reviewTypeId}`);
266-
try {
267-
const data = await this.prisma.reviewType.findUniqueOrThrow({
268-
where: { id: reviewTypeId },
269-
});
270-
271-
this.logger.log(`Review type found: ${reviewTypeId}`);
272-
return data as ReviewTypeResponseDto;
273-
} catch (error) {
274-
throw this._rethrowError(
275-
error,
276-
reviewTypeId,
277-
`fetching review type ${reviewTypeId}`,
278-
);
279-
}
160+
return this.reviewTypeService.getReviewType(reviewTypeId);
280161
}
281162

282163
@Delete('/:reviewTypeId')
@@ -298,38 +179,6 @@ export class ReviewTypeController {
298179
@ApiResponse({ status: 403, description: 'Forbidden.' })
299180
@ApiResponse({ status: 404, description: 'Review not found.' })
300181
async deleteReviewType(@Param('reviewTypeId') reviewTypeId: string) {
301-
this.logger.log(`Deleting review type with ID: ${reviewTypeId}`);
302-
try {
303-
await this.prisma.reviewType.delete({
304-
where: { id: reviewTypeId },
305-
});
306-
this.logger.log(`Review type deleted successfully: ${reviewTypeId}`);
307-
return { message: `Review type ${reviewTypeId} deleted successfully.` };
308-
} catch (error) {
309-
throw this._rethrowError(
310-
error,
311-
reviewTypeId,
312-
`deleting review type ${reviewTypeId}`,
313-
);
314-
}
315-
}
316-
317-
/**
318-
* Build exception by error code
319-
*/
320-
_rethrowError(error: any, reviewTypeId: string, message: string) {
321-
const errorResponse = this.prismaErrorService.handleError(error, message);
322-
323-
if (errorResponse.code === 'RECORD_NOT_FOUND') {
324-
return new NotFoundException({
325-
message: `Review type with ID ${reviewTypeId} was not found`,
326-
code: errorResponse.code,
327-
});
328-
}
329-
330-
return new InternalServerErrorException({
331-
message: errorResponse.message,
332-
code: errorResponse.code,
333-
});
182+
return this.reviewTypeService.deleteReviewType(reviewTypeId);
334183
}
335184
}

0 commit comments

Comments
 (0)