From 012c9299edfde8e412d32eb266cfdaba5df2a499 Mon Sep 17 00:00:00 2001 From: Simon Larsen Date: Wed, 4 Sep 2024 12:50:46 +0100 Subject: [PATCH] refactor: Fix typo in CopilotActionType enum values and update related code --- Common/Models/DatabaseModels/CopilotAction.ts | 27 ------- Common/Server/API/CopilotActionAPI.ts | 76 +++++++++++++++++-- .../Types/Copilot/CopilotActionProps/Index.ts | 2 +- Common/Types/Copilot/CopilotActionType.ts | 4 +- Copilot/Init.ts | 5 +- .../CopilotActionProps/ActionPropBase.ts | 9 +++ .../CopilotActionProps/FileActionPropBase.ts | 0 .../Service/CopilotActions/ImroveReadme.ts | 2 +- Copilot/Service/CopilotActions/Index.ts | 2 +- Copilot/Types/CopilotActionTypeOrder.ts | 2 +- Copilot/Utils/CopilotAction.ts | 73 +++++++++++++++--- Copilot/Utils/CopilotActionTypes.ts | 52 +++++++++++++ 12 files changed, 200 insertions(+), 54 deletions(-) create mode 100644 Copilot/Service/CopilotActions/CopilotActionProps/ActionPropBase.ts create mode 100644 Copilot/Service/CopilotActions/CopilotActionProps/FileActionPropBase.ts create mode 100644 Copilot/Utils/CopilotActionTypes.ts diff --git a/Common/Models/DatabaseModels/CopilotAction.ts b/Common/Models/DatabaseModels/CopilotAction.ts index 1368bf035d..698df16641 100644 --- a/Common/Models/DatabaseModels/CopilotAction.ts +++ b/Common/Models/DatabaseModels/CopilotAction.ts @@ -336,33 +336,6 @@ export default class CopilotAction extends BaseModel { }) public deletedByUserId?: ObjectID = undefined; - @ColumnAccessControl({ - create: [ - Permission.ProjectOwner, - Permission.ProjectAdmin, - Permission.ProjectMember, - Permission.CreateCopilotAction, - ], - read: [ - Permission.ProjectOwner, - Permission.ProjectAdmin, - Permission.ProjectMember, - Permission.ReadCopilotAction, - ], - update: [], - }) - @TableColumn({ - type: TableColumnType.LongText, - title: "File Path in Code Repository", - required: true, - description: "File Path in Code Repository where this event was triggered", - }) - @Column({ - type: ColumnType.LongText, - nullable: false, - }) - public filePath?: string = undefined; - @ColumnAccessControl({ create: [ Permission.ProjectOwner, diff --git a/Common/Server/API/CopilotActionAPI.ts b/Common/Server/API/CopilotActionAPI.ts index 445e891aae..a27a2e5bb7 100644 --- a/Common/Server/API/CopilotActionAPI.ts +++ b/Common/Server/API/CopilotActionAPI.ts @@ -15,6 +15,10 @@ import ObjectID from "Common/Types/ObjectID"; import CopilotAction from "Common/Models/DatabaseModels/CopilotAction"; import CopilotCodeRepositoryService from "../Services/CopilotCodeRepositoryService"; import CodeRepositoryAuthorization from "../Middleware/CodeRepositoryAuthorization"; +import CopilotActionStatus from "../../Types/Copilot/CopilotActionStatus"; +import CopilotActionTypePriority from "../../Models/DatabaseModels/CopilotActionTypePriority"; +import CopilotActionTypePriorityService from "../Services/CopilotActionTypePriorityService"; +import SortOrder from "../../Types/BaseDatabase/SortOrder"; export default class CopilotActionAPI extends BaseAPI< CopilotAction, @@ -26,7 +30,7 @@ export default class CopilotActionAPI extends BaseAPI< this.router.get( `${new this.entityType() .getCrudApiPath() - ?.toString()}/copilot-actions-by-file/:secretkey`, + ?.toString()}/copilot-action-types-by-priority/:secretkey`, CodeRepositoryAuthorization.isAuthorizedRepository, async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { try { @@ -36,10 +40,68 @@ export default class CopilotActionAPI extends BaseAPI< throw new BadDataException("Secret key is required"); } - const filePath: string = req.body["filePath"]!; + const codeRepository: CopilotCodeRepository | null = + await CopilotCodeRepositoryService.findOneBy({ + query: { + secretToken: new ObjectID(secretkey), + }, + select: { + _id: true, + }, + props: { + isRoot: true, + }, + }); - if (!filePath) { - throw new BadDataException("File path is required"); + if (!codeRepository) { + throw new BadDataException( + "Code repository not found. Secret key is invalid.", + ); + } + + const copilotActionTypes: Array = + await CopilotActionTypePriorityService.findBy({ + query: { + codeRepositoryId: codeRepository.id! + }, + select: { + _id: true, + actionType: true, + priority: true, + }, + skip: 0, + sort: { + priority: SortOrder.Ascending, + }, + limit: LIMIT_PER_PROJECT, + props: { + isRoot: true, + }, + }); + + return Response.sendJsonObjectResponse(req, res, { + actionTypes: CopilotActionTypePriority.toJSONArray( + copilotActionTypes, + CopilotActionTypePriority, + ), + }); + } catch (err) { + next(err); + } + }, + ); + + this.router.get( + `${new this.entityType() + .getCrudApiPath() + ?.toString()}/copilot-actions-in-queue/:secretkey`, + CodeRepositoryAuthorization.isAuthorizedRepository, + async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { + try { + const secretkey: string = req.params["secretkey"]!; + + if (!secretkey) { + throw new BadDataException("Secret key is required"); } const serviceCatalogId: string = req.body["serviceCatalogId"]!; @@ -71,14 +133,13 @@ export default class CopilotActionAPI extends BaseAPI< await CopilotActionService.findBy({ query: { codeRepositoryId: codeRepository.id!, - filePath: filePath, serviceCatalogId: new ObjectID(serviceCatalogId), + copilotActionStatus: CopilotActionStatus.IN_QUEUE }, select: { _id: true, codeRepositoryId: true, serviceCatalogId: true, - filePath: true, copilotActionStatus: true, copilotActionType: true, createdAt: true, @@ -110,7 +171,7 @@ export default class CopilotActionAPI extends BaseAPI< this.router.post( `${new this.entityType() .getCrudApiPath() - ?.toString()}/add-copilot-action/:secretkey`, + ?.toString()}/queue-copilot-action/:secretkey`, CodeRepositoryAuthorization.isAuthorizedRepository, async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => { try { @@ -147,6 +208,7 @@ export default class CopilotActionAPI extends BaseAPI< copilotAction.codeRepositoryId = codeRepository.id!; copilotAction.projectId = codeRepository.projectId!; + copilotAction.copilotActionStatus = CopilotActionStatus.IN_QUEUE; const createdAction: CopilotAction = await CopilotActionService.create({ diff --git a/Common/Types/Copilot/CopilotActionProps/Index.ts b/Common/Types/Copilot/CopilotActionProps/Index.ts index 7f82024ba8..a9f6a8db2b 100644 --- a/Common/Types/Copilot/CopilotActionProps/Index.ts +++ b/Common/Types/Copilot/CopilotActionProps/Index.ts @@ -85,7 +85,7 @@ export class CopilotActionPropUtil { return CopilotActionPropType.Directory; } - if (actionType === CopilotActionType.IMRPOVE_README) { + if (actionType === CopilotActionType.IMPROVE_README) { return CopilotActionPropType.File; } diff --git a/Common/Types/Copilot/CopilotActionType.ts b/Common/Types/Copilot/CopilotActionType.ts index d87da5cb01..993e288462 100644 --- a/Common/Types/Copilot/CopilotActionType.ts +++ b/Common/Types/Copilot/CopilotActionType.ts @@ -2,7 +2,7 @@ enum CopilotActionType { IMPROVE_COMMENTS = "Improve Comments", ADD_COMMENTS = "Add Comments", - IMRPOVE_README = "Improve Readme", + IMPROVE_README = "Improve Readme", ADD_README = "Add Readme", FIX_GRAMMAR_AND_SPELLING = "Fix Grammar and Spelling", @@ -148,7 +148,7 @@ export class CopilotActionTypeUtil { dependsOn: [], }, { - type: CopilotActionType.IMRPOVE_README, + type: CopilotActionType.IMPROVE_README, description: "Improve the README file", defaultPriority: 4, dependsOn: [], diff --git a/Copilot/Init.ts b/Copilot/Init.ts index 6a44fc1fe0..e139ace316 100644 --- a/Copilot/Init.ts +++ b/Copilot/Init.ts @@ -59,6 +59,7 @@ const init: PromiseVoidFunction = async (): Promise => { await setUpRepository(); for (const serviceRepository of servicesToImprove) { + checkIfCurrentFixCountIsLessThanFixNumberOfCodeEventsInEachRun(); const filesInService: Dictionary = @@ -72,10 +73,6 @@ const init: PromiseVoidFunction = async (): Promise => { }`, ); - // const files: Array = ArrayUtil.shuffle( - // Object.values(filesInService), - // ); // shuffle the files to avoid fixing the same file in each run. - const files: Array = Object.values(filesInService); for (const file of files) { diff --git a/Copilot/Service/CopilotActions/CopilotActionProps/ActionPropBase.ts b/Copilot/Service/CopilotActions/CopilotActionProps/ActionPropBase.ts new file mode 100644 index 0000000000..3614b6a1a6 --- /dev/null +++ b/Copilot/Service/CopilotActions/CopilotActionProps/ActionPropBase.ts @@ -0,0 +1,9 @@ +import NotImplementedException from "Common/Types/Exception/NotImplementedException"; + +export default class CopilotActionPropBase{ + public static async isActionRequired(data: { + copilotActionBase + }): Promise { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Copilot/Service/CopilotActions/CopilotActionProps/FileActionPropBase.ts b/Copilot/Service/CopilotActions/CopilotActionProps/FileActionPropBase.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Copilot/Service/CopilotActions/ImroveReadme.ts b/Copilot/Service/CopilotActions/ImroveReadme.ts index cc8a048f53..9662e7888e 100644 --- a/Copilot/Service/CopilotActions/ImroveReadme.ts +++ b/Copilot/Service/CopilotActions/ImroveReadme.ts @@ -9,7 +9,7 @@ import CodeRepositoryUtil from "../../Utils/CodeRepository"; export default class ImproveReadme extends CopilotActionBase { public constructor() { super(); - this.copilotActionType = CopilotActionType.IMRPOVE_README; + this.copilotActionType = CopilotActionType.IMPROVE_README; this.acceptFileExtentions = CodeRepositoryUtil.getReadmeFileExtentions(); } diff --git a/Copilot/Service/CopilotActions/Index.ts b/Copilot/Service/CopilotActions/Index.ts index 2b496c7c09..0889a7a19b 100644 --- a/Copilot/Service/CopilotActions/Index.ts +++ b/Copilot/Service/CopilotActions/Index.ts @@ -31,7 +31,7 @@ const actionDictionary: Dictionary = { [CopilotActionType.FIX_GRAMMAR_AND_SPELLING]: FixGrammarAndSpelling, [CopilotActionType.REFACTOR_CODE]: RefactorCode, [CopilotActionType.WRITE_UNIT_TESTS]: WriteUnitTests, - [CopilotActionType.IMRPOVE_README]: ImproveReadme, + [CopilotActionType.IMPROVE_README]: ImproveReadme, }; export interface CopilotExecutionResult { diff --git a/Copilot/Types/CopilotActionTypeOrder.ts b/Copilot/Types/CopilotActionTypeOrder.ts index 4a4e971ca5..a3a2bd78fb 100644 --- a/Copilot/Types/CopilotActionTypeOrder.ts +++ b/Copilot/Types/CopilotActionTypeOrder.ts @@ -5,7 +5,7 @@ const CopiotActionTypeOrder: Array = [ // CopilotActionType.REFACTOR_CODE, // CopilotActionType.FIX_GRAMMAR_AND_SPELLING, // CopilotActionType.IMPROVE_VARIABLE_NAMES, - // CopilotActionType.IMRPOVE_README, + // CopilotActionType.IMPROVE_README, // CopilotActionType.WRITE_UNIT_TESTS, ]; diff --git a/Copilot/Utils/CopilotAction.ts b/Copilot/Utils/CopilotAction.ts index e80c7eed4f..6e77035143 100644 --- a/Copilot/Utils/CopilotAction.ts +++ b/Copilot/Utils/CopilotAction.ts @@ -8,18 +8,72 @@ import HTTPResponse from "Common/Types/API/HTTPResponse"; import API from "Common/Utils/API"; import ObjectID from "Common/Types/ObjectID"; import logger from "Common/Server/Utils/Logger"; +import CopilotActionTypePriority from "Common/Models/DatabaseModels/CopilotActionTypePriority"; export default class CopilotActionUtil { - public static async getCopilotActions(data: { - filePath: string; - serviceCatalogId: ObjectID; - }): Promise> { - if (!data.filePath) { - throw new BadDataException("File path is required"); + + public static async getActionTypesBasedOnPriority(): Promise> { + + const repositorySecretKey: string | null = GetRepositorySecretKey(); + + const url: URL = URL.fromString( + GetOneUptimeURL().toString() + "/api", + ).addRoute( + `${new CopilotAction().getCrudApiPath()?.toString()}/copilot-action-types-by-priority/${repositorySecretKey}`, + ); + + const actionTypesResult: HTTPErrorResponse | HTTPResponse = + await API.get(url); + + if (actionTypesResult instanceof HTTPErrorResponse) { + throw actionTypesResult; } + const actionTypes: Array = CopilotActionTypePriority.fromJSONArray( + actionTypesResult.data["actionTypes"] as JSONArray, + CopilotActionTypePriority, + ) || []; + + logger.debug(`Copilot action types based on priority: ${JSON.stringify(actionTypes, null, 2)}`); + + return actionTypes; + } + + public static async getActionsToWorkOn(data: { + serviceCatalogId: ObjectID; + }): Promise> { if (!data.serviceCatalogId) { - throw new BadDataException("Service catalog id is required"); + throw new BadDataException("Service Catalog ID is required"); + } + + const repositorySecretKey: string | null = GetRepositorySecretKey(); + + if (!repositorySecretKey) { + throw new BadDataException("Repository Secret Key is required"); + } + + // check actions in queue + + const actionsInQueue: Array = await CopilotActionUtil.getInQueueActions({ + serviceCatalogId: data.serviceCatalogId, + }); + + if(actionsInQueue.length > 0) { + logger.debug(`Actions in queue: ${JSON.stringify(actionsInQueue, null, 2)}`); + return actionsInQueue; + } + + const getEnabledActionsBasedOnPriority + + + } + + + public static async getInQueueActions(data: { + serviceCatalogId: ObjectID; + }): Promise> { + if (!data.serviceCatalogId) { + throw new BadDataException("Service Catalog ID is required"); } const repositorySecretKey: string | null = GetRepositorySecretKey(); @@ -33,12 +87,11 @@ export default class CopilotActionUtil { ).addRoute( `${new CopilotAction() .getCrudApiPath() - ?.toString()}/copilot-actions-by-file/${repositorySecretKey}`, + ?.toString()}/copilot-actions-in-queue/${repositorySecretKey}`, ); const copilotActionsResult: HTTPErrorResponse | HTTPResponse = await API.get(url, { - filePath: data.filePath, serviceCatalogId: data.serviceCatalogId.toString(), }); @@ -53,7 +106,7 @@ export default class CopilotActionUtil { ) || []; logger.debug( - `Copilot events fetched successfully for file path: ${data.filePath} and service catalog id: ${data.serviceCatalogId}`, + `Copilot actions in queue for service catalog id: ${data.serviceCatalogId}`, ); logger.debug(`Copilot events: ${JSON.stringify(copilotActions, null, 2)}`); diff --git a/Copilot/Utils/CopilotActionTypes.ts b/Copilot/Utils/CopilotActionTypes.ts new file mode 100644 index 0000000000..f02119da0c --- /dev/null +++ b/Copilot/Utils/CopilotActionTypes.ts @@ -0,0 +1,52 @@ +import CopilotActionTypePriority from "Common/Models/DatabaseModels/CopilotActionTypePriority"; +import CopilotActionType from "Common/Types/Copilot/CopilotActionType"; +import CopilotActionUtil from "./CopilotAction"; + +export default class CopilotActionTypeUtil { + private static isActionEnabled(actionType: CopilotActionType): boolean { + if (actionType === CopilotActionType.ADD_COMMENTS) { + return true; + } + + if (actionType === CopilotActionType.IMPROVE_COMMENTS) { + return true; + } + + if (actionType === CopilotActionType.ADD_LOGS) { + return true; + } + + if (actionType === CopilotActionType.IMPROVE_LOGS) { + return true; + } + + // readme + if (actionType === CopilotActionType.ADD_README) { + return true; + } + + if (actionType === CopilotActionType.IMPROVE_README) { + return true; + } + + return false; + } + + public static async getEnabledActionTypesBasedOnPriority( + + ): Promise> { + + // if there are no actions then, get actions based on priority + const actionTypes: Array = await CopilotActionUtil.getActionTypesBasedOnPriority(); + + const enabledActions: Array = []; + + for (const actionType of actionTypes) { + if (this.isActionEnabled(actionType.actionType!)) { + enabledActions.push(actionType.actionType!); + } + } + + return enabledActions; + } +} \ No newline at end of file