mirror of
https://github.com/strapi/strapi.git
synced 2025-12-05 11:32:13 +00:00
Merge pull request #17311 from strapi/fix/validate-unique-stage-names
Validate all stages of a workflow have a unique name
This commit is contained in:
commit
058c194967
@ -20,7 +20,7 @@ import { WorkflowAttributes } from '../../components/WorkflowAttributes';
|
||||
import { REDUX_NAMESPACE } from '../../constants';
|
||||
import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
|
||||
import { reducer, initialState } from '../../reducer';
|
||||
import { getWorkflowValidationSchema } from '../../utils/getWorkflowValidationSchema';
|
||||
import { validateWorkflow } from '../../utils/validateWorkflow';
|
||||
|
||||
export function ReviewWorkflowsCreateView() {
|
||||
const { formatMessage } = useIntl();
|
||||
@ -108,7 +108,9 @@ export function ReviewWorkflowsCreateView() {
|
||||
submitForm();
|
||||
}
|
||||
},
|
||||
validationSchema: getWorkflowValidationSchema({ formatMessage }),
|
||||
validate(values) {
|
||||
return validateWorkflow({ values, formatMessage });
|
||||
},
|
||||
});
|
||||
|
||||
useInjectReducer(REDUX_NAMESPACE, reducer);
|
||||
|
||||
@ -25,7 +25,7 @@ import { WorkflowAttributes } from '../../components/WorkflowAttributes';
|
||||
import { REDUX_NAMESPACE } from '../../constants';
|
||||
import { useReviewWorkflows } from '../../hooks/useReviewWorkflows';
|
||||
import { reducer, initialState } from '../../reducer';
|
||||
import { getWorkflowValidationSchema } from '../../utils/getWorkflowValidationSchema';
|
||||
import { validateWorkflow } from '../../utils/validateWorkflow';
|
||||
|
||||
export function ReviewWorkflowsEditView() {
|
||||
const { workflowId } = useParams();
|
||||
@ -113,11 +113,11 @@ export function ReviewWorkflowsEditView() {
|
||||
if (currentWorkflowHasDeletedServerStages) {
|
||||
setIsConfirmDeleteDialogOpen(true);
|
||||
} else if (limits?.workflows && meta?.workflowCount > parseInt(limits.workflows, 10)) {
|
||||
/**
|
||||
* If the current license has a limit, check if the total count of workflows
|
||||
* exceeds that limit and display the limits modal instead of sending the
|
||||
* update, because it would throw an API error.
|
||||
*/
|
||||
/**
|
||||
* If the current license has a limit, check if the total count of workflows
|
||||
* exceeds that limit and display the limits modal instead of sending the
|
||||
* update, because it would throw an API error.
|
||||
*/
|
||||
setShowLimitModal('workflow');
|
||||
|
||||
/**
|
||||
@ -134,7 +134,9 @@ export function ReviewWorkflowsEditView() {
|
||||
submitForm();
|
||||
}
|
||||
},
|
||||
validationSchema: getWorkflowValidationSchema({ formatMessage }),
|
||||
validate(values) {
|
||||
return validateWorkflow({ values, formatMessage });
|
||||
},
|
||||
});
|
||||
|
||||
useInjectReducer(REDUX_NAMESPACE, reducer);
|
||||
|
||||
@ -0,0 +1,148 @@
|
||||
import { validateWorkflow } from '../validateWorkflow';
|
||||
|
||||
const generateStringWithLength = (length) => new Array(length + 1).join('_');
|
||||
|
||||
const formatMessage = (message) => message.defaultMessage;
|
||||
|
||||
const setup = (values) => validateWorkflow({ values, formatMessage });
|
||||
|
||||
describe('Settings | Review Workflows | validateWorkflow()', () => {
|
||||
test('name: valid input', async () => {
|
||||
expect(await setup({ name: 'short name' })).toEqual(true);
|
||||
});
|
||||
|
||||
test('name: empty', async () => {
|
||||
expect(await setup({ name: '' })).toMatchInlineSnapshot(`
|
||||
{
|
||||
"name": "name is a required field",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('name: too long', async () => {
|
||||
expect(await setup({ name: generateStringWithLength(256) })).toMatchInlineSnapshot(`
|
||||
{
|
||||
"name": "Name can not be longer than 255 characters",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('contentTypes: valid input', async () => {
|
||||
expect(await setup({ name: 'name', contentTypes: ['1'] })).toEqual(true);
|
||||
});
|
||||
|
||||
test('stages: empty array', async () => {
|
||||
expect(
|
||||
await setup({
|
||||
name: 'name',
|
||||
stages: [],
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"stages": "stages field must have at least 1 items",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('stages.name: valid input', async () => {
|
||||
expect(
|
||||
await setup({
|
||||
name: 'name',
|
||||
stages: [
|
||||
{
|
||||
name: 'stage-1',
|
||||
color: '#ffffff',
|
||||
},
|
||||
],
|
||||
})
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('stages.name: duplicated name', async () => {
|
||||
expect(
|
||||
await setup({
|
||||
name: 'name',
|
||||
stages: [
|
||||
{
|
||||
name: 'stage-1',
|
||||
color: '#ffffff',
|
||||
},
|
||||
|
||||
{
|
||||
name: 'stage-1',
|
||||
color: '#ffffff',
|
||||
},
|
||||
],
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"stages": [
|
||||
{
|
||||
"name": "Stage name must be unique",
|
||||
},
|
||||
{
|
||||
"name": "Stage name must be unique",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('stages.name: name too long', async () => {
|
||||
expect(
|
||||
await setup({
|
||||
name: 'name',
|
||||
stages: [
|
||||
{
|
||||
name: generateStringWithLength(256),
|
||||
color: '#ffffff',
|
||||
},
|
||||
],
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"stages": [
|
||||
{
|
||||
"name": "Name can not be longer than 255 characters",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('stages.color: valid input', async () => {
|
||||
expect(
|
||||
await setup({
|
||||
name: 'name',
|
||||
stages: [
|
||||
{
|
||||
name: 'stage-1',
|
||||
color: '#ffffff',
|
||||
},
|
||||
],
|
||||
})
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('stages.color: invalid hex code', async () => {
|
||||
expect(
|
||||
await setup({
|
||||
name: 'name',
|
||||
stages: [
|
||||
{
|
||||
name: 'stage-1',
|
||||
color: 'non-hex-code',
|
||||
},
|
||||
],
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"stages": [
|
||||
{
|
||||
"color": "stages[0].color must match the following: "/^#(?:[0-9a-fA-F]{3}){1,2}$/i"",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,7 +1,8 @@
|
||||
import set from 'lodash/set';
|
||||
import * as yup from 'yup';
|
||||
|
||||
export function getWorkflowValidationSchema({ formatMessage }) {
|
||||
return yup.object({
|
||||
export async function validateWorkflow({ values, formatMessage }) {
|
||||
const schema = yup.object({
|
||||
contentTypes: yup.array().of(yup.string()),
|
||||
name: yup
|
||||
.string()
|
||||
@ -32,6 +33,20 @@ export function getWorkflowValidationSchema({ formatMessage }) {
|
||||
id: 'Settings.review-workflows.validation.stage.max-length',
|
||||
defaultMessage: 'Name can not be longer than 255 characters',
|
||||
})
|
||||
)
|
||||
.test(
|
||||
'unique-name',
|
||||
formatMessage({
|
||||
id: 'Settings.review-workflows.validation.stage.duplicate',
|
||||
defaultMessage: 'Stage name must be unique',
|
||||
}),
|
||||
function (stageName) {
|
||||
const {
|
||||
options: { context },
|
||||
} = this;
|
||||
|
||||
return context.stages.filter((stage) => stage.name === stageName).length === 1;
|
||||
}
|
||||
),
|
||||
color: yup
|
||||
.string()
|
||||
@ -46,4 +61,20 @@ export function getWorkflowValidationSchema({ formatMessage }) {
|
||||
)
|
||||
.min(1),
|
||||
});
|
||||
|
||||
try {
|
||||
await schema.validate(values, { abortEarly: false, context: values });
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
let errors = {};
|
||||
|
||||
if (error instanceof yup.ValidationError) {
|
||||
error.inner.forEach((error) => {
|
||||
set(errors, error.path, error.message);
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user