Repository: techminds-unipd/mvp
Pull request #33: Backend delete workflow
Author: login: Giuseppe5000
Labels: name: backend
Created: 2025-03-12T11:34:33Z | Merged: 2025-03-12T18:16:33Z

close #8 

Unica piccolezza è che deleteWorkflow di repository ritorna null in due casi:
1. non c'è user
2. non c'è il workflow

Io assumo sempre che il null sia sempre il secondo caso, perchè l'esistenza del jwt implica che lo user deve esistere.

Linked issue #8: Codifica funzionalità backend DeleteWorkflow

Diff:
diff --git a/backend/src/BusinessErrors.ts b/backend/src/BusinessErrors.ts
index c3658eb..e69a096 100644
--- a/backend/src/BusinessErrors.ts
+++ b/backend/src/BusinessErrors.ts
@@ -20,7 +20,6 @@ export class WrongPasswordError extends BusinessError {
     message = "Password doesn't match when attempt to login";
 }
 
-
 // WORKFLOW ERRORS
 
 export class WorkflowAlreadyExistsError extends BusinessError {
diff --git a/backend/src/workflow/WorkflowModule.ts b/backend/src/workflow/WorkflowModule.ts
index 7790ae6..768c3e8 100644
--- a/backend/src/workflow/WorkflowModule.ts
+++ b/backend/src/workflow/WorkflowModule.ts
@@ -1,5 +1,13 @@
 import { Module } from "@nestjs/common";
 import { MongooseModule } from "@nestjs/mongoose";
+
+import { UserEntity, userEntitySchema } from "src/user/adapter/output/UserEntity";
+import { DELETE_WORKFLOW_USE_CASE } from "./service/port/input/DeleteWorkflowUseCase";
+import { DeleteWorkflowService } from "./service/DeleteWorkflowService";
+import { DELETE_WORKFLOW_PORT } from "./service/port/output/DeleteWorkflowPort";
+import WorkflowPortAdapter from "./adapter/output/WorkflowPortAdapter";
+import { WorkflowRepository } from "./adapter/output/WorkflowRepository";
+import DeleteWorkflowController from "./adapter/input/DeleteWorkflowController";
 import CreateWorkflowController from "./adapter/input/CreateWorkflowController";
 import { CREATE_WORKFLOW_USE_CASE } from "./service/port/input/CreateWorkflowUseCase";
 import CreateWorkflowService from "./service/CreateWorkflowService";
@@ -7,15 +15,14 @@ import { CREATE_WORKFLOW_PORT } from "./service/port/output/CreateWorkflowPort";
 import GetWorkflowController from "./adapter/input/GetWorkflowController";
 import { GET_WORKFLOW_USE_CASE } from "./service/port/input/GetWorkflowUseCase";
 import { GET_WORKFLOW_PORT } from "./service/port/output/GetWorkflowPort";
-import WorkflowPortAdapter from "./adapter/output/WorkflowPortAdapter";
 import { GetWorkflowService } from "./service/GetWorkflowService";
-import { WorkflowRepository } from "./adapter/output/WorkflowRepository";
-import { UserEntity, userEntitySchema } from "src/user/adapter/output/UserEntity";
 
 @Module({
     imports: [MongooseModule.forFeature([{ name: UserEntity.name, schema: userEntitySchema }])],
-    controllers: [CreateWorkflowController, GetWorkflowController],
+    controllers: [DeleteWorkflowController, GetWorkflowController, CreateWorkflowController],
     providers: [
+        { provide: DELETE_WORKFLOW_USE_CASE, useClass: DeleteWorkflowService },
+        { provide: DELETE_WORKFLOW_PORT, useClass: WorkflowPortAdapter },
         { provide: CREATE_WORKFLOW_USE_CASE, useClass: CreateWorkflowService },
         { provide: CREATE_WORKFLOW_PORT, useClass: WorkflowPortAdapter },
         { provide: GET_WORKFLOW_USE_CASE, useClass: GetWorkflowService },
@@ -24,6 +31,3 @@ import { UserEntity, userEntitySchema } from "src/user/adapter/output/UserEntity
     ]
 })
 export class WorkflowModule {}
-
-
-
diff --git a/backend/src/workflow/adapter/input/DeleteWorkflowController.ts b/backend/src/workflow/adapter/input/DeleteWorkflowController.ts
index e69de29..20f6d12 100644
--- a/backend/src/workflow/adapter/input/DeleteWorkflowController.ts
+++ b/backend/src/workflow/adapter/input/DeleteWorkflowController.ts
@@ -0,0 +1,44 @@
+import { Controller, Get, HttpException, HttpStatus, Inject, Param, Request, UseGuards } from "@nestjs/common";
+import { EdgeDTO, NodeDataDTO, NodeDTO, PositionDTO, RequestHeader, WorkflowDTO } from "./WorkflowDTO";
+import { AuthGuard } from "./AuthGuard";
+import { Workflow } from "src/workflow/domain/Workflow";
+import { DeleteWorkflowUseCase, DELETE_WORKFLOW_USE_CASE } from "src/workflow/service/port/input/DeleteWorkflowUseCase";
+import DeleteWorkflowCommand from "src/workflow/domain/DeleteWorkflowCommand";
+import { ApiBearerAuth } from "@nestjs/swagger";
+import { WorkflowNotFoundError } from "src/BusinessErrors";
+
+@ApiBearerAuth()
+@Controller("workflow")
+class DeleteWorkflowController {
+    constructor(@Inject(DELETE_WORKFLOW_USE_CASE) private readonly deletetWorkflowUseCase: DeleteWorkflowUseCase) {}
+
+    private toDTO(workflow: Workflow): WorkflowDTO {
+        const nodes: NodeDTO[] = workflow.nodes.map(
+            (node, index) =>
+                new NodeDTO(index, new PositionDTO(node.position.x, node.position.y), new NodeDataDTO(node.type))
+        );
+        const edges: EdgeDTO[] = [];
+
+        for (let i = 0; i < workflow.nodes.length - 1; i++) edges.push(new EdgeDTO(workflow.nodes[i].action, i, i + 1));
+
+        return new WorkflowDTO(workflow.name, nodes, edges);
+    }
+
+    @UseGuards(AuthGuard)
+    @Get("/delete/:name")
+    async deleteWorkflow(@Param("name") workflowName: string, @Request() request: RequestHeader): Promise<WorkflowDTO> {
+        try {
+            const username = request.username;
+            const workflow = await this.deletetWorkflowUseCase.deleteWorkflow(
+                new DeleteWorkflowCommand(username, workflowName)
+            );
+            return this.toDTO(workflow);
+        } catch (error) {
+            if (error instanceof WorkflowNotFoundError)
+                throw new HttpException("Workflow not found", HttpStatus.BAD_REQUEST);
+            throw new HttpException("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+}
+
+export default DeleteWorkflowController;
diff --git a/backend/src/workflow/adapter/output/WorkflowPortAdapter.ts b/backend/src/workflow/adapter/output/WorkflowPortAdapter.ts
index 520d511..38642de 100644
--- a/backend/src/workflow/adapter/output/WorkflowPortAdapter.ts
+++ b/backend/src/workflow/adapter/output/WorkflowPortAdapter.ts
@@ -34,6 +34,12 @@ class WorkflowPortAdapter implements GetWorkflowPort, CreateWorkflowPort {
         return this.toDomain(workflowEntity);
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<Workflow | null> {
+        const workflowEntity = await this.workflowRepository.deleteWorkflow(username, workflowName);
+        if (!workflowEntity) return null;
+        return this.toDomain(workflowEntity);
+    }
+
     async addWorkflow(username: string, workflow: Workflow): Promise<Workflow | null> {
         const addedWorkflow = await this.workflowRepository.addWorkflow(username, this.toEntity(workflow));
         if (!addedWorkflow) return null;
diff --git a/backend/src/workflow/adapter/output/WorkflowRepository.ts b/backend/src/workflow/adapter/output/WorkflowRepository.ts
index 177be07..296d6b5 100644
--- a/backend/src/workflow/adapter/output/WorkflowRepository.ts
+++ b/backend/src/workflow/adapter/output/WorkflowRepository.ts
@@ -17,6 +17,21 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const workflow = await this.getWorkflowByName(username, workflowName);
+        // workflow non trovato
+        if (!workflow) return null;
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } }, { new: true })
+            .exec();
+        // user non trovato (impossibile, se vuoi per me si può togliere questo controllo perché se lo user non c'è allora esce nel controllo sopra)
+        if (!user) return null;
+        const deletedWorkflow = user.workflows.find((w) => w.name === workflowName);
+        // workflow non cancellato
+        if (deletedWorkflow) return null;
+        return workflow;
+    }
+
     async addWorkflow(username: string, workflow: WorkflowEntity): Promise<WorkflowEntity | null> {
         const user = await this.userEntityModel
             .findOneAndUpdate({ username: username }, { $push: { workflows: workflow } }, { new: true })
diff --git a/backend/src/workflow/domain/DeleteWorkflowCommand.ts b/backend/src/workflow/domain/DeleteWorkflowCommand.ts
new file mode 100644
index 0000000..de853c3
--- /dev/null
+++ b/backend/src/workflow/domain/DeleteWorkflowCommand.ts
@@ -0,0 +1,9 @@
+export default class DeleteWorkflowCommand {
+    readonly username: string;
+    readonly workflowName: string;
+
+    constructor(username: string, workflowName: string) {
+        this.username = username;
+        this.workflowName = workflowName;
+    }
+}
diff --git a/backend/src/workflow/service/DeleteWorkflowService.ts b/backend/src/workflow/service/DeleteWorkflowService.ts
index e69de29..22185fc 100644
--- a/backend/src/workflow/service/DeleteWorkflowService.ts
+++ b/backend/src/workflow/service/DeleteWorkflowService.ts
@@ -0,0 +1,17 @@
+import { Inject, Injectable } from "@nestjs/common";
+import { Workflow } from "../domain/Workflow";
+import DeleteWorkflowCommand from "../domain/DeleteWorkflowCommand";
+import { DeleteWorkflowUseCase } from "./port/input/DeleteWorkflowUseCase";
+import { DELETE_WORKFLOW_PORT, DeleteWorkflowPort } from "./port/output/DeleteWorkflowPort";
+import { WorkflowNotFoundError } from "src/BusinessErrors";
+
+@Injectable()
+export class DeleteWorkflowService implements DeleteWorkflowUseCase {
+    constructor(@Inject(DELETE_WORKFLOW_PORT) private readonly deleteWorkflowPort: DeleteWorkflowPort) {}
+
+    async deleteWorkflow(cmd: DeleteWorkflowCommand): Promise<Workflow> {
+        const deletedWorkflow = await this.deleteWorkflowPort.deleteWorkflow(cmd.username, cmd.workflowName);
+        if (!deletedWorkflow) throw new WorkflowNotFoundError();
+        return deletedWorkflow;
+    }
+}
diff --git a/backend/src/workflow/service/port/input/DeleteWorkflowUseCase.ts b/backend/src/workflow/service/port/input/DeleteWorkflowUseCase.ts
index e69de29..5c0ed0d 100644
--- a/backend/src/workflow/service/port/input/DeleteWorkflowUseCase.ts
+++ b/backend/src/workflow/service/port/input/DeleteWorkflowUseCase.ts
@@ -0,0 +1,8 @@
+import DeleteWorkflowCommand from "src/workflow/domain/DeleteWorkflowCommand";
+import { Workflow } from "src/workflow/domain/Workflow";
+
+export interface DeleteWorkflowUseCase {
+    deleteWorkflow(cmd: DeleteWorkflowCommand): Promise<Workflow>;
+}
+
+export const DELETE_WORKFLOW_USE_CASE = "DELETE_WORKFLOW_USE_CASE";
diff --git a/backend/src/workflow/service/port/output/DeleteWorkflowPort.ts b/backend/src/workflow/service/port/output/DeleteWorkflowPort.ts
index e69de29..e6ce40d 100644
--- a/backend/src/workflow/service/port/output/DeleteWorkflowPort.ts
+++ b/backend/src/workflow/service/port/output/DeleteWorkflowPort.ts
@@ -0,0 +1,7 @@
+import { Workflow } from "src/workflow/domain/Workflow";
+
+export interface DeleteWorkflowPort {
+    deleteWorkflow(username: string, workflowName: string): Promise<Workflow | null>;
+}
+
+export const DELETE_WORKFLOW_PORT = "DELETE_WORKFLOW_PORT";
diff --git a/backend/test/workflow/adapter/input/DeleteWorkflowController.spec.ts b/backend/test/workflow/adapter/input/DeleteWorkflowController.spec.ts
index 156a2eb..fdc2f15 100644
--- a/backend/test/workflow/adapter/input/DeleteWorkflowController.spec.ts
+++ b/backend/test/workflow/adapter/input/DeleteWorkflowController.spec.ts
@@ -1 +1,72 @@
-it("",  () => { expect(true).toBe(true); });
\ No newline at end of file
+import { Test, TestingModule } from "@nestjs/testing";
+import { JwtService } from "@nestjs/jwt";
+import { MongooseError } from "mongoose";
+import { HttpException, HttpStatus } from "@nestjs/common";
+import { Node, NodeType, Point, Workflow } from "src/workflow/domain/Workflow";
+import { EdgeDTO, NodeDataDTO, NodeDTO, PositionDTO, WorkflowDTO } from "src/workflow/adapter/input/WorkflowDTO";
+import DeleteWorkflowController from "src/workflow/adapter/input/DeleteWorkflowController";
+import { DELETE_WORKFLOW_USE_CASE } from "src/workflow/service/port/input/DeleteWorkflowUseCase";
+import { WorkflowNotFoundError } from "src/BusinessErrors";
+
+describe("DeleteWorkflowController", () => {
+    let deleteWorkflowController: DeleteWorkflowController;
+    let deleteWorkflowUseCaseMock: { deleteWorkflow: jest.Mock };
+    let jwtService: { verifyAsync: jest.Mock };
+    const workflowMock = new Workflow("prova", [
+        new Node(NodeType.GCalendar, "action1", new Point(1, 1)),
+        new Node(NodeType.Gmail, "action2", new Point(2, 2)),
+        new Node(NodeType.Pastebin, "", new Point(3, 3))
+    ]);
+    const workflowDTOMock = new WorkflowDTO("prova", [
+        new NodeDTO(0, new PositionDTO(1, 1), new NodeDataDTO("GCALENDAR")),
+        new NodeDTO(1, new PositionDTO(2, 2), new NodeDataDTO("GMAIL")),
+        new NodeDTO(2, new PositionDTO(3, 3), new NodeDataDTO("PASTEBIN"))
+    ], [
+        new EdgeDTO("action1", 0, 1),
+        new EdgeDTO("action2", 1, 2)
+    ]);
+
+    const createTestingModule = async () => {
+        const module: TestingModule = await Test.createTestingModule({
+            controllers: [DeleteWorkflowController],
+            providers: [
+                { provide: DELETE_WORKFLOW_USE_CASE, useValue: deleteWorkflowUseCaseMock },
+                { provide: JwtService, useValue: jwtService }
+            ]
+        }).compile();
+        deleteWorkflowController = module.get<DeleteWorkflowController>(DeleteWorkflowController);
+    };
+
+    beforeEach(async () => {
+        deleteWorkflowUseCaseMock = { deleteWorkflow: jest.fn() };
+        jwtService = { verifyAsync: jest.fn() };
+        await createTestingModule();
+    });
+
+    describe("deleteWorkflow", () => {
+        it("should delete the workflow by its name", async () => {
+            deleteWorkflowUseCaseMock.deleteWorkflow.mockResolvedValue(workflowMock);
+            expect(await deleteWorkflowController.deleteWorkflow("prova", { username: "username" })).toEqual(workflowDTOMock);
+        });
+
+        it("should throw HttpException because the database throws an exception", async () => {
+            deleteWorkflowUseCaseMock.deleteWorkflow.mockImplementation(() => {
+                throw new MongooseError("");
+            });
+            const result = deleteWorkflowController.deleteWorkflow("prova", { username: "username" });
+
+            expect(result).rejects.toThrow(HttpException);
+            expect(result).rejects.toHaveProperty("status", HttpStatus.INTERNAL_SERVER_ERROR);
+        });
+
+        it("should throw HttpException because the workflow was not found in the database", async () => {
+            deleteWorkflowUseCaseMock.deleteWorkflow.mockImplementation(() => {
+                throw new WorkflowNotFoundError;
+            });
+            const result = deleteWorkflowController.deleteWorkflow("prova", { username: "username" });
+            expect(result).rejects.toThrow(HttpException);
+            expect(result).rejects.toHaveProperty("status", HttpStatus.BAD_REQUEST);
+        });
+
+    });
+});
diff --git a/backend/test/workflow/adapter/output/WorkflowPortAdapter.spec.ts b/backend/test/workflow/adapter/output/WorkflowPortAdapter.spec.ts
index 53963c0..6bda314 100644
--- a/backend/test/workflow/adapter/output/WorkflowPortAdapter.spec.ts
+++ b/backend/test/workflow/adapter/output/WorkflowPortAdapter.spec.ts
@@ -6,7 +6,7 @@ import { Node, NodeType, Point, Workflow } from "src/workflow/domain/Workflow";
 
 describe("WorkflowPortAdapter", () => {
     let workflowPortAdapter: WorkflowPortAdapter;
-    let workflowRepositoryMock: { getWorkflowByName: jest.Mock, addWorkflow: jest.Mock };
+    let workflowRepositoryMock: { getWorkflowByName: jest.Mock, addWorkflow: jest.Mock, deleteWorkflow: jest.Mock };
     const workflowMock = new Workflow("prova", [
         new Node(NodeType.GCalendar, "action1", new Point(1, 1)),
         new Node(NodeType.Gmail, "action2", new Point(2, 2)),
@@ -31,7 +31,7 @@ describe("WorkflowPortAdapter", () => {
     };
 
     beforeEach(async () => {
-        workflowRepositoryMock = { getWorkflowByName: jest.fn(), addWorkflow: jest.fn() };
+        workflowRepositoryMock = { getWorkflowByName: jest.fn(), addWorkflow: jest.fn(), deleteWorkflow: jest.fn() };
         await createTestingModule();
     });
 
@@ -57,5 +57,18 @@ describe("WorkflowPortAdapter", () => {
             workflowRepositoryMock.addWorkflow.mockResolvedValue(null);
             expect(await workflowPortAdapter.addWorkflow("prova", workflowEmptyMock)).toEqual(null);
         });
+    })
+
+    describe("deleteWorkflow", () => {
+        it("should return the workflow of the user", async () => {
+            workflowRepositoryMock.deleteWorkflow.mockResolvedValue(workflowEntityMock);
+            expect(await workflowPortAdapter.deleteWorkflow("username", "prova")).toEqual(workflowMock);
+        });
+
+        it("should return null if the workflow doesn't exists", async () => {
+            workflowRepositoryMock.deleteWorkflow.mockResolvedValue(null);
+            expect(await workflowPortAdapter.deleteWorkflow("username", "prova")).toEqual(null);
+        });
     });
-});
\ No newline at end of file
+
+});
diff --git a/backend/test/workflow/adapter/output/WorkflowRepository.spec.ts b/backend/test/workflow/adapter/output/WorkflowRepository.spec.ts
index be3b03a..7063413 100644
--- a/backend/test/workflow/adapter/output/WorkflowRepository.spec.ts
+++ b/backend/test/workflow/adapter/output/WorkflowRepository.spec.ts
@@ -38,7 +38,7 @@ describe("WorkflowRepository", () => {
     };
 
     beforeEach(async () => {
-        userEntityModelMock = { findOne: jest.fn(), create: jest.fn(), exec: jest.fn(), findOneAndUpdate: jest.fn() };
+        userEntityModelMock = { findOne: jest.fn(), create: jest.fn(), exec: jest.fn(), findOneAndUpdate: jest.fn()};
         await createTestingModule();
     });
 
@@ -75,5 +75,30 @@ describe("WorkflowRepository", () => {
         });
     });
 
+    describe("deleteWorkflow", () => {
+        it("should return a workflow", async () => {
+            workflowRepository.getWorkflowByName = jest.fn();
+            (workflowRepository.getWorkflowByName as jest.Mock).mockResolvedValue(workflowEntityMock);
+            userEntityModelMock.findOneAndUpdate.mockReturnThis();
+            userEntityModelMock.exec.mockResolvedValue(userEntityEmptyWorkflowMock);
+            expect(await workflowRepository.deleteWorkflow("username", "prova")).toEqual(workflowEntityMock);
+        });
+
+        it("should return null if the workflow doesn't exists", async () => {
+            workflowRepository.getWorkflowByName = jest.fn();
+            (workflowRepository.getWorkflowByName as jest.Mock).mockResolvedValue(null);
+            expect(await workflowRepository.deleteWorkflow("username", "prova")).toEqual(null);
+        });
+
+        it("should return null if it didn't delete the workflow", async () => {
+            workflowRepository.getWorkflowByName = jest.fn();
+            (workflowRepository.getWorkflowByName as jest.Mock).mockResolvedValue(workflowEntityMock);
+            userEntityModelMock.findOneAndUpdate.mockReturnThis();
+            userEntityModelMock.exec.mockResolvedValue(userEntityMock);
+            expect(await workflowRepository.deleteWorkflow("username", "prova")).toEqual(null);
+        });
+        
+    });
+
 });
 
