Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dc80999
feat: update workflow run item
hentrymartin Sep 15, 2025
d224404
fix: lint
hentrymartin Sep 15, 2025
56e99a3
fix: lint
hentrymartin Sep 15, 2025
a207c7e
updated from develop
hentrymartin Sep 16, 2025
8251d86
fix: removed createdAt and createdBy from comment DTO
hentrymartin Sep 16, 2025
237d5d6
fix: removed createdAt and createdBy from comment DTO
hentrymartin Sep 16, 2025
997a68a
fix: removed createdAt and createdBy from comment DTO
hentrymartin Sep 16, 2025
abf0b9a
fix: removed createdAt and createdBy from comment DTO
hentrymartin Sep 16, 2025
ab1a860
fix: removed createdAt and createdBy from comment DTO
hentrymartin Sep 16, 2025
0a02608
fix: removed createdAt and createdBy from comment DTO
hentrymartin Sep 16, 2025
88a8e1d
fix: removed createdAt and createdBy from comment DTO
hentrymartin Sep 16, 2025
a2a73bd
Merge branch 'develop' into pm-1792
hentrymartin Sep 17, 2025
7334871
updated from develo[
hentrymartin Sep 17, 2025
f9b79f4
updated from develop
hentrymartin Sep 17, 2025
08c1a0e
fix: removed toString
hentrymartin Sep 17, 2025
afe7b6f
Merge branch 'develop' into pm-1792
hentrymartin Sep 17, 2025
65837e4
fix: lint
hentrymartin Sep 17, 2025
72af621
fix: lint
hentrymartin Sep 17, 2025
01e2bbc
fix: allowonly upvotes and downvotes to be updated
hentrymartin Sep 17, 2025
3bdd63c
fix: allowonly upvotes and downvotes to be updated
hentrymartin Sep 17, 2025
7efc25f
fix: allowonly upvotes and downvotes to be updated
hentrymartin Sep 17, 2025
cdf35b1
Merge branch 'develop' into pm-1792
hentrymartin Sep 18, 2025
749617f
fix: allow m2m tokens to update certain fields
hentrymartin Sep 18, 2025
25369e4
fix: allow m2m tokens to update certain fields
hentrymartin Sep 18, 2025
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
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ workflows:
only:
- develop
- feat/ai-workflows
- pm-1782
- pm-1792


- 'build-prod':
context: org-global
Expand Down
45 changes: 45 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,
UpdateAiWorkflowRunItemDto,
} from '../../dto/aiWorkflow.dto';
import { Scopes } from 'src/shared/decorators/scopes.decorator';
import { UserRole } from 'src/shared/enums/userRole.enum';
Expand Down Expand Up @@ -227,6 +228,50 @@ export class AiWorkflowController {
);
}

@Patch('/:workflowId/runs/:runId/items/:itemId')
@Scopes(Scope.UpdateWorkflowRun)
@Roles(
UserRole.Admin,
UserRole.Copilot,
UserRole.ProjectManager,
UserRole.Reviewer,
UserRole.Submitter,
UserRole.User,
)
@ApiOperation({ summary: 'Update an AIWorkflowRunItem by id' })
@ApiParam({ name: 'workflowId', description: 'The ID of the AI workflow' })
@ApiParam({ name: 'runId', description: 'The ID of the AI workflow run' })
@ApiParam({
name: 'itemId',
description: 'The ID of the AI workflow run item',
})
@ApiBody({
description: 'AIWorkflowRunItem update data',
type: UpdateAiWorkflowRunItemDto,
})
@ApiResponse({
status: 200,
description: 'AIWorkflowRunItem updated 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 updateRunItem(
@Param('workflowId') workflowId: string,
@Param('runId') runId: string,
@Param('itemId') itemId: string,
@Body(new ValidationPipe({ whitelist: true, transform: true }))
patchData: UpdateAiWorkflowRunItemDto,
) {
return this.aiWorkflowService.updateRunItem(
workflowId,
runId,
itemId,
patchData,
);
}

@Get('/:workflowId/runs/:runId/items')
@Roles(
UserRole.Admin,
Expand Down
69 changes: 63 additions & 6 deletions src/api/ai-workflow/ai-workflow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
CreateAiWorkflowRunDto,
UpdateAiWorkflowDto,
UpdateAiWorkflowRunDto,
UpdateAiWorkflowRunItemDto,
} from '../../dto/aiWorkflow.dto';
import { ScorecardStatus } from 'src/dto/scorecard.dto';
import { JwtUser } from 'src/shared/modules/global/jwt.service';
Expand Down Expand Up @@ -444,12 +445,12 @@ export class AiWorkflowService {
UserRole.Submitter,
].map((r) => r.toLowerCase());

const memberRoles = (
await this.resourceApiService.getMemberResourcesRoles(
challengeId,
user.userId,
)
).filter((resource) =>
const userRoles = await this.resourceApiService.getMemberResourcesRoles(
challengeId,
user.userId,
);

const memberRoles = userRoles.filter((resource) =>
requiredRoles.some(
(role) =>
resource.roleName!.toLowerCase().indexOf(role.toLowerCase()) >= 0,
Expand Down Expand Up @@ -486,4 +487,60 @@ export class AiWorkflowService {

return items;
}

async updateRunItem(
workflowId: string,
runId: string,
itemId: string,
patchData: UpdateAiWorkflowRunItemDto,
) {
const workflow = await this.prisma.aiWorkflow.findUnique({
where: { id: workflowId },
});
if (!workflow) {
this.logger.error(`Workflow with id ${workflowId} not found.`);
throw new NotFoundException(`Workflow with id ${workflowId} not found.`);
}

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

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

const updateData: any = {};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a more specific type for updateData instead of any to improve type safety and maintainability.


if (patchData.upVotes !== undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should extend the logic here to allow M2M to be able to update everything, except scorecardQuestionId

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kkartunov Now this logic is updated, can you please do a review again?

updateData.upVotes = patchData.upVotes;
}
if (patchData.downVotes !== undefined) {
updateData.downVotes = patchData.downVotes;
}

return this.prisma.aiWorkflowRunItem.update({
where: { id: itemId },
include: {
comments: true,
},
data: updateData,
});
}
}
33 changes: 33 additions & 0 deletions src/dto/aiWorkflow.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
IsInt,
IsDate,
Min,
IsUUID,
Max,
IsEmpty,
} from 'class-validator';
import { Type, Transform } from 'class-transformer';

Expand Down Expand Up @@ -138,3 +140,34 @@ export class CreateAiWorkflowRunItemsDto {
@Type(() => CreateAiWorkflowRunItemDto)
items: CreateAiWorkflowRunItemDto[];
}

export class CommentDto {
@ApiProperty({ required: false })
@IsOptional()
@IsUUID()
id?: string;

@ApiProperty()
@IsString()
@Transform(trimTransformer)
@IsNotEmpty()
content: string;

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

export class UpdateAiWorkflowRunItemDto extends PartialType(
CreateAiWorkflowRunItemDto,
) {
@IsEmpty({ message: 'scorecardQuestionId cannot be updated' })
scorecardQuestionId?: never;

@IsEmpty({ message: 'content cannot be updated' })
content?: never;

@IsEmpty({ message: 'questionScore cannot be updated' })
questionScore?: never;
}