Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ workflows:
only:
- develop
- feat/ai-workflows
- pm-1792
- pm-1955


- 'build-prod':
Expand Down
39 changes: 39 additions & 0 deletions src/api/ai-workflow/ai-workflow.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
UpdateAiWorkflowDto,
CreateAiWorkflowRunItemsDto,
UpdateAiWorkflowRunDto,
CreateRunItemCommentDto,
UpdateAiWorkflowRunItemDto,
} from '../../dto/aiWorkflow.dto';
import { Scopes } from 'src/shared/decorators/scopes.decorator';
Expand All @@ -39,6 +40,44 @@ import { User } from 'src/shared/decorators/user.decorator';
export class AiWorkflowController {
constructor(private readonly aiWorkflowService: AiWorkflowService) {}

@Post('/:workflowId/runs/:runId/items/:itemId/comments')
@Roles(
UserRole.Submitter,
UserRole.Copilot,
UserRole.ProjectManager,
UserRole.Admin,
UserRole.Reviewer,
UserRole.User,
)
@ApiOperation({ summary: 'Create a comment for a specific run item' })
@ApiResponse({
status: 201,
description: 'Comment created successfully.',
})
@ApiResponse({ status: 400, description: 'Bad Request.' })
@ApiResponse({ status: 401, description: 'Unauthorized.' })
@ApiResponse({ status: 403, description: 'Forbidden.' })
@ApiResponse({
status: 404,
description: 'Workflow, Run, or Item not found.',
})
async createRunItemComment(
@Param('workflowId') workflowId: string,
@Param('runId') runId: string,
@Param('itemId') itemId: string,
@Body(new ValidationPipe({ whitelist: true, transform: true }))
body: CreateRunItemCommentDto,
@User() user: JwtUser,
) {
return this.aiWorkflowService.createRunItemComment(
workflowId,
runId,
itemId,
body,
user,
);
}

@Post()
@Roles(UserRole.Admin)
@Scopes(Scope.CreateWorkflow)
Expand Down
45 changes: 45 additions & 0 deletions src/api/ai-workflow/ai-workflow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { PrismaService } from '../../shared/modules/global/prisma.service';
import {
CreateAiWorkflowDto,
CreateAiWorkflowRunDto,
CreateRunItemCommentDto,
UpdateAiWorkflowDto,
UpdateAiWorkflowRunDto,
UpdateAiWorkflowRunItemDto,
Expand Down Expand Up @@ -37,6 +38,50 @@ export class AiWorkflowService {
this.logger = LoggerService.forRoot('AiWorkflowService');
}

async createRunItemComment(
workflowId: string,
runId: string,
itemId: string,
body: CreateRunItemCommentDto,
user: JwtUser,
) {
const workflow = await this.prisma.aiWorkflow.findUnique({
where: { id: workflowId },
});
if (!workflow) {
throw new NotFoundException(`Workflow with id ${workflowId} not found.`);
}

const run = await this.prisma.aiWorkflowRun.findUnique({
where: { id: runId },
});
if (!run || run.workflowId !== workflowId) {
throw new NotFoundException(
`Run with id ${runId} not found or does not belong to workflow ${workflowId}.`,
);
}

const item = await this.prisma.aiWorkflowRunItem.findUnique({
where: { id: itemId },
});
if (!item || item.workflowRunId !== runId) {
throw new NotFoundException(
`Item with id ${itemId} not found or does not belong to run ${runId}.`,
);
}

const createdComment = await this.prisma.aiWorkflowRunItemComment.create({
data: {
workflowRunItemId: itemId,
content: body.content,
parentId: body.parentId ?? null,
userId: user.userId!,
},
});

return createdComment;
}

async scorecardExists(scorecardId: string): Promise<boolean> {
const count = await this.prisma.scorecard.count({
where: { id: scorecardId, status: ScorecardStatus.ACTIVE },
Expand Down
20 changes: 19 additions & 1 deletion src/dto/aiWorkflow.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger';
import {
ApiHideProperty,
ApiProperty,
OmitType,
PartialType,
} from '@nestjs/swagger';
import {
IsString,
IsNotEmpty,
Expand Down Expand Up @@ -162,6 +167,19 @@ export class CommentDto {
export class UpdateAiWorkflowRunItemDto extends PartialType(
CreateAiWorkflowRunItemDto,
) {
@ApiHideProperty()
@IsEmpty({ message: 'scorecardQuestionId cannot be updated' })
scorecardQuestionId?: never;
}

export class CreateRunItemCommentDto {
@ApiProperty()
@IsString()
@IsNotEmpty()
content: string;

@ApiProperty({ required: false })
@IsString()
@IsOptional()
parentId?: string;
}