feat: inplement auth for cases
This commit is contained in:
@@ -5,6 +5,7 @@ const defineUser = require('../models/users');
|
|||||||
const defineMember = require('../models/members');
|
const defineMember = require('../models/members');
|
||||||
const defineProject = require('../models/projects');
|
const defineProject = require('../models/projects');
|
||||||
const defineFolder = require('../models/folders');
|
const defineFolder = require('../models/folders');
|
||||||
|
const defineCase = require('../models/cases');
|
||||||
|
|
||||||
function authMiddleware(sequelize) {
|
function authMiddleware(sequelize) {
|
||||||
/**
|
/**
|
||||||
@@ -56,12 +57,24 @@ function authMiddleware(sequelize) {
|
|||||||
*/
|
*/
|
||||||
async function verifyProjectVisible(req, res, next) {
|
async function verifyProjectVisible(req, res, next) {
|
||||||
const Project = defineProject(sequelize, DataTypes);
|
const Project = defineProject(sequelize, DataTypes);
|
||||||
|
const Folder = defineFolder(sequelize, DataTypes);
|
||||||
const Member = defineMember(sequelize, DataTypes);
|
const Member = defineMember(sequelize, DataTypes);
|
||||||
Project.hasMany(Member, { foreignKey: 'projectId' });
|
Project.hasMany(Member, { foreignKey: 'projectId' });
|
||||||
|
|
||||||
const projectId = req.params.projectId || req.query.projectId;
|
let projectId = req.params.projectId || req.query.projectId;
|
||||||
|
const folderId = req.params.folderId || req.query.folderId;
|
||||||
|
if (!projectId && !folderId) {
|
||||||
|
return res.status(400).json({ error: 'projectId or folderId is required' });
|
||||||
|
}
|
||||||
|
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
return res.status(400).json({ error: 'projectId is required' });
|
// find project id from folderId
|
||||||
|
const folder = await Folder.findByPk(folderId);
|
||||||
|
if (folder && folder.projectId) {
|
||||||
|
projectId = folder.projectId;
|
||||||
|
} else {
|
||||||
|
return res.status(404).send('failed to find project from folderId');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const project = await Project.findOne({
|
const project = await Project.findOne({
|
||||||
@@ -173,22 +186,40 @@ function authMiddleware(sequelize) {
|
|||||||
async function verifyProjectDeveloper(req, res, next) {
|
async function verifyProjectDeveloper(req, res, next) {
|
||||||
const Project = defineProject(sequelize, DataTypes);
|
const Project = defineProject(sequelize, DataTypes);
|
||||||
const Folder = defineFolder(sequelize, DataTypes);
|
const Folder = defineFolder(sequelize, DataTypes);
|
||||||
|
const Case = defineCase(sequelize, DataTypes);
|
||||||
const Member = defineMember(sequelize, DataTypes);
|
const Member = defineMember(sequelize, DataTypes);
|
||||||
Project.hasMany(Member, { foreignKey: 'projectId' });
|
Project.hasMany(Member, { foreignKey: 'projectId' });
|
||||||
|
Folder.hasMany(Case, { foreignKey: 'folderId' });
|
||||||
|
|
||||||
let projectId = req.params.projectId || req.query.projectId;
|
let projectId = req.params.projectId || req.query.projectId;
|
||||||
const folderId = req.params.folderId || req.query.folderId;
|
const folderId = req.params.folderId || req.query.folderId;
|
||||||
if (!projectId && !folderId) {
|
const caseId = req.params.caseId || req.query.caseId;
|
||||||
return res.status(400).json({ error: 'projectId or folderId is required' });
|
if (!projectId && !folderId && !caseId) {
|
||||||
|
return res.status(400).json({ error: 'projectId, folderId or caseId is required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
// find project id from folderId
|
if (folderId) {
|
||||||
const folder = await Folder.findByPk(folderId);
|
// find project id from folderId
|
||||||
if (folder && folder.projectId) {
|
const folder = await Folder.findByPk(folderId);
|
||||||
projectId = folder.projectId;
|
if (folder && folder.projectId) {
|
||||||
|
projectId = folder.projectId;
|
||||||
|
} else {
|
||||||
|
return res.status(404).send('failed to find project from folderId');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (caseId) {
|
||||||
|
// find project id from caseId
|
||||||
|
const testCase = await Case.findByPk(caseId, {
|
||||||
|
include: {
|
||||||
|
model: Folder,
|
||||||
|
include: Project,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (testCase && testCase.Folder && testCase.Folder.Project) {
|
||||||
|
projectId = testCase.Folder.Project.id;
|
||||||
} else {
|
} else {
|
||||||
return res.status(404).send('failed to find project from folderId');
|
return res.status(404).send('Failed to find project from caseId');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const router = express.Router();
|
|||||||
const defineCase = require('../../models/cases');
|
const defineCase = require('../../models/cases');
|
||||||
const { DataTypes } = require('sequelize');
|
const { DataTypes } = require('sequelize');
|
||||||
|
|
||||||
const requiredFields = ['title', 'state', 'priority', 'type', 'automationStatus', 'template', 'folderId'];
|
const requiredFields = ['title', 'state', 'priority', 'type', 'automationStatus', 'template'];
|
||||||
|
|
||||||
function isEmpty(value) {
|
function isEmpty(value) {
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
@@ -18,6 +18,8 @@ module.exports = function (sequelize) {
|
|||||||
const Case = defineCase(sequelize, DataTypes);
|
const Case = defineCase(sequelize, DataTypes);
|
||||||
|
|
||||||
router.post('/', verifySignedIn, verifyProjectDeveloper, async (req, res) => {
|
router.post('/', verifySignedIn, verifyProjectDeveloper, async (req, res) => {
|
||||||
|
const folderId = req.query.folderId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
requiredFields.some((field) => {
|
requiredFields.some((field) => {
|
||||||
@@ -25,22 +27,12 @@ module.exports = function (sequelize) {
|
|||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
error: 'Title, state, priority, type, automationStatus, template, and folderId are required',
|
error: 'Title, state, priority, type, automationStatus, and template are required',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { title, state, priority, type, automationStatus, description, template, preConditions, expectedResults } =
|
||||||
title,
|
req.body;
|
||||||
state,
|
|
||||||
priority,
|
|
||||||
type,
|
|
||||||
automationStatus,
|
|
||||||
description,
|
|
||||||
template,
|
|
||||||
preConditions,
|
|
||||||
expectedResults,
|
|
||||||
folderId,
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
const newCase = await Case.create({
|
const newCase = await Case.create({
|
||||||
title,
|
title,
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ export default function CasesPane({ projectId, folderId, messages, locale }: Pro
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const data = await fetchCases(context.token.access_token, folderId);
|
const data = await fetchCases(context.token.access_token, folderId);
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
setCases(data);
|
setCases(data);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error in effect:', error.message);
|
console.error('Error in effect:', error.message);
|
||||||
@@ -50,19 +48,19 @@ export default function CasesPane({ projectId, folderId, messages, locale }: Pro
|
|||||||
setDeleteCaseIds([]);
|
setDeleteCaseIds([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteCases = (deleteCaseIds: number[]) => {
|
|
||||||
setDeleteCaseIds(deleteCaseIds);
|
|
||||||
setIsDeleteConfirmDialogOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteCase = async (deleteCaseId: number) => {
|
const onDeleteCase = async (deleteCaseId: number) => {
|
||||||
setDeleteCaseIds([deleteCaseId]);
|
setDeleteCaseIds([deleteCaseId]);
|
||||||
setIsDeleteConfirmDialogOpen(true);
|
setIsDeleteConfirmDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onDeleteCases = (deleteCaseIds: number[]) => {
|
||||||
|
setDeleteCaseIds(deleteCaseIds);
|
||||||
|
setIsDeleteConfirmDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const onConfirm = async () => {
|
const onConfirm = async () => {
|
||||||
if (deleteCaseIds.length > 0) {
|
if (deleteCaseIds.length > 0) {
|
||||||
await deleteCases(context.token.access_token, deleteCaseIds);
|
await deleteCases(context.token.access_token, deleteCaseIds, projectId);
|
||||||
setCases(cases.filter((entry) => !deleteCaseIds.includes(entry.id)));
|
setCases(cases.filter((entry) => !deleteCaseIds.includes(entry.id)));
|
||||||
closeDeleteConfirmDialog();
|
closeDeleteConfirmDialog();
|
||||||
}
|
}
|
||||||
@@ -74,8 +72,8 @@ export default function CasesPane({ projectId, folderId, messages, locale }: Pro
|
|||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
cases={cases}
|
cases={cases}
|
||||||
onCreateCase={() => handleCreateCase(folderId)}
|
onCreateCase={() => handleCreateCase(folderId)}
|
||||||
onDeleteCase={() => onDeleteCase}
|
onDeleteCase={onDeleteCase}
|
||||||
onDeleteCases={() => onDeleteCases}
|
onDeleteCases={onDeleteCases}
|
||||||
messages={messages}
|
messages={messages}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ type Props = {
|
|||||||
cases: CaseType[];
|
cases: CaseType[];
|
||||||
onCreateCase: () => void;
|
onCreateCase: () => void;
|
||||||
onDeleteCase: (caseId: number) => void;
|
onDeleteCase: (caseId: number) => void;
|
||||||
onDeleteCases: (selectedCases: string[]) => void;
|
onDeleteCases: (caseIds: number[]) => void;
|
||||||
messages: CasesMessages;
|
messages: CasesMessages;
|
||||||
locale: string;
|
locale: string;
|
||||||
};
|
};
|
||||||
@@ -61,6 +61,10 @@ export default function TestCaseTable({
|
|||||||
});
|
});
|
||||||
}, [sortDescriptor, cases]);
|
}, [sortDescriptor, cases]);
|
||||||
|
|
||||||
|
const handleDeleteCase = (deleteCaseId: number) => {
|
||||||
|
onDeleteCase(deleteCaseId);
|
||||||
|
};
|
||||||
|
|
||||||
const renderCell = useCallback((testCase: Case, columnKey: Key) => {
|
const renderCell = useCallback((testCase: Case, columnKey: Key) => {
|
||||||
const cellValue = testCase[columnKey as keyof Case];
|
const cellValue = testCase[columnKey as keyof Case];
|
||||||
|
|
||||||
@@ -95,7 +99,7 @@ export default function TestCaseTable({
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
<DropdownMenu aria-label="test case actions">
|
<DropdownMenu aria-label="test case actions">
|
||||||
<DropdownItem className="text-danger" onClick={() => onDeleteCase(testCase.id)}>
|
<DropdownItem className="text-danger" onPress={() => handleDeleteCase(testCase.id)}>
|
||||||
{messages.deleteCase}
|
{messages.deleteCase}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -106,6 +110,17 @@ export default function TestCaseTable({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleDeleteCases = () => {
|
||||||
|
let deleteCaseIds: number[];
|
||||||
|
if (selectedKeys === 'all') {
|
||||||
|
deleteCaseIds = sortedItems.map((item) => item.id);
|
||||||
|
} else {
|
||||||
|
deleteCaseIds = Array.from(selectedKeys).map(Number);
|
||||||
|
}
|
||||||
|
onDeleteCases(deleteCaseIds);
|
||||||
|
setSelectedKeys(new Set([]));
|
||||||
|
};
|
||||||
|
|
||||||
const classNames = useMemo(
|
const classNames = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
wrapper: ['max-w-3xl'],
|
wrapper: ['max-w-3xl'],
|
||||||
@@ -125,16 +140,6 @@ export default function TestCaseTable({
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDeleteCasesClick = async () => {
|
|
||||||
if (selectedKeys === 'all') {
|
|
||||||
const allKeys = sortedItems.map((item) => item.id);
|
|
||||||
onDeleteCases(allKeys);
|
|
||||||
} else {
|
|
||||||
onDeleteCases([...selectedKeys]);
|
|
||||||
}
|
|
||||||
setSelectedKeys(new Set([]));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between">
|
<div className="border-b-1 dark:border-neutral-700 w-full p-3 flex items-center justify-between">
|
||||||
@@ -146,12 +151,12 @@ export default function TestCaseTable({
|
|||||||
size="sm"
|
size="sm"
|
||||||
color="danger"
|
color="danger"
|
||||||
className="me-2"
|
className="me-2"
|
||||||
onClick={onDeleteCasesClick}
|
onPress={handleDeleteCases}
|
||||||
>
|
>
|
||||||
{messages.delete}
|
{messages.delete}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button startContent={<Plus size={16} />} size="sm" color="primary" onClick={onCreateCase}>
|
<Button startContent={<Plus size={16} />} size="sm" color="primary" onPress={onCreateCase}>
|
||||||
{messages.newTestCase}
|
{messages.newTestCase}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ async function createCase(jwt: string, folderId: string) {
|
|||||||
template: 0,
|
template: 0,
|
||||||
preConditions: '',
|
preConditions: '',
|
||||||
expectedResults: '',
|
expectedResults: '',
|
||||||
folderId: folderId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchOptions = {
|
const fetchOptions = {
|
||||||
@@ -71,7 +70,7 @@ async function createCase(jwt: string, folderId: string) {
|
|||||||
body: JSON.stringify(newCase),
|
body: JSON.stringify(newCase),
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = `${apiServer}/cases`;
|
const url = `${apiServer}/cases?folderId=${folderId}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, fetchOptions);
|
const response = await fetch(url, fetchOptions);
|
||||||
@@ -111,29 +110,7 @@ async function updateCase(jwt: string, updateCaseData: CaseType) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function deleteCase(jwt: string, caseId: number) {
|
async function deleteCases(jwt: string, deleteCaseIds: number[], projectId: number) {
|
||||||
// const fetchOptions = {
|
|
||||||
// method: 'DELETE',
|
|
||||||
// headers: {
|
|
||||||
// 'Content-Type': 'application/json',
|
|
||||||
// Authorization: `Bearer ${jwt}`,
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
async function deleteCases(jwt: string, deleteCaseIds: number[]) {
|
|
||||||
const fetchOptions = {
|
const fetchOptions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -143,7 +120,7 @@ async function deleteCases(jwt: string, deleteCaseIds: number[]) {
|
|||||||
body: JSON.stringify({ caseIds: deleteCaseIds }),
|
body: JSON.stringify({ caseIds: deleteCaseIds }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = `${apiServer}/cases/bulkdelete`;
|
const url = `${apiServer}/cases/bulkdelete?projectId=${projectId}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, fetchOptions);
|
const response = await fetch(url, fetchOptions);
|
||||||
|
|||||||
Reference in New Issue
Block a user