diff --git a/backend/test/workflow/service/DeleteWorkflowService.spec.ts b/backend/test/workflow/service/DeleteWorkflowService.spec.ts
index 156a2eb..660e777 100644
--- a/backend/test/workflow/service/DeleteWorkflowService.spec.ts
+++ b/backend/test/workflow/service/DeleteWorkflowService.spec.ts
@@ -1 +1,37 @@
-it("",  () => { expect(true).toBe(true); });
\ No newline at end of file
+import { Test, TestingModule } from "@nestjs/testing";
+import { WorkflowNotFoundError } from "src/BusinessErrors";
+import DeleteWorkflowCommand from "src/workflow/domain/DeleteWorkflowCommand";
+import { Workflow } from "src/workflow/domain/Workflow";
+import { DeleteWorkflowService } from "src/workflow/service/DeleteWorkflowService";
+import { DELETE_WORKFLOW_PORT } from "src/workflow/service/port/output/DeleteWorkflowPort";
+
+describe("GetWorkflowService", () => {
+    let deleteWorkflowService: DeleteWorkflowService;
+    let deleteWorkflowPortMock: { deleteWorkflow: jest.Mock };
+    let deleteWorkflowCommandMock = new DeleteWorkflowCommand("username", "prova");
+    const workflowMock = new Workflow("prova", []);
+
+    const createTestingModule = async () => {
+        const module: TestingModule = await Test.createTestingModule({
+            providers: [DeleteWorkflowService, { provide: DELETE_WORKFLOW_PORT, useValue: deleteWorkflowPortMock }]
+        }).compile();
+        deleteWorkflowService = module.get<DeleteWorkflowService>(DeleteWorkflowService);
+    };
+
+    beforeEach(async () => {
+        deleteWorkflowPortMock = { deleteWorkflow: jest.fn() };
+        await createTestingModule();
+    });
+
+    describe("deleteWorkflow", () => {
+        it("should delete the workflow by its name", async () => {
+            deleteWorkflowPortMock.deleteWorkflow.mockResolvedValue(workflowMock);
+            expect(await deleteWorkflowService.deleteWorkflow(deleteWorkflowCommandMock)).toEqual(workflowMock);
+        });
+
+        it("shouldn't find the workflow in the database", async () => {
+            deleteWorkflowPortMock.deleteWorkflow.mockResolvedValue(null);
+            expect(deleteWorkflowService.deleteWorkflow(deleteWorkflowCommandMock)).rejects.toThrow(WorkflowNotFoundError);
+        });
+    });
+});


