feat: inplement auth for cases

This commit is contained in:
Takeshi Kimata
2024-06-03 22:51:26 +09:00
parent e4db79047a
commit fe8b82cb4b
14 changed files with 106 additions and 98 deletions

View File

@@ -66,13 +66,11 @@ const casesShowRoute = require('./routes/cases/show')(sequelize);
const casesNewRoute = require('./routes/cases/new')(sequelize);
const casesEditRoute = require('./routes/cases/edit')(sequelize);
const casesDeleteRoute = require('./routes/cases/delete')(sequelize);
const casesBulkDeleteRoute = require('./routes/cases/bulkDelete')(sequelize);
app.use('/cases', casesIndexRoute);
app.use('/cases', casesShowRoute);
app.use('/cases', casesNewRoute);
app.use('/cases', casesEditRoute);
app.use('/cases', casesDeleteRoute);
app.use('/cases', casesBulkDeleteRoute);
// "/steps"
const stepsNewRoute = require('./routes/steps/new')(sequelize);

View File

@@ -1,25 +0,0 @@
const express = require('express');
const router = express.Router();
const defineCase = require('../../models/cases');
const { DataTypes } = require('sequelize');
module.exports = function (sequelize) {
const Case = defineCase(sequelize, DataTypes);
router.post('/bulkdelete', async (req, res) => {
const { caseIds } = req.body;
if (!caseIds || !Array.isArray(caseIds)) {
return res.status(400).send('Invalid caseIds array');
}
try {
await Case.destroy({ where: { id: caseIds } });
res.status(204).send();
} catch (error) {
console.error(error);
res.status(500).send('Internal Server Error');
}
});
return router;
};

View File

