From 966e4d7d1e370cec0eab42f73342d74ec3746c17 Mon Sep 17 00:00:00 2001 From: Takeshi Kimata <117462761+kimatata@users.noreply.github.com> Date: Mon, 11 Mar 2024 20:48:24 +0900 Subject: [PATCH] Add/Insert Step --- backend/index.js | 2 + backend/routes/steps/delete.js | 10 ++- backend/routes/steps/new.js | 63 +++++++++++++++++++ .../[folderId]/cases/[caseId]/page.tsx | 33 +++++++--- .../cases/[caseId]/steps-editor.tsx | 2 +- 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 backend/routes/steps/new.js diff --git a/backend/index.js b/backend/index.js index dcdfcca..b1f14c8 100644 --- a/backend/index.js +++ b/backend/index.js @@ -52,7 +52,9 @@ app.use("/cases", casesNewRoute); app.use("/cases", casesEditRoute); // "/steps" +const stepsNewRoute = require("./routes/steps/new")(sequelize); const stepsDeleteRoute = require("./routes/steps/delete")(sequelize); +app.use("/steps", stepsNewRoute); app.use("/steps", stepsDeleteRoute); // "/runs" diff --git a/backend/routes/steps/delete.js b/backend/routes/steps/delete.js index cad7a33..8749db3 100644 --- a/backend/routes/steps/delete.js +++ b/backend/routes/steps/delete.js @@ -10,11 +10,15 @@ module.exports = function (sequelize) { router.delete("/:stepId", async (req, res) => { const stepId = req.params.stepId; + // TODO The caseId should not be specified from the front end, but should be traced from stepId by association. const caseId = req.query.parentCaseId; + const t = await sequelize.transaction(); + try { const step = await Step.findByPk(stepId); if (!step) { + await t.rollback(); return res.status(404).send("Step not found"); } @@ -23,6 +27,7 @@ module.exports = function (sequelize) { where: { StepId: stepId, }, + transaction: t, }); // Decrease stepNo for all caseSteps with greater than the caseStep to be deleted. @@ -35,14 +40,17 @@ module.exports = function (sequelize) { [Op.gt]: deletingCaseStep.stepNo, }, }, + transaction: t, } ); - await step.destroy(); + await step.destroy({ transaction: t }); + await t.commit(); res.status(204).send(); } catch (error) { console.error(error); + await t.rollback(); res.status(500).send("Internal Server Error"); } }); diff --git a/backend/routes/steps/new.js b/backend/routes/steps/new.js new file mode 100644 index 0000000..5bd9f79 --- /dev/null +++ b/backend/routes/steps/new.js @@ -0,0 +1,63 @@ +const express = require("express"); +const router = express.Router(); +const defineStep = require("../../models/steps"); +const defineCaseStep = require("../../models/caseSteps"); +const { DataTypes, Op } = require("sequelize"); + +module.exports = function (sequelize) { + const Step = defineStep(sequelize, DataTypes); + const CaseStep = defineCaseStep(sequelize, DataTypes); + + router.post("/", async (req, res) => { + const newStepNo = req.query.newStepNo; + const caseId = req.query.parentCaseId; + + const t = await sequelize.transaction(); + + try { + // Update existing stepNo for steps with stepNo greater than or equal to newStepNo + const maxStepNo = await CaseStep.max("stepNo", { + where: { caseId: caseId }, + transaction: t, + }); + if (maxStepNo >= newStepNo) { + await CaseStep.update( + { stepNo: sequelize.literal("stepNo + 1") }, + { + where: { + caseId: caseId, + stepNo: { [Op.gte]: newStepNo }, + }, + transaction: t, + } + ); + } + + const newStep = await Step.create( + { + step: "", + result: "", + }, + { transaction: t } + ); + + await CaseStep.create( + { + caseId: caseId, + stepId: newStep.id, + stepNo: newStepNo, + }, + { transaction: t } + ); + + await t.commit(); + res.json(newStep); + } catch (error) { + console.error(error); + await t.rollback(); + res.status(500).send("Internal Server Error"); + } + }); + + return router; +}; diff --git a/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/page.tsx b/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/page.tsx index c90d693..18b3f94 100644 --- a/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/page.tsx +++ b/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/page.tsx @@ -83,6 +83,30 @@ async function fetchCase(url: string) { } } +/** + * create step + */ +async function fetchCreateStep(newStepNo: number, parentCaseId: number) { + const fetchOptions = { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }; + + const url = `${apiServer}/steps?newStepNo=${newStepNo}&parentCaseId=${parentCaseId}`; + + try { + const response = await fetch(url, fetchOptions); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + } catch (error) { + console.error("Error deleting project:", error); + throw error; + } +} + /** * delete step */ @@ -146,7 +170,7 @@ export default function Page({ const url = `${apiServer}/cases?caseId=${params.caseId}`; const onPlusClick = async (newStepNo: number) => { - console.log(newStepNo); + await fetchCreateStep(newStepNo, params.caseId); }; const onDeleteClick = async (stepId: number) => { @@ -156,19 +180,12 @@ export default function Page({ return; } const deletedStepNo = deletedStep.caseSteps.stepNo; - console.log(deletedStepNo) // delete request await fetchDeleteStep(stepId, params.caseId); - // const updatedSteps = testCase.Steps.filter((step) => step.id !== stepId); - // setTestCase({ - // ...testCase, - // Steps: updatedSteps, - // }); const updatedSteps = testCase.Steps.map(step => { if (step.caseSteps.stepNo > deletedStepNo) { - console.log("bigger", step) return { ...step, caseSteps: { diff --git a/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/steps-editor.tsx b/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/steps-editor.tsx index 3281a3c..4377e3e 100644 --- a/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/steps-editor.tsx +++ b/frontend/app/projects/[projectId]/folders/[folderId]/cases/[caseId]/steps-editor.tsx @@ -68,7 +68,7 @@ export default function StepsEditor({ isIconOnly size="sm" className="bg-transparent rounded-full" - onPress={() => onStepPlus(step.caseSteps.stepNo)} + onPress={() => onStepPlus(step.caseSteps.stepNo + 1)} >