Reviews:
user: login: MatteoSquarz
body: Quello che dici sul null mi sembra ragionevole
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T12:55:25Z
user: login: Jacky8703
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T12:56:13Z
user: login: Giuseppe5000
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:34:43Z
user: login: Giuseppe5000
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:36:28Z
user: login: Jacky8703
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:36:52Z
user: login: Jacky8703
body: alcuni dubbi
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T13:48:19Z
user: login: Jacky8703
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:00:00Z
user: login: Giuseppe5000
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:05:06Z
user: login: Jacky8703
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:08:39Z
user: login: Jacky8703
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:09:42Z
user: login: Jacky8703
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:17:30Z
user: login: Jacky8703
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T14:22:28Z
user: login: Jacky8703
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T18:11:32Z

Review comments:
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Jacky8703
body: occhio che così non controlli veramente se il workflow è stato tolto perché findOneAndUpdate se non metti { new: true } ti ritorna il documento prima che venga modificato, quindi il workflow rimane in user.
Edit: leggendo meglio ho capito il senso di quello che hai fatto, però sarebbe da trovare un modo per controllare che il workflow sia stato veramente eliminato perché per com'è adesso non lo sai
created_at: 2025-03-12T12:56:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Giuseppe5000
body: All'inizio facevo due query, un findOne per controllare l'esistenza (e riprendere i dati del workflow) e poi updateOne che ritornava un oggetto con dei metadati (tipo numero di documenti cambiati) che controllavo per capire che la cancellazione fosse avvenuta.
Il problema è che poi il mock di exec creava dei problemi (puoi andare indietro di qualche commit per guardare cosa intendo).
created_at: 2025-03-12T13:34:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Giuseppe5000
body: Guarda l'implementazione e i test commentati alla commit 551742f
created_at: 2025-03-12T13:36:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,5 +75,19 @@ describe("WorkflowRepository", () => {
         });
     });
 
