diff --git a/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.spec.ts b/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.spec.ts index bb8ced92..03f878ac 100644 --- a/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.spec.ts +++ b/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.spec.ts @@ -4,6 +4,7 @@ import { AllowedRoles } from "../../../middleware/authorization-middleware" import { v4 as uuidv4 } from "uuid" import { deleteScenarioShareTokenController } from "./delete-scenario-share-token-controller" import { db } from "../../../../db/db" +import { StatusCode } from "../../../utils/status-code" jest.mock("../../../../db/db") @@ -16,8 +17,12 @@ const mockResponse = () => { return res } +beforeEach(() => { + jest.clearAllMocks() +}) + describe("deleteScenarioShareTokenController", () => { - it("should be able to delete only my token whe user role is operator", async () => { + it("should be able to delete only my token when user role is operator", async () => { const response = mockResponse() const querySpy = jest.spyOn(require("../../../queries/scenario"), @@ -26,7 +31,7 @@ describe("deleteScenarioShareTokenController", () => { params: { projectName: "project", scenarioName: "scenario", shareTokenId: "my-share-token" }, user: { role: AllowedRoles.Operator, userId: uuidv4() }, }; - (db.oneOrNone as any).mockReturnValueOnce({} ); // dummy token search response + (db.oneOrNone as any).mockReturnValueOnce({}); // dummy token search response (db.none as any).mockReturnValueOnce() await deleteScenarioShareTokenController( @@ -42,6 +47,27 @@ describe("deleteScenarioShareTokenController", () => { request.user.userId) expect(response.send).toHaveBeenCalledTimes(1) }) + it("should return 404 when my token does not exist and user role is operator", async () => { + + const response = mockResponse() + const querySpy = jest.spyOn(require("../../../queries/scenario"), + "deleteMyScenarioShareToken") + const request = { + params: { projectName: "project", scenarioName: "scenario", shareTokenId: "my-share-token" }, + user: { role: AllowedRoles.Operator, userId: uuidv4() }, + }; + (db.oneOrNone as any).mockReturnValueOnce(null); // dummy token search response + (db.none as any).mockReturnValueOnce() + + await deleteScenarioShareTokenController( + request as unknown as IGetUserAuthInfoRequest, + response as unknown as Response) + + expect(querySpy).not.toHaveBeenCalled() + expect(response.send).toHaveBeenCalledTimes(1) + expect(response.status).toHaveBeenCalledWith(StatusCode.NotFound) + }) + it("should be able to delete any token whe user role is admin", async () => { const response = mockResponse() @@ -51,7 +77,7 @@ describe("deleteScenarioShareTokenController", () => { params: { projectName: "project", scenarioName: "scenario", shareTokenId: "my-share-token" }, user: { role: AllowedRoles.Admin, userId: uuidv4() }, }; - (db.oneOrNone as any).mockReturnValueOnce({} ); // dummy token search response + (db.oneOrNone as any).mockReturnValueOnce({}); // dummy token search response (db.none as any).mockReturnValueOnce() await deleteScenarioShareTokenController( @@ -67,5 +93,25 @@ describe("deleteScenarioShareTokenController", () => { ) expect(response.send).toHaveBeenCalledTimes(1) }) + it("should return 404 when share token id does not exist", async () => { + + const response = mockResponse() + const querySpy = jest.spyOn(require("../../../queries/scenario"), + "deleteScenarioShareToken") + const request = { + params: { projectName: "project", scenarioName: "scenario", shareTokenId: "my-share-token" }, + user: { role: AllowedRoles.Admin, userId: uuidv4() }, + }; + (db.oneOrNone as any).mockReturnValueOnce(null); // dummy token search response + (db.none as any).mockReturnValueOnce() + + await deleteScenarioShareTokenController( + request as unknown as IGetUserAuthInfoRequest, + response as unknown as Response) + + expect(querySpy).not.toHaveBeenCalled() + expect(response.send).toHaveBeenCalledTimes(1) + expect(response.status).toHaveBeenCalledWith(StatusCode.NotFound) + }) }) diff --git a/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.ts b/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.ts index 7edfea65..c0639eea 100644 --- a/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.ts +++ b/src/server/controllers/scenario/share-token/delete-scenario-share-token-controller.ts @@ -7,8 +7,9 @@ import { deleteMyScenarioShareToken, deleteScenarioShareToken, findMyScenarioShareToken, - findScenarioShareToken, + findScenarioShareTokenById, } from "../../../queries/scenario" +import { logger } from "../../../../logger" export const deleteScenarioShareTokenController = async (req: IGetUserAuthInfoRequest, res: Response) => { const { user } = req @@ -23,11 +24,12 @@ export const deleteScenarioShareTokenController = async (req: IGetUserAuthInfoRe res.status(StatusCode.NotFound).send() } else { const shareToken = await db.oneOrNone( - findScenarioShareToken(projectName, scenarioName, shareTokenId)) + findScenarioShareTokenById(projectName, scenarioName, shareTokenId)) if (shareToken) { await db.none(deleteScenarioShareToken(projectName, scenarioName, shareTokenId)) return res.status(StatusCode.Ok).send() } + logger.info(`Scenario token ${shareTokenId} not found. Cannot delete it.`) res.status(StatusCode.NotFound).send() } diff --git a/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.spec.ts b/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.spec.ts index 80251053..30d27e0c 100644 --- a/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.spec.ts +++ b/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.spec.ts @@ -30,7 +30,7 @@ describe("getScenarioShareTokenController", () => { expect(querySpy).toHaveBeenCalledTimes(1) expect(querySpy) .toHaveBeenLastCalledWith(request.params.projectName, request.params.scenarioName, request.user.userId) - expect(response.send).toHaveBeenCalledTimes(1) + expect(response.status).toHaveBeenCalledTimes(1) expect(response.json).toHaveBeenNthCalledWith(1, mockData) }) it("should return all tokens when user role is admin", async () => { diff --git a/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.ts b/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.ts index 2dd896cd..50865490 100644 --- a/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.ts +++ b/src/server/controllers/scenario/share-token/get-scenario-share-token-controller.ts @@ -10,8 +10,10 @@ export const getScenarioShareTokenController = async (req: IGetUserAuthInfoReque const { projectName, scenarioName } = req.params if ([AllowedRoles.Operator].includes(role)) { const myApiKeys = await db.manyOrNone(selectOnlyMyScenarioShareTokens(projectName, scenarioName, userId)) - return res.send(StatusCode.Ok).json(myApiKeys) + res.status(StatusCode.Ok).json(myApiKeys) + } else { + const shareTokens = await db.manyOrNone(searchScenarioShareTokens(projectName, scenarioName)) + res.status(StatusCode.Ok).json(shareTokens) } - const shareTokens = await db.manyOrNone(searchScenarioShareTokens(projectName, scenarioName)) - res.status(StatusCode.Ok).json(shareTokens) + } diff --git a/src/server/queries/scenario.ts b/src/server/queries/scenario.ts index 40f4d34c..ee75bbee 100644 --- a/src/server/queries/scenario.ts +++ b/src/server/queries/scenario.ts @@ -283,16 +283,28 @@ export const findScenarioShareToken = (projectName: string, scenarioName: string } } -export const findMyScenarioShareToken = (projectName: string, scenarioName: string, token: string, userId: string) => { +export const findScenarioShareTokenById = (projectName: string, scenarioName: string, id: string) => { return { text: `SELECT t.token FROM jtl.scenario_share_tokens as t LEFT JOIN jtl.scenario as s ON s.id = t.scenario_id LEFT JOIN jtl.projects as p ON p.id = s.project_id WHERE p.project_name = $1 AND s.name = $2 - AND t.token = $3 + AND t.id = $3;`, + values: [projectName, scenarioName, id], + } +} + +export const findMyScenarioShareToken = (projectName: string, scenarioName: string, tokenId: string, userId: string) => { + return { + text: `SELECT t.token FROM jtl.scenario_share_tokens as t + LEFT JOIN jtl.scenario as s ON s.id = t.scenario_id + LEFT JOIN jtl.projects as p ON p.id = s.project_id + WHERE p.project_name = $1 + AND s.name = $2 + AND t.id = $3 AND t.created_by = $4;`, - values: [projectName, scenarioName, token, userId], + values: [projectName, scenarioName, tokenId, userId], } } @@ -349,7 +361,7 @@ export const deleteMyScenarioShareToken = (projectName, scenarioName, id, userId text: `DELETE FROM jtl.scenario_share_tokens as sst USING jtl.scenario as sc WHERE sst.id = $3 - AND sst.created_by = $3 + AND sst.created_by = $4 AND sst.scenario_id = sc.id AND sc.name = $2 AND sc.project_id = (SELECT id FROM jtl.projects WHERE project_name = $1)`,