@@ -4,16 +4,17 @@ const defineCase = require('../../models/cases');
const { DataTypes } = require('sequelize');
module.exports = function (sequelize) {
const { verifySignedIn, verifyProjectDeveloper } = require('../../middleware/auth')(sequelize);
const Case = defineCase(sequelize, DataTypes);
router.delete('/:caseId', async (req, res) => {
const caseId = req.params.caseId;
try {
const testcase = await Case.findByPk(caseId);
if (!testcase) {
return res.status(404).send('Case not found');
router.post('/bulkdelete', verifySignedIn, verifyProjectDeveloper, async (req, res) => {
const { caseIds } = req.body;
if (!caseIds || !Array.isArray(caseIds)) {
return res.status(400).send('Invalid caseIds array');
}
await testcase.destroy();
try {
await Case.destroy({ where: { id: caseIds } });
res.status(204).send();
} catch (error) {
console.error(error);

View File

@@ -5,10 +5,11 @@ const defineStep = require('../../models/steps');
const { DataTypes } = require('sequelize');
module.exports = function (sequelize) {
const { verifySignedIn, verifyProjectDeveloper } = require('../../middleware/auth')(sequelize);
const Case = defineCase(sequelize, DataTypes);
const Step = defineStep(sequelize, DataTypes);
router.put('/:caseId', async (req, res) => {
router.put('/:caseId', verifySignedIn, verifyProjectDeveloper, async (req, res) => {
const caseId = req.params.caseId;
const updateCase = req.body;
try {

View File

@@ -5,8 +5,9 @@ const { DataTypes } = require('sequelize');
module.exports = function (sequelize) {
const Case = defineCase(sequelize, DataTypes);
const { verifySignedIn, verifyProjectVisible } = require('../../middleware/auth')(sequelize);
router.get('/', async (req, res) => {
router.get('/', verifySignedIn, verifyProjectVisible, async (req, res) => {
const { folderId } = req.query;
if (!folderId) {

View File

@@ -14,9 +14,10 @@ function isEmpty(value) {
}
module.exports = function (sequelize) {
const { verifySignedIn, verifyProjectDeveloper } = require('../../middleware/auth')(sequelize);
const Case = defineCase(sequelize, DataTypes);
router.post('/', async (req, res) => {
router.post('/', verifySignedIn, verifyProjectDeveloper, async (req, res) => {
try {
if (
requiredFields.some((field) => {

View File

@@ -13,8 +13,9 @@ module.exports = function (sequelize) {
Step.belongsToMany(Case, { through: 'caseSteps' });
Case.belongsToMany(Attachment, { through: 'caseAttachments' });
Attachment.belongsToMany(Case, { through: 'caseAttachments' });
const { verifySignedIn, verifyProjectVisible } = require('../../middleware/auth')(sequelize);
router.get('/:caseId', async (req, res) => {
router.get('/:caseId', verifySignedIn, verifyProjectVisible, async (req, res) => {
const caseId = req.params.caseId;
if (!caseId) {

View File

@@ -154,6 +154,8 @@
"actions": "Actions",
"delete_case": "Delete test case",
"delete": "Delete",
"close": "Close",
"are_you_sure": "Are you sure you want to delete test cases?",
"new_test_case": "New Test Case",
"status": "Status",
"critical": "Critical",

View File

@@ -153,6 +153,8 @@
"actions": "アクション",
"delete_case": "テストケースを削除",
"delete": "削除",
"close": "閉じる",
"are_you_sure": "テストケースを削除してもよろしいですか?",
"new_test_case": "新規テストケース",
"status": "ステータス",
"critical": "致",

View File

@@ -1,8 +1,10 @@
'use client';
import { useEffect, useState } from 'react';
import { useState, useEffect, useContext } from 'react';
import { TokenContext } from '@/utils/TokenProvider';
import TestCaseTable from './TestCaseTable';
import { fetchCases, createCase, deleteCase, deleteCases } from './caseControl';
import { CasesMessages } from '@/types/case';
import { fetchCases, createCase, deleteCases } from './caseControl';
import { CaseType, CasesMessages } from '@/types/case';
import DeleteConfirmDialog from '@/components/DeleteConfirmDialog';
type Props = {
projectId: string;
@@ -12,11 +14,18 @@ type Props = {
};
export default function CasesPane({ projectId, folderId, messages, locale }: Props) {
const [cases, setCases] = useState([]);
const [cases, setCases] = useState<CaseType[]>([]);
const context = useContext(TokenContext);
useEffect(() => {
async function fetchDataEffect() {
if (!context.isSignedIn()) {
return;
}
try {
const data = await fetchCases(folderId);
const data = await fetchCases(context.token.access_token, folderId);
console.log(data);
setCases(data);
} catch (error: any) {
console.error('Error in effect:', error.message);
@@ -24,25 +33,39 @@ export default function CasesPane({ projectId, folderId, messages, locale }: Pro
}
fetchDataEffect();
}, []);
}, [context, folderId]);
const handleCreateCase = async (folderId: string) => {
const newCase = await createCase(folderId);
const newCase = await createCase(context.token.access_token, folderId);
const updateCases = [...cases];
updateCases.push(newCase);
setCases(updateCases);
};
const handleDeleteCase = async (caseId: number) => {
await deleteCase(caseId);
const data = await fetchCases(folderId);
setCases(data);
// Delete confirm dialog
const [isDeleteConfirmDialogOpen, setIsDeleteConfirmDialogOpen] = useState(false);
const [deleteCaseIds, setDeleteCaseIds] = useState<number[]>([]);
const closeDeleteConfirmDialog = () => {
setIsDeleteConfirmDialogOpen(false);
setDeleteCaseIds([]);
};
const handleDeleteCases = async (deleteCaseIds: string[]) => {
await deleteCases(deleteCaseIds);
const data = await fetchCases(folderId);
setCases(data);
const onDeleteCases = (deleteCaseIds: number[]) => {
setDeleteCaseIds(deleteCaseIds);
setIsDeleteConfirmDialogOpen(true);
};
const onDeleteCase = async (deleteCaseId: number) => {
setDeleteCaseIds([deleteCaseId]);
setIsDeleteConfirmDialogOpen(true);
};
const onConfirm = async () => {
if (deleteCaseIds.length > 0) {
await deleteCases(context.token.access_token, deleteCaseIds);
setCases(cases.filter((entry) => !deleteCaseIds.includes(entry.id)));
closeDeleteConfirmDialog();
}
};
return (
@@ -51,11 +74,20 @@ export default function CasesPane({ projectId, folderId, messages, locale }: Pro
projectId={projectId}
cases={cases}
onCreateCase={() => handleCreateCase(folderId)}
onDeleteCase={handleDeleteCase}
onDeleteCases={handleDeleteCases}
onDeleteCase={() => onDeleteCase}
onDeleteCases={() => onDeleteCases}
messages={messages}
locale={locale}
/>
<DeleteConfirmDialog
isOpen={isDeleteConfirmDialogOpen}
onCancel={closeDeleteConfirmDialog}
onConfirm={onConfirm}
closeText={messages.close}
confirmText={messages.areYouSure}
deleteText={messages.delete}
/>
</>
);
}

View File

@@ -16,28 +16,12 @@ import {
} from '@nextui-org/react';
import { Link, NextUiLinkClasses } from '@/src/navigation';
import { Plus, MoreVertical, Trash, Circle } from 'lucide-react';
import { CasesMessages } from '@/types/case';
import { CaseType, CasesMessages } from '@/types/case';
import { priorities } from '@/config/selection';
type Case = {
id: number;
title: string;
state: number;
priority: number;
type: number;
automationStatus: number;
description: string;
template: number;
preConditions: string;
expectedResults: string;
folderId: number;
createdAt: string;
updatedAt: string;
};
type Props = {
projectId: string;
cases: Case[];
cases: CaseType[];
onCreateCase: () => void;
onDeleteCase: (caseId: number) => void;
onDeleteCases: (selectedCases: string[]) => void;

View File

@@ -2,7 +2,7 @@ import Config from '@/config/config';
const apiServer = Config.apiServer;
import { CaseType } from '@/types/case';
async function fetchCase(caseId: number) {
async function fetchCase(jwt: string, caseId: number) {
const url = `${apiServer}/cases/${caseId}`;
try {
@@ -10,6 +10,7 @@ async function fetchCase(caseId: number) {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${jwt}`,
},
});
@@ -24,7 +25,7 @@ async function fetchCase(caseId: number) {
}
}
async function fetchCases(folderId: string) {
async function fetchCases(jwt: string, folderId: string) {
const url = `${apiServer}/cases?folderId=${folderId}`;
try {
@@ -32,6 +33,7 @@ async function fetchCases(folderId: string) {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${jwt}`,
},
});
@@ -46,7 +48,7 @@ async function fetchCases(folderId: string) {
}
}
async function createCase(folderId: string) {
async function createCase(jwt: string, folderId: string) {
const newCase = {
title: 'untitled case',
state: 0,
@@ -64,6 +66,7 @@ async function createCase(folderId: string) {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${jwt}`,
},
body: JSON.stringify(newCase),
};
@@ -83,11 +86,12 @@ async function createCase(folderId: string) {
}
}
async function updateCase(updateCaseData: CaseType) {
async function updateCase(jwt: string, updateCaseData: CaseType) {
const fetchOptions = {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${jwt}`,
},
body: JSON.stringify(updateCaseData),
};
@@ -107,34 +111,36 @@ async function updateCase(updateCaseData: CaseType) {
}
}
async function deleteCase(caseId: number) {
const fetchOptions = {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
};
// async function deleteCase(jwt: string, caseId: number) {
// const fetchOptions = {
// method: 'DELETE',
// headers: {
// 'Content-Type': 'application/json',
// Authorization: `Bearer ${jwt}`,
// },
// };
const url = `${apiServer}/cases/${caseId}`;
// const url = `${apiServer}/cases/${caseId}`;
try {
const response = await fetch(url, fetchOptions);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
} catch (error: any) {
console.error('Error deleting case:', error);
throw error;
}
}
// try {
// const response = await fetch(url, fetchOptions);
// if (!response.ok) {
// throw new Error(`HTTP error! Status: ${response.status}`);
// }
// } catch (error: any) {
// console.error('Error deleting case:', error);
// throw error;
// }
// }
async function deleteCases(deleteCases: string[]) {
async function deleteCases(jwt: string, deleteCaseIds: number[]) {
const fetchOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${jwt}`,
},
body: JSON.stringify({ caseIds: deleteCases }),
body: JSON.stringify({ caseIds: deleteCaseIds }),
};
const url = `${apiServer}/cases/bulkdelete`;
@@ -150,4 +156,4 @@ async function deleteCases(deleteCases: string[]) {
}
}
export { fetchCase, fetchCases, updateCase, createCase, deleteCase, deleteCases };
export { fetchCase, fetchCases, updateCase, createCase, deleteCases };

View File

@@ -11,6 +11,8 @@ export default function Page({ params }: { params: { projectId: string; folderId
actions: t('actions'),
deleteCase: t('delete_case'),
delete: t('delete'),
close: t('close'),
areYouSure: t('are_you_sure'),
newTestCase: t('new_test_case'),
status: t('status'),
critical: t('critical'),

View File

@@ -66,6 +66,8 @@ export type CasesMessages = {
priority: string;
actions: string;
deleteCase: string;
close: string;
areYouSure: string;
delete: string;
newTestCase: string;
status: string;