+    describe("deleteWorkflow", () => {
+        it("should return a workflow", async () => {
+            userEntityModelMock.findOneAndUpdate.mockReturnThis();
+            userEntityModelMock.exec.mockResolvedValue(userEntityMock);
+            expect(await workflowRepository.deleteWorkflow("username", "prova")).toEqual(workflowEntityMock);
+        });
+        it("should return null if the workflow doesn't exists", async () => {
path: backend/test/workflow/adapter/output/WorkflowRepository.spec.ts
user: login: Jacky8703
body: questo test più che controllare che un workflow non ci sia, controlla che un utente non ci sia (ora non so se quel $pull fallisce ti ritorna null?), secondo me se vuoi controllare che un workflow non ci sia devi fare fallire il controllo dopo di deletedWorkflow, quindi ritornando uno user che non abbia quel workflow da exec(). E comunque il controllo sullo user lo farei lo stesso perché non si sa mai, alla fine basta chiamare il metodo con uno username inventato e non passa
created_at: 2025-03-12T13:36:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Jacky8703
body: eh ok ma anche come avevi fatto prima non potevi essere sicuro che il delete l'avesse fatto, è quello che dico io, se dici dopo il tuo update provo a mettere un findOne, in questo modo si ha la certezza che il workflow sia stato eliminato
created_at: 2025-03-12T14:00:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Giuseppe5000
body: Beh con updatedCount = 1 penso sia palese che abbia rimosso il workflow.
created_at: 2025-03-12T14:05:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Jacky8703
body: io ho visto questo if (deleteResult.modifiedCount !== 1) return null; ma lo fai sul documento prima di aggiornarlo. Comunque c'è un modo molto più semplice di fare il controllo, basta chiamare prima getWorkflow e dopo ritornare il documento aggiornato così si è sicuri che la delete sia andata a buon fine
created_at: 2025-03-12T14:08:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Jacky8703
body: ora provo ad implementare pure i test, se vanno io pusho così vedi se ti ispira
created_at: 2025-03-12T14:09:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Jacky8703
body: anzi prima vedo se $pull può ritornare null, perché se è così allora si può semplificare tutto
created_at: 2025-03-12T14:17:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,16 @@ export class WorkflowRepository {
         return user.workflows[0];
     }
 
+    async deleteWorkflow(username: string, workflowName: string): Promise<WorkflowEntity | null> {
+        const user = await this.userEntityModel
+            .findOneAndUpdate({ username: username }, { $pull: { workflows: { name: workflowName } } })
path: backend/src/workflow/adapter/output/WorkflowRepository.ts
user: login: Jacky8703
body: niente non funziona, ti ritorna il documento anche se non trova il workflow
created_at: 2025-03-12T14:22:28Z
author_association: CONTRIBUTOR

PR comments:
user: login: Jacky8703
created_at: 2025-03-12T15:13:15Z
body: @Giuseppe5000 ho pushato, vedi se ti può andare bene
author_association: CONTRIBUTOR
user: login: Giuseppe5000
created_at: 2025-03-12T17:14:49Z
body: > @Giuseppe5000 ho pushato, vedi se ti può andare bene

Si direi che va bene
author_association: CONTRIBUTOR