Skip to content

Commit

Permalink
Bugfix: deleting scenario token (#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
ludeknovy authored Mar 8, 2024
1 parent b05579f commit a49c7a6
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -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"),
Expand All @@ -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(
Expand All @@ -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()
Expand All @@ -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(
Expand All @@ -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)
})

})
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

}
20 changes: 16 additions & 4 deletions src/server/queries/scenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
}
}

Expand Down Expand Up @@ -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)`,
Expand Down

0 comments on commit a49c7a6

Please sign in to comment.