Skip to content

Commit

Permalink
feat(utils): add #hasDuplicatedPropertyValues util
Browse files Browse the repository at this point in the history
  • Loading branch information
philippfromme committed Aug 18, 2022
1 parent 140f8d9 commit 4bfcdfd
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 1 deletion.
45 changes: 45 additions & 0 deletions docs/ERRORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,49 @@ const ERROR_TYPES = {
allowedPropertyType: 'bpmn:MultiInstanceLoopCharacteristics'
}
}
```

## ❌ Property Value Duplicated Error

### Type Definition

```js
/**
* @typedef PropertyValueDuplicatedError
*
* @type {Object}
*
* @property {string} id
* @property {(number|string)[]|null} path
* @property {Object} error
* @property {ERROR_TYPES} error.type
* @property {import('moddle/lib/base')} error.node
* @property {import('moddle/lib/base')|null} error.parentNode
* @property {string} error.duplicatedProperty
* @property {string} error.duplicatedPropertyValue
* @property {import('moddle/lib/base')[]} error.properties
* @property {string} error.propertiesName
*/
```

### Example

```js
{
id: 'ServiceTask_1',
message: 'Properties of type <zeebe:Header> have property <key> with duplicate value of <foo>',
path: null,
error: {
type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
node: Base { $type: 'zeebe:TaskHeaders', ... },
parentNode: Base { $type: 'zeebe:ServiceTask', ... },
duplicatedProperty: 'key',
duplicatedPropertyValue: 'foo',
properties: [
Base { $type: 'zeebe:Header', ... },
Base { $type: 'zeebe:Header', ... }
],
propertiesName: 'values'
}
}
```
44 changes: 44 additions & 0 deletions rules/utils/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,50 @@ function formatTypes(types, exclusive = false) {

module.exports.formatTypes = formatTypes;

module.exports.hasDuplicatedPropertyValues = function(node, propertiesName, propertyName, parentNode = null) {
const properties = node.get(propertiesName);

const propertyValues = properties.map(property => property.get(propertyName));

// (1) find duplicates
const duplicates = propertyValues.reduce((duplicates, propertyValue, index) => {
if (propertyValues.indexOf(propertyValue) !== index && !duplicates.includes(propertyValue)) {
return [
...duplicates,
propertyValue
];
}

return duplicates;
}, []);

// (2) report error for each duplicate
if (duplicates.length) {
return duplicates.map(duplicate => {

// (3) find properties with duplicate
const duplicateProperties = properties.filter(property => property.get(propertyName) === duplicate);

// (4) report error
return {
message: `Properties of type <${ duplicateProperties[ 0 ].$type }> have property <${ propertyName }> with duplicate value of <${ duplicate }>`,
path: null,
error: {
type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
node,
parentNode: parentNode == node ? null : parentNode,
duplicatedProperty: propertyName,
duplicatedPropertyValue: duplicate,
properties: duplicateProperties,
propertiesName
}
};
});
}

return [];
};

module.exports.hasProperties = function(node, properties, parentNode = null) {
return Object.entries(properties).reduce((results, property) => {
const [ propertyName, propertyChecks ] = property;
Expand Down
3 changes: 2 additions & 1 deletion rules/utils/error-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ module.exports.ERROR_TYPES = Object.freeze({
PROPERTY_DEPENDEND_REQUIRED: 'propertyDependendRequired',
PROPERTY_NOT_ALLOWED: 'propertyNotAllowed',
PROPERTY_REQUIRED: 'propertyRequired',
PROPERTY_TYPE_NOT_ALLOWED: 'propertyTypeNotAllowed'
PROPERTY_TYPE_NOT_ALLOWED: 'propertyTypeNotAllowed',
PROPERTY_VALUE_DUPLICATED: 'propertyValueDuplicated'
});
133 changes: 133 additions & 0 deletions test/utils/element.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { expect } = require('chai');
const {
ERROR_TYPES,
formatTypes,
hasDuplicatedPropertyValues,
hasExtensionElementOfType,
hasExtensionElementsOfTypes,
hasProperties,
Expand Down Expand Up @@ -286,6 +287,138 @@ describe('utils/element', function() {
});


describe('#hasDuplicatedPropertyValues', function() {

it('should not return errors', function() {

// given
const taskHeaders = createElement('zeebe:TaskHeaders', {
values: [
createElement('zeebe:Header', {
key: 'foo'
}),
createElement('zeebe:Header', {
key: 'bar'
}),
createElement('zeebe:Header', {
key: 'baz'
})
]
});

// when
const errors = hasDuplicatedPropertyValues(taskHeaders, 'values', 'key');

// then
expect(errors).to.be.empty;
});


it('should return error', function() {

// given
const taskHeaders = createElement('zeebe:TaskHeaders', {
values: [
createElement('zeebe:Header', {
key: 'foo'
}),
createElement('zeebe:Header', {
key: 'foo'
}),
createElement('zeebe:Header', {
key: 'bar'
}),
createElement('zeebe:Header', {
key: 'baz'
})
]
});

// when
const errors = hasDuplicatedPropertyValues(taskHeaders, 'values', 'key');

// then
expect(errors).to.exist;
expect(errors).to.have.length(1);

expect(errors[ 0 ]).eql({
message: 'Properties of type <zeebe:Header> have property <key> with duplicate value of <foo>',
path: null,
error: {
type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
node: taskHeaders,
parentNode: null,
duplicatedProperty: 'key',
duplicatedPropertyValue: 'foo',
properties: taskHeaders.get('values').filter(header => header.get('key') === 'foo'),
propertiesName: 'values'
}
});
});


it('should return errors', function() {

// given
const taskHeaders = createElement('zeebe:TaskHeaders', {
values: [
createElement('zeebe:Header', {
key: 'foo'
}),
createElement('zeebe:Header', {
key: 'foo'
}),
createElement('zeebe:Header', {
key: 'bar'
}),
createElement('zeebe:Header', {
key: 'bar'
}),
createElement('zeebe:Header', {
key: 'baz'
})
]
});

// when
const errors = hasDuplicatedPropertyValues(taskHeaders, 'values', 'key');

// then
expect(errors).to.exist;
expect(errors).to.have.length(2);

expect(errors[ 0 ]).eql({
message: 'Properties of type <zeebe:Header> have property <key> with duplicate value of <foo>',
path: null,
error: {
type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
node: taskHeaders,
parentNode: null,
duplicatedProperty: 'key',
duplicatedPropertyValue: 'foo',
properties: taskHeaders.get('values').filter(header => header.get('key') === 'foo'),
propertiesName: 'values'
}
});

expect(errors[ 1 ]).eql({
message: 'Properties of type <zeebe:Header> have property <key> with duplicate value of <bar>',
path: null,
error: {
type: ERROR_TYPES.PROPERTY_VALUE_DUPLICATED,
node: taskHeaders,
parentNode: null,
duplicatedProperty: 'key',
duplicatedPropertyValue: 'bar',
properties: taskHeaders.get('values').filter(header => header.get('key') === 'bar'),
propertiesName: 'values'
}
});
});

});


describe('#hasProperties', function() {

describe('required', function() {
Expand Down

0 comments on commit 4bfcdfd

Please sign in to comment.