diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b08ddceb..4c957dc034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Navigation on notifications page now works as expected [#272](https://github.com/openkfw/TruBudget/pull/272) - The link to the project/subproject is now active when the user has permissions to see it [#284](https://github.com/openkfw/TruBudget/pull/284) - The link to the project/subproject in fly-in notifications correctly redirects the user [#284](https://github.com/openkfw/TruBudget/pull/284) +- When a workflow item is assigned, the new assignee gets notified [#272](https://github.com/openkfw/TruBudget/pull/272) diff --git a/api/src/service/domain/workflow/project_projected_budget_delete.spec.ts b/api/src/service/domain/workflow/project_projected_budget_delete.spec.ts index e2c9ed7762..927961adc2 100644 --- a/api/src/service/domain/workflow/project_projected_budget_delete.spec.ts +++ b/api/src/service/domain/workflow/project_projected_budget_delete.spec.ts @@ -55,20 +55,31 @@ describe("Delete project projected budget: permissions", () => { }); it("The root user doesn't need permission to delete a projected budget", async () => { - const result = await deleteProjectedBudget(ctx, root, projectId, "Othertestcorp", "EUR", { - ...baseRepository, - getProject: async () => ({ - ...baseProject, - permissions: {}, - projectedBudgets: [ - { - organization: "Testcorp", - value: "10000", - currencyCode: "EUR", - }, - ], - }), - }); + const projectedBudget = { + organization: "Testcorp", + currencyCode: "EUR", + }; + const result = await deleteProjectedBudget( + ctx, + root, + projectId, + projectedBudget.organization, + projectedBudget.currencyCode, + { + ...baseRepository, + getProject: async () => ({ + ...baseProject, + permissions: {}, + projectedBudgets: [ + { + organization: projectedBudget.organization, + currencyCode: projectedBudget.currencyCode, + value: "10000", + }, + ], + }), + }, + ); // No errors, despite the missing permissions: assert.isTrue(Result.isOk(result), (result as Error).message); @@ -85,17 +96,69 @@ describe("Delete project projected budget: permissions", () => { assert.instanceOf(result, NotFound); }); - // Test can be activated if #290 is fixed or deleted otherwise - // it("Deleting a projected budget fails if the projected budget cannot be found.", async () => { - // const result = await deleteProjectedBudget(ctx, alice, projectId, "Somecorp", "EUR", { - // ...baseRepository, - // getProject: async () => ({ ...baseProject }), - // }); - - // // NotFound error as the project cannot be fetched: - // assert.isTrue(Result.isErr(result)); - // // assert.instanceOf(result, NotFound); - // }); + it("Deleting a projected budget does nothing if the projected budget cannot be found.", async () => { + const result = await deleteProjectedBudget(ctx, alice, projectId, "Somecorp", "EUR", { + ...baseRepository, + getProject: async () => ({ + ...baseProject, + projectedBudgets: [ + { + organization: "Testcorp", + currencyCode: "USD", + value: "10000", + }, + ], + }), + }); + + // NotFound error as the project cannot be fetched: + assert.isTrue(Result.isOk(result)); + }); +}); + +describe("Delete project projected budget: deletion", () => { + it("The projected budget is not available after deletion", async () => { + const projectedBudget = { + organization: "Testcorp", + currencyCode: "EUR", + }; + const projectedBudgetToDelete = { + organization: "Otherestcorp", + currencyCode: "EUR", + }; + const result = await deleteProjectedBudget( + ctx, + alice, + projectId, + projectedBudgetToDelete.organization, + projectedBudgetToDelete.currencyCode, + { + ...baseRepository, + getProject: async () => ({ + ...baseProject, + projectedBudgets: [ + { + organization: projectedBudget.organization, + value: "10000", + currencyCode: projectedBudget.currencyCode, + }, + { + organization: projectedBudgetToDelete.organization, + value: "10000", + currencyCode: projectedBudgetToDelete.currencyCode, + }, + ], + }), + }, + ); + + assert.isTrue(Result.isOk(result)); + if (Result.isErr(result)) { + throw result; + } + const { projectedBudgets } = result; + assert.isTrue(projectedBudgets.length === 1); + }); }); describe("Deleting Projected Budgets: notifications", () => { diff --git a/api/src/service/domain/workflow/project_projected_budget_update.spec.ts b/api/src/service/domain/workflow/project_projected_budget_update.spec.ts index 306cfb3355..b1594e3d0f 100644 --- a/api/src/service/domain/workflow/project_projected_budget_update.spec.ts +++ b/api/src/service/domain/workflow/project_projected_budget_update.spec.ts @@ -8,7 +8,6 @@ import { NotFound } from "../errors/not_found"; import { ServiceUser } from "../organization/service_user"; import { Project } from "./project"; import { updateProjectedBudget } from "./project_projected_budget_update"; -import { Subproject } from "./subproject"; const ctx: Ctx = { requestId: "", source: "test" }; const root: ServiceUser = { id: "root", groups: [] }; @@ -102,6 +101,45 @@ describe("Update project projected budget: permissions", () => { }); }); +describe("Update project projected budget: updating", () => { + it("The projected budget is updated", async () => { + const projectedBudgetToUpdate = { + organization: "Otherestcorp", + value: "10000", + currencyCode: "EUR", + }; + const updatedValue = "9999"; + const result = await updateProjectedBudget( + ctx, + alice, + projectId, + projectedBudgetToUpdate.organization, + updatedValue, + projectedBudgetToUpdate.currencyCode, + { + ...baseRepository, + getProject: async () => ({ + ...baseProject, + projectedBudgets: [ + { + organization: projectedBudgetToUpdate.organization, + value: projectedBudgetToUpdate.value, + currencyCode: projectedBudgetToUpdate.currencyCode, + }, + ], + }), + }, + ); + + assert.isTrue(Result.isOk(result)); + if (Result.isErr(result)) { + throw result; + } + const { projectedBudgets } = result; + assert.equal(projectedBudgets[0].value, updatedValue); + }); +}); + describe("Update Projected Budgets: notifications", () => { it("When a user updates a projected budget, a notification is issued to the assignee.", async () => { const result = await updateProjectedBudget(ctx, alice, projectId, "Testcorp", "9999", "EUR", { diff --git a/api/src/service/domain/workflow/subproject_projected_budget_delete.spec.ts b/api/src/service/domain/workflow/subproject_projected_budget_delete.spec.ts index c02747506b..976d2189ec 100644 --- a/api/src/service/domain/workflow/subproject_projected_budget_delete.spec.ts +++ b/api/src/service/domain/workflow/subproject_projected_budget_delete.spec.ts @@ -42,33 +42,37 @@ const baseRepository = { }, }; -describe("Update subproject projected budget: permissions", () => { - it("Without the subproject.budget.updateProjected permission, a user cannot update a projected budget.", async () => { - const result = await deleteProjectedBudget( - ctx, - alice, - projectId, - subprojectId, - "Othertestcorp", - "EUR", - { - ...baseRepository, - getSubproject: async () => ({ ...baseSubproject, permissions: {} }), - }, - ); +describe("Delete subproject projected budget: permissions", () => { + it( + "Without the subproject.budget.deleteProjected permission," + + " a user cannot delete a projected budget.", + async () => { + const result = await deleteProjectedBudget( + ctx, + alice, + projectId, + subprojectId, + "Testcorp", + "EUR", + { + ...baseRepository, + getSubproject: async () => ({ ...baseSubproject, permissions: {} }), + }, + ); - // NotAuthorized error due to the missing permissions: - assert.isTrue(Result.isErr(result)); - assert.instanceOf(result, NotAuthorized); - }); + // NotAuthorized error due to the missing permissions: + assert.isTrue(Result.isErr(result)); + assert.instanceOf(result, NotAuthorized); + }, + ); - it("The root user doesn't need permission to update a projected budget", async () => { + it("The root user doesn't need permission to delete a projected budget", async () => { const result = await deleteProjectedBudget( ctx, root, projectId, subprojectId, - "Othertestcorp", + "Testcorp", "EUR", { ...baseRepository, @@ -90,13 +94,13 @@ describe("Update subproject projected budget: permissions", () => { assert.isTrue(Result.isOk(result), (result as Error).message); }); - it("Updating a projected budget fails if the subproject cannot be found.", async () => { + it("Deleting a projected budget fails if the subproject cannot be found.", async () => { const result = await deleteProjectedBudget( ctx, alice, projectId, subprojectId, - "Othercorp", + "Testcorp", "EUR", { ...baseRepository, @@ -108,10 +112,89 @@ describe("Update subproject projected budget: permissions", () => { assert.isTrue(Result.isErr(result)); assert.instanceOf(result, NotFound); }); + + it("Deleting a projected budget does nothing if the projected budget cannot be found.", async () => { + const projectedBudget = { + organization: "Testcorp", + currencyCode: "USD", + value: "10000", + }; + + const projectedBudgetToBeDeleted = { + organization: "Othercorp", + currencyCode: "EUR", + value: "9999", + }; + + const result = await deleteProjectedBudget( + ctx, + alice, + projectId, + subprojectId, + projectedBudgetToBeDeleted.organization, + projectedBudgetToBeDeleted.currencyCode, + { + ...baseRepository, + getSubproject: async () => ({ + ...baseSubproject, + projectedBudgets: [projectedBudget], + }), + }, + ); + + // NotFound error as the project cannot be fetched: + assert.isTrue(Result.isOk(result)); + }); +}); + +describe("Delete subproject projected budget: deletion", () => { + it("The projected budget is not available after deletion", async () => { + const projectedBudget = { + organization: "Testcorp", + currencyCode: "EUR", + }; + const projectedBudgetToDelete = { + organization: "Otherestcorp", + currencyCode: "EUR", + }; + const result = await deleteProjectedBudget( + ctx, + alice, + projectId, + subprojectId, + projectedBudgetToDelete.organization, + projectedBudgetToDelete.currencyCode, + { + ...baseRepository, + getSubproject: async () => ({ + ...baseSubproject, + projectedBudgets: [ + { + organization: projectedBudget.organization, + value: "10000", + currencyCode: projectedBudget.currencyCode, + }, + { + organization: projectedBudgetToDelete.organization, + value: "10000", + currencyCode: projectedBudgetToDelete.currencyCode, + }, + ], + }), + }, + ); + + assert.isTrue(Result.isOk(result)); + if (Result.isErr(result)) { + throw result; + } + const { projectedBudgets } = result; + assert.isTrue(projectedBudgets.length === 1); + }); }); -describe("Update Projected Budgets: notifications", () => { - it("When a user updates a projected budget, a notification is issued to the assignee.", async () => { +describe("Delete Projected Budgets: notifications", () => { + it("When a user deletes a projected budget, a notification is issued to the assignee.", async () => { const result = await deleteProjectedBudget( ctx, alice, @@ -149,14 +232,18 @@ describe("Update Projected Budgets: notifications", () => { ); }); - it("If there is no assignee when updating a projected budget, no notifications are issued.", async () => { + it("If there is no assignee when deleting a projected budget, no notifications are issued.", async () => { + const projectedBudget = { + organization: "Testcorp", + currencyCode: "EUR", + }; const result = await deleteProjectedBudget( ctx, alice, projectId, subprojectId, - "Testcorp", - "EUR", + projectedBudget.organization, + projectedBudget.currencyCode, { ...baseRepository, getSubproject: async () => ({ @@ -165,8 +252,8 @@ describe("Update Projected Budgets: notifications", () => { assignee: undefined, projectedBudgets: [ { - organization: "Testcorp", - value: "10000", + organization: projectedBudget.organization, + value: projectedBudget.currencyCode, currencyCode: "EUR", }, ], @@ -186,7 +273,7 @@ describe("Update Projected Budgets: notifications", () => { }); it( - "If the user that updates a projected budget is assigned " + + "If the user that deletes a projected budget is assigned " + "to the subproject herself,no notifications are issued.", async () => { const result = await deleteProjectedBudget( @@ -226,7 +313,7 @@ describe("Update Projected Budgets: notifications", () => { ); it( - "If a subproject is assigned to a group when updating a projected budget, " + + "If a subproject is assigned to a group when deleting a projected budget, " + "each member, except for the user that updates it, receives a notificaton.", async () => { const group = "alice_and_bob_and_charlie"; diff --git a/api/src/service/domain/workflow/subproject_projected_budget_update.spec.ts b/api/src/service/domain/workflow/subproject_projected_budget_update.spec.ts index 4fea64189b..fcae0e7841 100644 --- a/api/src/service/domain/workflow/subproject_projected_budget_update.spec.ts +++ b/api/src/service/domain/workflow/subproject_projected_budget_update.spec.ts @@ -113,6 +113,46 @@ describe("Update subproject projected budget: permissions", () => { }); }); +describe("Update subproject projected budget: updating", () => { + it("The projected budget is updated", async () => { + const projectedBudgetToUpdate = { + organization: "Otherestcorp", + value: "10000", + currencyCode: "EUR", + }; + const updatedValue = "9999"; + const result = await updateProjectedBudget( + ctx, + alice, + projectId, + subprojectId, + projectedBudgetToUpdate.organization, + updatedValue, + projectedBudgetToUpdate.currencyCode, + { + ...baseRepository, + getSubproject: async () => ({ + ...baseSubproject, + projectedBudgets: [ + { + organization: projectedBudgetToUpdate.organization, + value: projectedBudgetToUpdate.value, + currencyCode: projectedBudgetToUpdate.currencyCode, + }, + ], + }), + }, + ); + + assert.isTrue(Result.isOk(result)); + if (Result.isErr(result)) { + throw result; + } + const { projectedBudgets } = result; + assert.equal(projectedBudgets[0].value, updatedValue); + }); +}); + describe("Update Projected Budgets: notifications", () => { it("When a user updates a projected budget, a notification is issued to the assignee.", async () => { const result = await updateProjectedBudget( diff --git a/api/src/service/domain/workflow/workflowitem_assign.spec.ts b/api/src/service/domain/workflow/workflowitem_assign.spec.ts new file mode 100644 index 0000000000..08bbefc06d --- /dev/null +++ b/api/src/service/domain/workflow/workflowitem_assign.spec.ts @@ -0,0 +1,349 @@ +import { assert } from "chai"; + +import { Ctx } from "../../../lib/ctx"; +import * as Result from "../../../result"; +import { BusinessEvent } from "../business_event"; +import { NotAuthorized } from "../errors/not_authorized"; +import { NotFound } from "../errors/not_found"; +import { ServiceUser } from "../organization/service_user"; +import { Workflowitem } from "./workflowitem"; +import { assignWorkflowitem } from "./workflowitem_assign"; + +const ctx: Ctx = { requestId: "", source: "test" }; +const root: ServiceUser = { id: "root", groups: [] }; +const alice: ServiceUser = { id: "alice", groups: ["alice_and_bob", "alice_and_bob_and_charlie"] }; +const bob: ServiceUser = { id: "bob", groups: ["alice_and_bob", "alice_and_bob_and_charlie"] }; +const charlie: ServiceUser = { id: "charlie", groups: ["alice_and_bob_and_charlie"] }; +const projectId = "dummy-project"; +const subprojectId = "dummy-subproject"; +const workflowitemId = "dummy"; +const baseWorkflowitem: Workflowitem = { + isRedacted: false, + id: workflowitemId, + subprojectId, + createdAt: new Date().toISOString(), + status: "open", + displayName: "dummy", + description: "dummy", + amountType: "N/A", + documents: [], + permissions: { "workflowitem.assign": [alice, bob, charlie].map(x => x.id) }, + log: [], + additionalData: {}, +}; + +describe("assign workflowitem: authorization", () => { + it("Without the workflowitem.assign permission, a user cannot change a workflowitem's assignee.", async () => { + const assigner = alice; + const assignee = bob; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem, permissions: {} }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + if (identity === "bob") return ["bob"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + // NotAuthorized error due to the missing permissions: + assert.isTrue(Result.isErr(result), "The request returns an error"); + assert.instanceOf(result, NotAuthorized, "The error is due to missing authorization"); + }); + + it("The root user doesn't need permission to change a workflowitem's assignee.", async () => { + const assigner = root; + const assignee = bob; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem, permissions: {} }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + if (identity === "bob") return ["bob"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + // No errors, despite the missing permissions: + assert.isTrue(Result.isOk(result), (result as Error).message); + }); +}); + +describe("assign workflowitem: preconditions", () => { + it("A user can assign a workflowitem to herself.", async () => { + const assigner = alice; + const assignee = alice; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + assert.isTrue(Result.isOk(result), (result as Error).message); + }); + + it("A user can assign a workflowitem to someone else.", async () => { + const assigner = alice; + const assignee = bob; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + if (identity === "bob") return ["bob"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + assert.isTrue(Result.isOk(result), (result as Error).message); + }); + + it("Assigning an already assigned user works (but is a no-op).", async () => { + const assigner = alice; + const assignee = alice; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem, assignee: alice.id }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + if (identity === "bob") return ["bob"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + assert.isTrue(Result.isOk(result), (result as Error).message); + }); + + it("A user can assign a workflowitem to a group.", async () => { + const assigner = alice; + const assignedGroup = "alice_and_bob"; + const result = await assignWorkflowitem( + ctx, + assigner, + assignedGroup, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem, assignee: alice.id }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + if (identity === "bob") return ["bob"]; + if (identity === "alice_and_bob") return ["alice", "bob"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + assert.isTrue(Result.isOk(result), (result as Error).message); + }); + + it("Assigning a user fails if the workflowitem cannot be found.", async () => { + const assigner = alice; + const assignee = alice; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => new Error("NotFound"), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + // NotFound error as the workflow item cannot be fetched: + assert.isTrue(Result.isErr(result)); + assert.instanceOf(result, NotFound); + }); + + it("The assignee must not be empty.", async () => { + const assigner = alice; + const assignee = ""; // <- not a valid user ID + const result = await assignWorkflowitem( + ctx, + assigner, + assignee, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + // InvalidCommand error as the user ID is not valid: + assert.isTrue(Result.isErr(result), "The result returns an error"); + // Make TypeScript happy: + if (Result.isOk(result)) { + throw result; + } + + assert.match(result.message, /assignee.*\s+.*empty/); + }); +}); + +describe("assign workflowitem: notifications", () => { + it("When a user assigns a workflowitem to someone else, a notification is issued to the new assignee.", async () => { + const assigner = alice; + const assignee = bob; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + if (identity === "bob") return ["bob"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + // A notification has been issued to the assignee: + assert.isTrue(Result.isOk(result), (result as Error).message); + // Make TypeScript happy: + if (Result.isErr(result)) { + throw result; + } + const { newEvents } = result; + assert.isTrue( + newEvents.some( + event => event.type === "notification_created" && event.recipient === assignee.id, + ), + "A new notification has been created", + ); + }); + + it("When a user assigns a workflowitem to herself, no notifications are issued.", async () => { + const assigner = alice; + const assignee = alice; + const result = await assignWorkflowitem( + ctx, + assigner, + assignee.id, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + + // There is an event representing the assignment, but no notification: + assert.isTrue(Result.isOk(result), (result as Error).message); + // Make TypeScript happy: + if (Result.isErr(result)) { + throw result; + } + const { newEvents } = result; + assert.isTrue(newEvents.length > 0); + assert.isFalse( + newEvents.some(event => event.type === "notification_created"), + "No notification has been issued", + ); + }); + + it( + "If a workflowitem gets assigned to a group, " + + "each member, except for the assigner, receives a notificaton.", + async () => { + const assigner = alice; + const assignedGroup = "alice_and_bob_and_charlie"; + const result = await assignWorkflowitem( + ctx, + assigner, + assignedGroup, + projectId, + subprojectId, + workflowitemId, + { + getWorkflowitem: async () => ({ ...baseWorkflowitem }), + getUsersForIdentity: async identity => { + if (identity === "alice") return ["alice"]; + if (identity === "bob") return ["bob"]; + if (identity === "alice_and_bob_and_charlie") return ["alice", "bob", "charlie"]; + throw Error(`unexpected identity: ${identity}`); + }, + }, + ); + assert.isTrue(Result.isOk(result), (result as Error).message); + // Make TypeScript happy: + if (Result.isErr(result)) { + throw result; + } + const { newEvents } = result; + + // A notification has been issued to both Bob and Charlie, but not to Alice, as she + // is the user who has changed the workflowitem's assignee: + function isNotificationFor(userId: string): (e: BusinessEvent) => boolean { + return event => event.type === "notification_created" && event.recipient === userId; + } + + assert.isFalse( + newEvents.some(isNotificationFor("alice")), + "User 'alice' does not receive a notification", + ); + assert.isTrue(newEvents.some(isNotificationFor("bob")), "User 'bob' receives a notification"); + assert.isTrue( + newEvents.some(isNotificationFor("charlie")), + "User 'charlie' receives a notification", + ); + }, + ); +}); diff --git a/api/src/service/domain/workflow/workflowitem_assign.ts b/api/src/service/domain/workflow/workflowitem_assign.ts index 69a3c2593e..4ab8cf6339 100644 --- a/api/src/service/domain/workflow/workflowitem_assign.ts +++ b/api/src/service/domain/workflow/workflowitem_assign.ts @@ -32,10 +32,7 @@ export async function assignWorkflowitem( ): Promise> { const workflowitem = await repository.getWorkflowitem(workflowitemId); if (Result.isErr(workflowitem)) { - return new VError( - new NotFound(ctx, "workflowitem", workflowitemId), - `Couldn't assign ${assignee} to workflowitem ${workflowitemId}. Workflowitem not found!`, - ); + return new NotFound(ctx, "workflowitem", workflowitemId); } if (assignee === workflowitem.assignee) { diff --git a/api/src/service/domain/workflow/workflowitem_assigned.ts b/api/src/service/domain/workflow/workflowitem_assigned.ts index 8adffa34c5..060ecaac91 100644 --- a/api/src/service/domain/workflow/workflowitem_assigned.ts +++ b/api/src/service/domain/workflow/workflowitem_assigned.ts @@ -44,7 +44,7 @@ export function createEvent( workflowitemId: Workflowitem.Id, assignee: Identity, time: string = new Date().toISOString(), -): Event { +): Result.Type { const event = { type: eventType, source, @@ -58,7 +58,7 @@ export function createEvent( const validationResult = validate(event); if (Result.isErr(validationResult)) { - throw new VError(validationResult, `not a valid ${eventType} event`); + return new VError(validationResult, `not a valid ${eventType} event`); } return event; }