Skip to content

Commit

Permalink
Merge pull request #82 from massfords/unique-state-names
Browse files Browse the repository at this point in the history
enforce unique state names during validation
  • Loading branch information
ChristopheBougere authored Jun 27, 2022
2 parents 1a4c195 + d075b86 commit d8452f0
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 16 deletions.
28 changes: 28 additions & 0 deletions src/__tests__/definitions/invalid-map-dupe-state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"Comment": "Exhibits duplicate state error. State names MUST be unique within the scope of the whole state machine.",
"StartAt": "Map",
"States": {
"Map": {
"Type": "Map",
"Next": "Final State",
"InputPath": "$.input",
"ItemsPath": "$.items",
"MaxConcurrency": 0,
"Iterator": {
"StartAt": "Final State",
"States": {
"Final State": {
"Type": "Wait",
"Seconds": 20,
"End": true
}
}
},
"ResultPath": "$.result"
},
"Final State": {
"Type": "Pass",
"End": true
}
}
}
23 changes: 23 additions & 0 deletions src/lib/state-names.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const jp = require('jsonpath');

module.exports = (definition) => {
const errorMessages = [];
const names = new Map();
jp.query(definition, '$..[\'States\']')
.forEach((states) => {
Object.keys(states).forEach((stateName) => {
const current = names.get(stateName);
names.set(stateName, current ? current + 1 : 1);
});
});
names.forEach((value, key) => {
if (value > 1) {
errorMessages.push({
'Error code': 'DUPLICATE_STATE_NAMES',
Message: `A state with this name already exists: ${key}`,
});
}
});

return errorMessages;
};
27 changes: 11 additions & 16 deletions src/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const errors = require('./schemas/errors');
const checkJsonPath = require('./lib/json-path-errors');
const missingTransitionTarget = require('./lib/missing-transition-target');
const stateTransitions = require('./lib/state-transitions');
const stateNames = require('./lib/state-names');

function formatError(e) {
const code = e.Code ? e.Code : e['Error code'];
Expand All @@ -37,32 +38,26 @@ function validator(definition) {
],
});

// Validating JSON paths
const jsonPathErrors = checkJsonPath(definition);

// Check unreachable states
const missingTransitionTargetErrors = missingTransitionTarget(definition);

// Validating JSON schemas
const isJsonSchemaValid = ajv.validate('http://asl-validator.cloud/state-machine.json#', definition);

// Check for Parallel states
const transitionErrors = isJsonSchemaValid ? stateTransitions(definition) : [];
const postSchemaValidationErrors = [];
if (isJsonSchemaValid) {
postSchemaValidationErrors.push(...checkJsonPath(definition));
postSchemaValidationErrors.push(...missingTransitionTarget(definition));
postSchemaValidationErrors.push(...stateTransitions(definition));
postSchemaValidationErrors.push(...stateNames(definition));
}

return {
isValid: isJsonSchemaValid && !jsonPathErrors.length && !missingTransitionTargetErrors.length
&& !transitionErrors.length,
errors: jsonPathErrors.concat(ajv.errors || [])
.concat(missingTransitionTargetErrors || [])
.concat(transitionErrors || []),
isValid: isJsonSchemaValid && !postSchemaValidationErrors.length,
errors: (ajv.errors || []).concat(postSchemaValidationErrors || []),
errorsText: (separator = '\n') => {
const errorList = [];
errorList.push(jsonPathErrors.map(formatError).join(separator));
if (ajv.errors) {
errorList.push(ajv.errorsText(ajv.errors, { separator }));
}
errorList.push(missingTransitionTargetErrors.map(formatError).join(separator));
errorList.push(transitionErrors.map(formatError).join(separator));
errorList.push(postSchemaValidationErrors.map(formatError).join(separator));
return errorList.join(separator);
},
};
Expand Down

0 comments on commit d8452f0

Please sign in to comment.