bpmnlint-plugin-camunda-compat 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +9 -2
- package/package.json +2 -1
- package/rules/called-decision-or-task-definition/config.js +20 -23
- package/rules/element-type/config.js +111 -172
- package/rules/inclusive-gateway.js +52 -0
- package/rules/timer/config.js +17 -0
- package/rules/timer/index.js +164 -0
- package/rules/utils/cron.js +95 -0
- package/rules/utils/element.js +60 -1
- package/rules/utils/error-types.js +2 -1
- package/rules/utils/iso8601.js +23 -0
package/index.js
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
const { omit } = require('min-dash');
|
2
2
|
|
3
3
|
const calledDecisionOrTaskDefinitionConfig = require('./rules/called-decision-or-task-definition/config'),
|
4
|
-
elementTypeConfig = require('./rules/element-type/config')
|
4
|
+
elementTypeConfig = require('./rules/element-type/config'),
|
5
|
+
timerConfig = require('./rules/timer/config');
|
5
6
|
|
6
7
|
const camundaCloud10Rules = {
|
7
8
|
'called-decision-or-task-definition': [ 'error', calledDecisionOrTaskDefinitionConfig.camundaCloud10 ],
|
@@ -14,6 +15,7 @@ const camundaCloud10Rules = {
|
|
14
15
|
'no-template': 'error',
|
15
16
|
'no-zeebe-properties': 'error',
|
16
17
|
'subscription': 'error',
|
18
|
+
'timer': [ 'error', timerConfig.camundaCloud10 ],
|
17
19
|
'user-task-form': 'error'
|
18
20
|
};
|
19
21
|
|
@@ -36,7 +38,12 @@ const camundaCloud13Rules = {
|
|
36
38
|
|
37
39
|
const camundaCloud80Rules = omit(camundaCloud13Rules, 'no-template');
|
38
40
|
|
39
|
-
const camundaCloud81Rules =
|
41
|
+
const camundaCloud81Rules = {
|
42
|
+
...omit(camundaCloud80Rules, 'no-zeebe-properties'),
|
43
|
+
'element-type': [ 'error', elementTypeConfig.camundaCloud81 ],
|
44
|
+
'inclusive-gateway': 'error',
|
45
|
+
'timer': [ 'error', timerConfig.camundaCloud81 ]
|
46
|
+
};
|
40
47
|
|
41
48
|
module.exports = {
|
42
49
|
configs: {
|
package/package.json
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "bpmnlint-plugin-camunda-compat",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.13.0",
|
4
4
|
"description": "A bpmnlint plug-in for Camunda Platform compatibility",
|
5
5
|
"main": "index.js",
|
6
6
|
"scripts": {
|
7
7
|
"all": "npm run lint && npm test",
|
8
|
+
"dev": "npm run test:watch",
|
8
9
|
"lint": "eslint .",
|
9
10
|
"test": "mocha test/**/*.spec.js",
|
10
11
|
"test:watch": "npm run test -- --watch"
|
@@ -1,36 +1,33 @@
|
|
1
|
+
const camundaCloud10ElementsTaskDefinition = [
|
2
|
+
'bpmn:ServiceTask'
|
3
|
+
];
|
4
|
+
|
5
|
+
const camundaCloud11ElementsTaskDefinition = [
|
6
|
+
...camundaCloud10ElementsTaskDefinition,
|
7
|
+
'bpmn:BusinessRuleTask',
|
8
|
+
'bpmn:ScriptTask',
|
9
|
+
'bpmn:SendTask'
|
10
|
+
];
|
11
|
+
|
12
|
+
const camundaCloud12ElementsTaskDefinition = [
|
13
|
+
...camundaCloud11ElementsTaskDefinition,
|
14
|
+
'bpmn:IntermediateThrowEvent'
|
15
|
+
];
|
16
|
+
|
1
17
|
module.exports = {
|
2
18
|
camundaCloud10: {
|
3
|
-
elementsTaskDefinition:
|
4
|
-
'bpmn:ServiceTask'
|
5
|
-
]
|
19
|
+
elementsTaskDefinition: camundaCloud10ElementsTaskDefinition
|
6
20
|
},
|
7
21
|
camundaCloud11: {
|
8
|
-
elementsTaskDefinition:
|
9
|
-
'bpmn:BusinessRuleTask',
|
10
|
-
'bpmn:ServiceTask',
|
11
|
-
'bpmn:ScriptTask',
|
12
|
-
'bpmn:SendTask'
|
13
|
-
]
|
22
|
+
elementsTaskDefinition: camundaCloud11ElementsTaskDefinition
|
14
23
|
},
|
15
24
|
camundaCloud12: {
|
16
|
-
elementsTaskDefinition:
|
17
|
-
'bpmn:BusinessRuleTask',
|
18
|
-
'bpmn:IntermediateThrowEvent',
|
19
|
-
'bpmn:ServiceTask',
|
20
|
-
'bpmn:ScriptTask',
|
21
|
-
'bpmn:SendTask'
|
22
|
-
]
|
25
|
+
elementsTaskDefinition: camundaCloud12ElementsTaskDefinition
|
23
26
|
},
|
24
27
|
camundaCloud13: {
|
25
28
|
elementsCalledDecision: [
|
26
29
|
'bpmn:BusinessRuleTask'
|
27
30
|
],
|
28
|
-
elementsTaskDefinition:
|
29
|
-
'bpmn:BusinessRuleTask',
|
30
|
-
'bpmn:IntermediateThrowEvent',
|
31
|
-
'bpmn:ServiceTask',
|
32
|
-
'bpmn:ScriptTask',
|
33
|
-
'bpmn:SendTask'
|
34
|
-
]
|
31
|
+
elementsTaskDefinition: camundaCloud12ElementsTaskDefinition
|
35
32
|
}
|
36
33
|
};
|
@@ -1,186 +1,125 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
'bpmn:UserTask'
|
25
|
-
],
|
26
|
-
elementsNoEventDefinitionRequired: [
|
27
|
-
'bpmn:EndEvent',
|
28
|
-
'bpmn:StartEvent'
|
29
|
-
],
|
30
|
-
eventDefinitions: {
|
31
|
-
'bpmn:BoundaryEvent': [
|
32
|
-
'bpmn:ErrorEventDefinition',
|
33
|
-
'bpmn:MessageEventDefinition',
|
34
|
-
'bpmn:TimerEventDefinition'
|
35
|
-
],
|
36
|
-
'bpmn:EndEvent': [
|
37
|
-
'bpmn:ErrorEventDefinition'
|
38
|
-
],
|
39
|
-
'bpmn:IntermediateCatchEvent': [
|
40
|
-
'bpmn:MessageEventDefinition',
|
41
|
-
'bpmn:TimerEventDefinition'
|
42
|
-
],
|
43
|
-
'bpmn:StartEvent': [
|
44
|
-
'bpmn:ErrorEventDefinition',
|
45
|
-
'bpmn:MessageEventDefinition',
|
46
|
-
'bpmn:TimerEventDefinition'
|
47
|
-
]
|
48
|
-
}
|
49
|
-
},
|
50
|
-
camundaCloud11: {
|
51
|
-
elements: [
|
1
|
+
const camundaCloud10Elements = [
|
2
|
+
'bpmn:Association',
|
3
|
+
'bpmn:BoundaryEvent',
|
4
|
+
'bpmn:CallActivity',
|
5
|
+
'bpmn:Collaboration',
|
6
|
+
'bpmn:Definitions',
|
7
|
+
'bpmn:EndEvent',
|
8
|
+
'bpmn:EventBasedGateway',
|
9
|
+
'bpmn:ExclusiveGateway',
|
10
|
+
'bpmn:Group',
|
11
|
+
'bpmn:IntermediateCatchEvent',
|
12
|
+
'bpmn:MessageFlow',
|
13
|
+
'bpmn:ParallelGateway',
|
14
|
+
'bpmn:Participant',
|
15
|
+
'bpmn:Process',
|
16
|
+
'bpmn:ReceiveTask',
|
17
|
+
'bpmn:SequenceFlow',
|
18
|
+
'bpmn:ServiceTask',
|
19
|
+
'bpmn:StartEvent',
|
20
|
+
'bpmn:SubProcess',
|
21
|
+
'bpmn:TextAnnotation',
|
22
|
+
'bpmn:UserTask'
|
23
|
+
];
|
52
24
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
'bpmn:ExclusiveGateway',
|
62
|
-
'bpmn:Group',
|
63
|
-
'bpmn:IntermediateCatchEvent',
|
64
|
-
'bpmn:MessageFlow',
|
65
|
-
'bpmn:ParallelGateway',
|
66
|
-
'bpmn:Participant',
|
67
|
-
'bpmn:Process',
|
68
|
-
'bpmn:ReceiveTask',
|
69
|
-
'bpmn:SequenceFlow',
|
70
|
-
'bpmn:ServiceTask',
|
71
|
-
'bpmn:StartEvent',
|
72
|
-
'bpmn:SubProcess',
|
73
|
-
'bpmn:TextAnnotation',
|
74
|
-
'bpmn:UserTask',
|
25
|
+
const camundaCloud11Elements = [
|
26
|
+
...camundaCloud10Elements,
|
27
|
+
'bpmn:BusinessRuleTask',
|
28
|
+
'bpmn:IntermediateThrowEvent',
|
29
|
+
'bpmn:ManualTask',
|
30
|
+
'bpmn:ScriptTask',
|
31
|
+
'bpmn:SendTask'
|
32
|
+
];
|
75
33
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
'bpmn:ManualTask',
|
80
|
-
'bpmn:ScriptTask',
|
81
|
-
'bpmn:SendTask'
|
82
|
-
],
|
83
|
-
elementsNoEventDefinitionRequired: [
|
34
|
+
const camundaCloud12Elements = [
|
35
|
+
...camundaCloud11Elements
|
36
|
+
];
|
84
37
|
|
85
|
-
|
86
|
-
|
87
|
-
|
38
|
+
const camundaCloud81Elements = [
|
39
|
+
...camundaCloud12Elements,
|
40
|
+
'bpmn:InclusiveGateway'
|
41
|
+
];
|
88
42
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
'bpmn:BoundaryEvent': [
|
94
|
-
'bpmn:ErrorEventDefinition',
|
95
|
-
'bpmn:MessageEventDefinition',
|
96
|
-
'bpmn:TimerEventDefinition'
|
97
|
-
],
|
98
|
-
'bpmn:EndEvent': [
|
99
|
-
'bpmn:ErrorEventDefinition'
|
100
|
-
],
|
101
|
-
'bpmn:IntermediateCatchEvent': [
|
102
|
-
'bpmn:MessageEventDefinition',
|
103
|
-
'bpmn:TimerEventDefinition'
|
104
|
-
],
|
105
|
-
'bpmn:StartEvent': [
|
106
|
-
'bpmn:ErrorEventDefinition',
|
107
|
-
'bpmn:MessageEventDefinition',
|
108
|
-
'bpmn:TimerEventDefinition'
|
109
|
-
]
|
110
|
-
}
|
111
|
-
},
|
112
|
-
camundaCloud12: {
|
113
|
-
elements: [
|
43
|
+
const camundaCloud10ElementsNoEventDefinitionRequired = [
|
44
|
+
'bpmn:EndEvent',
|
45
|
+
'bpmn:StartEvent'
|
46
|
+
];
|
114
47
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
'bpmn:Collaboration',
|
120
|
-
'bpmn:Definitions',
|
121
|
-
'bpmn:EndEvent',
|
122
|
-
'bpmn:EventBasedGateway',
|
123
|
-
'bpmn:ExclusiveGateway',
|
124
|
-
'bpmn:Group',
|
125
|
-
'bpmn:IntermediateCatchEvent',
|
126
|
-
'bpmn:MessageFlow',
|
127
|
-
'bpmn:ParallelGateway',
|
128
|
-
'bpmn:Participant',
|
129
|
-
'bpmn:Process',
|
130
|
-
'bpmn:ReceiveTask',
|
131
|
-
'bpmn:SequenceFlow',
|
132
|
-
'bpmn:ServiceTask',
|
133
|
-
'bpmn:StartEvent',
|
134
|
-
'bpmn:SubProcess',
|
135
|
-
'bpmn:TextAnnotation',
|
136
|
-
'bpmn:UserTask',
|
48
|
+
const camundaCloud11ElementsNoEventDefinitionRequired = [
|
49
|
+
...camundaCloud10ElementsNoEventDefinitionRequired,
|
50
|
+
'bpmn:IntermediateThrowEvent'
|
51
|
+
];
|
137
52
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
'bpmn:ManualTask',
|
142
|
-
'bpmn:ScriptTask',
|
143
|
-
'bpmn:SendTask'
|
144
|
-
],
|
145
|
-
elementsNoEventDefinitionRequired: [
|
53
|
+
const camundaCloud12ElementsNoEventDefinitionRequired = [
|
54
|
+
...camundaCloud11ElementsNoEventDefinitionRequired
|
55
|
+
];
|
146
56
|
|
147
|
-
|
148
|
-
|
149
|
-
|
57
|
+
const camundaCloud81ElementsNoEventDefinitionRequired = [
|
58
|
+
...camundaCloud12ElementsNoEventDefinitionRequired
|
59
|
+
];
|
150
60
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
61
|
+
const camundaCloud10EventDefinitions = {
|
62
|
+
'bpmn:BoundaryEvent': [
|
63
|
+
'bpmn:ErrorEventDefinition',
|
64
|
+
'bpmn:MessageEventDefinition',
|
65
|
+
'bpmn:TimerEventDefinition'
|
66
|
+
],
|
67
|
+
'bpmn:EndEvent': [
|
68
|
+
'bpmn:ErrorEventDefinition'
|
69
|
+
],
|
70
|
+
'bpmn:IntermediateCatchEvent': [
|
71
|
+
'bpmn:MessageEventDefinition',
|
72
|
+
'bpmn:TimerEventDefinition'
|
73
|
+
],
|
74
|
+
'bpmn:StartEvent': [
|
75
|
+
'bpmn:ErrorEventDefinition',
|
76
|
+
'bpmn:MessageEventDefinition',
|
77
|
+
'bpmn:TimerEventDefinition'
|
78
|
+
]
|
79
|
+
};
|
155
80
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
'bpmn:MessageEventDefinition',
|
160
|
-
'bpmn:TimerEventDefinition'
|
161
|
-
],
|
162
|
-
'bpmn:EndEvent': [
|
81
|
+
const camundaCloud11EventDefinitions = {
|
82
|
+
...camundaCloud10EventDefinitions
|
83
|
+
};
|
163
84
|
|
164
|
-
|
165
|
-
|
85
|
+
const camundaCloud12EventDefinitions = {
|
86
|
+
...camundaCloud11EventDefinitions,
|
87
|
+
'bpmn:EndEvent': [
|
88
|
+
...camundaCloud11EventDefinitions['bpmn:EndEvent'],
|
89
|
+
'bpmn:MessageEventDefinition'
|
90
|
+
],
|
91
|
+
'bpmn:IntermediateThrowEvent': [
|
92
|
+
'bpmn:MessageEventDefinition'
|
93
|
+
]
|
94
|
+
};
|
166
95
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
'bpmn:StartEvent': [
|
175
|
-
'bpmn:ErrorEventDefinition',
|
176
|
-
'bpmn:MessageEventDefinition',
|
177
|
-
'bpmn:TimerEventDefinition'
|
178
|
-
],
|
96
|
+
const camundaCloud81EventDefinitions = {
|
97
|
+
...camundaCloud12EventDefinitions,
|
98
|
+
'bpmn:EndEvent': [
|
99
|
+
...camundaCloud12EventDefinitions['bpmn:EndEvent'],
|
100
|
+
'bpmn:TerminateEventDefinition'
|
101
|
+
]
|
102
|
+
};
|
179
103
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
104
|
+
module.exports = {
|
105
|
+
camundaCloud10: {
|
106
|
+
elements: camundaCloud10Elements,
|
107
|
+
elementsNoEventDefinitionRequired: camundaCloud10ElementsNoEventDefinitionRequired,
|
108
|
+
eventDefinitions: camundaCloud10EventDefinitions
|
109
|
+
},
|
110
|
+
camundaCloud11: {
|
111
|
+
elements: camundaCloud11Elements,
|
112
|
+
elementsNoEventDefinitionRequired: camundaCloud11ElementsNoEventDefinitionRequired,
|
113
|
+
eventDefinitions: camundaCloud11EventDefinitions
|
114
|
+
},
|
115
|
+
camundaCloud12: {
|
116
|
+
elements: camundaCloud12Elements,
|
117
|
+
elementsNoEventDefinitionRequired: camundaCloud12ElementsNoEventDefinitionRequired,
|
118
|
+
eventDefinitions: camundaCloud12EventDefinitions
|
119
|
+
},
|
120
|
+
camundaCloud81: {
|
121
|
+
elements: camundaCloud81Elements,
|
122
|
+
elementsNoEventDefinitionRequired: camundaCloud81ElementsNoEventDefinitionRequired,
|
123
|
+
eventDefinitions: camundaCloud81EventDefinitions
|
185
124
|
}
|
186
125
|
};
|
@@ -0,0 +1,52 @@
|
|
1
|
+
const { is } = require('bpmnlint-utils');
|
2
|
+
|
3
|
+
const { hasProperties, ERROR_TYPES } = require('./utils/element');
|
4
|
+
|
5
|
+
const { reportErrors } = require('./utils/reporter');
|
6
|
+
|
7
|
+
module.exports = function() {
|
8
|
+
function check(node, reporter) {
|
9
|
+
if (!is(node, 'bpmn:InclusiveGateway')) {
|
10
|
+
return;
|
11
|
+
}
|
12
|
+
|
13
|
+
const incoming = node.get('incoming');
|
14
|
+
|
15
|
+
if (incoming && incoming.length > 1) {
|
16
|
+
const error = {
|
17
|
+
message: `Element of type <${ node.$type }> must have one property <incoming> of type <bpmn:SequenceFlow>`,
|
18
|
+
path: [ 'incoming' ],
|
19
|
+
error: {
|
20
|
+
type: ERROR_TYPES.PROPERTY_NOT_ALLOWED,
|
21
|
+
node,
|
22
|
+
parentNode: null,
|
23
|
+
property: 'incoming'
|
24
|
+
}
|
25
|
+
};
|
26
|
+
|
27
|
+
reportErrors(node, reporter, error);
|
28
|
+
}
|
29
|
+
|
30
|
+
const outgoing = node.get('outgoing');
|
31
|
+
|
32
|
+
if (outgoing && outgoing.length > 1) {
|
33
|
+
for (let sequenceFlow of outgoing) {
|
34
|
+
if (node.get('default') !== sequenceFlow) {
|
35
|
+
const errors = hasProperties(sequenceFlow, {
|
36
|
+
conditionExpression: {
|
37
|
+
required: true
|
38
|
+
}
|
39
|
+
}, sequenceFlow);
|
40
|
+
|
41
|
+
if (errors.length) {
|
42
|
+
reportErrors(sequenceFlow, reporter, errors);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
return {
|
50
|
+
check
|
51
|
+
};
|
52
|
+
};
|
@@ -0,0 +1,17 @@
|
|
1
|
+
const camundaCloud10Formats = [
|
2
|
+
'iso8601'
|
3
|
+
];
|
4
|
+
|
5
|
+
const camundaCloud81Formats = [
|
6
|
+
...camundaCloud10Formats,
|
7
|
+
'cron'
|
8
|
+
];
|
9
|
+
|
10
|
+
module.exports = {
|
11
|
+
camundaCloud10: {
|
12
|
+
formats: camundaCloud10Formats
|
13
|
+
},
|
14
|
+
camundaCloud81: {
|
15
|
+
formats: camundaCloud81Formats
|
16
|
+
}
|
17
|
+
};
|
@@ -0,0 +1,164 @@
|
|
1
|
+
const {
|
2
|
+
is
|
3
|
+
} = require('bpmnlint-utils');
|
4
|
+
|
5
|
+
const {
|
6
|
+
getEventDefinition,
|
7
|
+
hasProperties,
|
8
|
+
hasProperty
|
9
|
+
} = require('../utils/element');
|
10
|
+
|
11
|
+
const { validateCronExpression } = require('../utils/cron');
|
12
|
+
|
13
|
+
const {
|
14
|
+
validateCycle: validateISO8601Cycle,
|
15
|
+
validateDate: validateISO8601Date,
|
16
|
+
validateDuration: validateISO8601Duration
|
17
|
+
} = require('../utils/iso8601');
|
18
|
+
|
19
|
+
const { reportErrors } = require('../utils/reporter');
|
20
|
+
|
21
|
+
module.exports = function(config = {}) {
|
22
|
+
const { formats = [] } = config;
|
23
|
+
|
24
|
+
function check(node, reporter) {
|
25
|
+
if (!is(node, 'bpmn:Event')) {
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
|
29
|
+
const eventDefinition = getEventDefinition(node);
|
30
|
+
|
31
|
+
if (!eventDefinition || !is(eventDefinition, 'bpmn:TimerEventDefinition')) {
|
32
|
+
return;
|
33
|
+
}
|
34
|
+
|
35
|
+
let errors = checkTimePropertyExists(eventDefinition, node);
|
36
|
+
|
37
|
+
if (errors && errors.length) {
|
38
|
+
reportErrors(node, reporter, errors);
|
39
|
+
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
|
43
|
+
const timeProperty = getTimeProperty(eventDefinition);
|
44
|
+
|
45
|
+
errors = checkTimePropertyNotEmpty(timeProperty, node);
|
46
|
+
|
47
|
+
if (errors && errors.length) {
|
48
|
+
reportErrors(node, reporter, errors);
|
49
|
+
|
50
|
+
return;
|
51
|
+
}
|
52
|
+
|
53
|
+
errors = checkTimePropertyCorrectFormat(eventDefinition, node, formats);
|
54
|
+
|
55
|
+
if (errors && errors.length) {
|
56
|
+
reportErrors(node, reporter, errors);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
return {
|
61
|
+
check
|
62
|
+
};
|
63
|
+
};
|
64
|
+
|
65
|
+
function checkTimePropertyExists(eventDefinition, event) {
|
66
|
+
if (is(event, 'bpmn:StartEvent')) {
|
67
|
+
return hasProperty(eventDefinition, [ 'timeCycle', 'timeDate' ], event);
|
68
|
+
} else if (is(event, 'bpmn:IntermediateCatchEvent') || isInterruptingBoundaryEvent(event)) {
|
69
|
+
return hasProperties(eventDefinition, {
|
70
|
+
timeDuration: {
|
71
|
+
required: true
|
72
|
+
}
|
73
|
+
}, event);
|
74
|
+
} else if (is(event, 'bpmn:BoundaryEvent')) {
|
75
|
+
return hasProperty(eventDefinition, [ 'timeCycle', 'timeDuration' ], event);
|
76
|
+
}
|
77
|
+
|
78
|
+
return [];
|
79
|
+
}
|
80
|
+
|
81
|
+
function checkTimePropertyNotEmpty(timeProperty, event) {
|
82
|
+
return hasProperties(timeProperty, {
|
83
|
+
body: {
|
84
|
+
required: true,
|
85
|
+
}
|
86
|
+
}, event);
|
87
|
+
}
|
88
|
+
|
89
|
+
function checkTimePropertyCorrectFormat(eventDefinition, event, formats) {
|
90
|
+
const timeCycle = eventDefinition.get('timeCycle'),
|
91
|
+
timeDate = eventDefinition.get('timeDate'),
|
92
|
+
timeDuration = eventDefinition.get('timeDuration');
|
93
|
+
|
94
|
+
if (timeCycle) {
|
95
|
+
return hasProperties(timeCycle, {
|
96
|
+
body: {
|
97
|
+
allowed: cycle => validateCycle(cycle, formats)
|
98
|
+
}
|
99
|
+
}, event);
|
100
|
+
} else if (timeDate) {
|
101
|
+
return hasProperties(timeDate, {
|
102
|
+
body: {
|
103
|
+
allowed: date => validateDate(date, formats)
|
104
|
+
}
|
105
|
+
}, event);
|
106
|
+
} else if (timeDuration) {
|
107
|
+
return hasProperties(timeDuration, {
|
108
|
+
body: {
|
109
|
+
allowed: duration => validateDuration(duration, formats)
|
110
|
+
}
|
111
|
+
}, event);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
// helper //////////////
|
118
|
+
function getTimeProperty(eventDefinition) {
|
119
|
+
return eventDefinition.get('timeCycle') || eventDefinition.get('timeDate') || eventDefinition.get('timeDuration');
|
120
|
+
}
|
121
|
+
|
122
|
+
function isInterruptingBoundaryEvent(event) {
|
123
|
+
return is(event, 'bpmn:BoundaryEvent') && event.get('cancelActivity') !== false;
|
124
|
+
}
|
125
|
+
|
126
|
+
function validateDate(date, formats) {
|
127
|
+
if (validateExpression(date)) {
|
128
|
+
return true;
|
129
|
+
}
|
130
|
+
|
131
|
+
if (formats.includes('iso8601') && validateISO8601Date(date)) {
|
132
|
+
return true;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
function validateCycle(cycle, formats) {
|
137
|
+
if (validateExpression(cycle)) {
|
138
|
+
return true;
|
139
|
+
}
|
140
|
+
|
141
|
+
if (formats.includes('iso8601') && validateISO8601Cycle(cycle)) {
|
142
|
+
return true;
|
143
|
+
}
|
144
|
+
|
145
|
+
if (formats.includes('cron') && validateCronExpression(cycle)) {
|
146
|
+
return true;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
function validateDuration(duration, formats) {
|
151
|
+
if (validateExpression(duration)) {
|
152
|
+
return true;
|
153
|
+
}
|
154
|
+
|
155
|
+
if (formats.includes('iso8601') && validateISO8601Duration(duration)) {
|
156
|
+
return true;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
function validateExpression(text) {
|
161
|
+
if (text.startsWith('=')) {
|
162
|
+
return true;
|
163
|
+
}
|
164
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
const MACROS = [
|
2
|
+
'@yearly',
|
3
|
+
'@annually',
|
4
|
+
'@monthly',
|
5
|
+
'@weekly',
|
6
|
+
'@daily',
|
7
|
+
'@midnight',
|
8
|
+
'@hourly'
|
9
|
+
];
|
10
|
+
const MACRO_REGEX = new RegExp(`^(${ MACROS.join('|') })$`);
|
11
|
+
|
12
|
+
function validateMacro(text) {
|
13
|
+
return MACRO_REGEX.test(text);
|
14
|
+
}
|
15
|
+
|
16
|
+
const ASTERISK = '\\*';
|
17
|
+
const QUESTION_MARK = '\\?';
|
18
|
+
const COMMA = ',';
|
19
|
+
|
20
|
+
const SECOND = '([0-5]?[0-9])';
|
21
|
+
const MINUTE = '([0-5]?[0-9])';
|
22
|
+
const HOUR = '([01]?[0-9]|2[0-3])';
|
23
|
+
const DAY_OF_MONTH = or(`L(-${ or('[0-2]?[0-9]', '3[0-1]') })?`, '[0-2]?[0-9]', '3[0-1]', dayOfMonthSuffix(or('L', '[0-2]?[0-9]', '3[0-1]')));
|
24
|
+
|
25
|
+
const MONTHS = [
|
26
|
+
'JAN',
|
27
|
+
'FEB',
|
28
|
+
'MAR',
|
29
|
+
'APR',
|
30
|
+
'MAY',
|
31
|
+
'JUN',
|
32
|
+
'JUL',
|
33
|
+
'AUG',
|
34
|
+
'SEP',
|
35
|
+
'OCT',
|
36
|
+
'NOV',
|
37
|
+
'DEC'
|
38
|
+
];
|
39
|
+
const MONTH = or('[0]?[1-9]', '1[0-2]', ...MONTHS);
|
40
|
+
|
41
|
+
const WEEK_DAYS = [
|
42
|
+
'MON',
|
43
|
+
'TUE',
|
44
|
+
'WED',
|
45
|
+
'THU',
|
46
|
+
'FRI',
|
47
|
+
'SAT',
|
48
|
+
'SUN'
|
49
|
+
];
|
50
|
+
const DAY_OF_WEEK = or('[0-7]', ...WEEK_DAYS);
|
51
|
+
|
52
|
+
const SECOND_REGEX = or(ASTERISK, interval(ASTERISK), commaSeparated(or(interval(SECOND), optionalRange(SECOND))));
|
53
|
+
const MINUTE_REGEX = or(ASTERISK, interval(ASTERISK), commaSeparated(or(interval(MINUTE), optionalRange(MINUTE))));
|
54
|
+
const HOUR_REGEX = or(ASTERISK, interval(ASTERISK), commaSeparated(or(interval(HOUR), optionalRange(HOUR))));
|
55
|
+
const DAY_OF_MONTH_REGEX = or(ASTERISK, QUESTION_MARK, commaSeparated(optionalRange(DAY_OF_MONTH)));
|
56
|
+
const MONTH_REGEX = or(ASTERISK, commaSeparated(optionalRange(MONTH)));
|
57
|
+
const DAY_OF_WEEK_REGEX = or(ASTERISK, QUESTION_MARK, commaSeparated(or(optionalRange(DAY_OF_WEEK), dayOfWeekSuffix(DAY_OF_WEEK))));
|
58
|
+
|
59
|
+
const CRON_REGEX = new RegExp(`^${ SECOND_REGEX } ${ MINUTE_REGEX } ${ HOUR_REGEX } ${ DAY_OF_MONTH_REGEX } ${ MONTH_REGEX } ${ DAY_OF_WEEK_REGEX }$`, 'i');
|
60
|
+
|
61
|
+
function validateCron(value) {
|
62
|
+
return CRON_REGEX.test(value);
|
63
|
+
}
|
64
|
+
|
65
|
+
module.exports.validateCronExpression = function(value) {
|
66
|
+
return validateMacro(value) || validateCron(value);
|
67
|
+
};
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
// helper //////////////
|
72
|
+
|
73
|
+
function or(...patterns) {
|
74
|
+
return `(${patterns.join('|')})`;
|
75
|
+
}
|
76
|
+
|
77
|
+
function optionalRange(pattern) {
|
78
|
+
return `${pattern}(-${pattern})?`;
|
79
|
+
}
|
80
|
+
|
81
|
+
function commaSeparated(pattern) {
|
82
|
+
return `${pattern}(${COMMA}${pattern})*`;
|
83
|
+
}
|
84
|
+
|
85
|
+
function interval(pattern) {
|
86
|
+
return `${pattern}/\\d+`;
|
87
|
+
}
|
88
|
+
|
89
|
+
function dayOfMonthSuffix(pattern) {
|
90
|
+
return `${pattern}W`;
|
91
|
+
}
|
92
|
+
|
93
|
+
function dayOfWeekSuffix(pattern) {
|
94
|
+
return `${pattern}(#[1-5]|L)`;
|
95
|
+
}
|
package/rules/utils/element.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
const {
|
2
2
|
isArray,
|
3
3
|
isDefined,
|
4
|
+
isFunction,
|
4
5
|
isNil,
|
5
6
|
some
|
6
7
|
} = require('min-dash');
|
@@ -223,10 +224,62 @@ module.exports.hasProperties = function(node, properties, parentNode = null) {
|
|
223
224
|
];
|
224
225
|
}
|
225
226
|
|
227
|
+
if (isFunction(propertyChecks.allowed) && !propertyChecks.allowed(propertyValue)) {
|
228
|
+
return [
|
229
|
+
...results,
|
230
|
+
{
|
231
|
+
message: `Property <${ propertyName }> of element of type <${ node.$type }> must not have value of <${ truncate(propertyValue) }>`,
|
232
|
+
path: path
|
233
|
+
? [ ...path, propertyName ]
|
234
|
+
: [ propertyName ],
|
235
|
+
error: {
|
236
|
+
type: ERROR_TYPES.PROPERTY_VALUE_NOT_ALLOWED,
|
237
|
+
node,
|
238
|
+
parentNode: parentNode == node ? null : parentNode,
|
239
|
+
property: propertyName,
|
240
|
+
}
|
241
|
+
}
|
242
|
+
];
|
243
|
+
}
|
244
|
+
|
226
245
|
return results;
|
227
246
|
}, []);
|
228
247
|
};
|
229
248
|
|
249
|
+
module.exports.hasProperty = function(node, types, parentNode = null) {
|
250
|
+
const typesArray = isArray(types) ? types : [ types ];
|
251
|
+
|
252
|
+
const properties = findProperties(node, typesArray);
|
253
|
+
|
254
|
+
if (properties.length !== 1) {
|
255
|
+
return [
|
256
|
+
{
|
257
|
+
message: `Element of type <${ node.$type }> must have one property of type ${ formatTypes(typesArray, true) }`,
|
258
|
+
path: getPath(node, parentNode),
|
259
|
+
error: {
|
260
|
+
type: ERROR_TYPES.PROPERTY_REQUIRED,
|
261
|
+
node,
|
262
|
+
parentNode: parentNode == node ? null : parentNode,
|
263
|
+
requiredProperty: types
|
264
|
+
}
|
265
|
+
}
|
266
|
+
];
|
267
|
+
}
|
268
|
+
|
269
|
+
return [];
|
270
|
+
};
|
271
|
+
|
272
|
+
function findProperties(node, types) {
|
273
|
+
const properties = [];
|
274
|
+
for (const type of types) {
|
275
|
+
if (isDefined(node.get(type))) {
|
276
|
+
properties.push(node.get(type));
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
return properties;
|
281
|
+
}
|
282
|
+
|
230
283
|
module.exports.hasExtensionElement = function(node, types, parentNode = null) {
|
231
284
|
const typesArray = isArray(types) ? types : [ types ];
|
232
285
|
|
@@ -281,4 +334,10 @@ module.exports.isExactly = isExactly;
|
|
281
334
|
|
282
335
|
module.exports.isAnyExactly = function(node, types) {
|
283
336
|
return some(types, (type) => isExactly(node, type));
|
284
|
-
};
|
337
|
+
};
|
338
|
+
|
339
|
+
function truncate(string, maxLength = 10) {
|
340
|
+
const stringified = `${ string }`;
|
341
|
+
|
342
|
+
return stringified.length > maxLength ? `${ stringified.slice(0, maxLength) }...` : stringified;
|
343
|
+
}
|
@@ -6,5 +6,6 @@ module.exports.ERROR_TYPES = Object.freeze({
|
|
6
6
|
PROPERTY_NOT_ALLOWED: 'propertyNotAllowed',
|
7
7
|
PROPERTY_REQUIRED: 'propertyRequired',
|
8
8
|
PROPERTY_TYPE_NOT_ALLOWED: 'propertyTypeNotAllowed',
|
9
|
-
PROPERTY_VALUE_DUPLICATED: 'propertyValueDuplicated'
|
9
|
+
PROPERTY_VALUE_DUPLICATED: 'propertyValueDuplicated',
|
10
|
+
PROPERTY_VALUE_NOT_ALLOWED: 'propertyValueNotAllowed',
|
10
11
|
});
|
@@ -0,0 +1,23 @@
|
|
1
|
+
const ISO_DATE_REGEX = /^\d{4}(-\d\d){2}T(\d\d:){2}\d\d(Z|([+-]\d\d:\d\d)(\[\w+\/\w+\])?)?$/;
|
2
|
+
const ISO_DURATION = 'P(?!$)(\\d+(\\.\\d+)?[Yy])?(\\d+(\\.\\d+)?[Mm])?(\\d+(\\.\\d+)?[Dd])?(T(?!$)(\\d+(\\.\\d+)?[Hh])?(\\d+(\\.\\d+)?[Mm])?(\\d+(\\.\\d+)?[Ss])?)?$';
|
3
|
+
const ISO_DURATION_REGEX = new RegExp(`^${ISO_DURATION}$`);
|
4
|
+
const ISO_CYCLE = `R(-1|\\d+)?/${ISO_DURATION}`;
|
5
|
+
const ISO_CYCLE_REGEX = new RegExp(`^${ISO_CYCLE}$`);
|
6
|
+
|
7
|
+
module.exports = {
|
8
|
+
validateCycle,
|
9
|
+
validateDate,
|
10
|
+
validateDuration
|
11
|
+
};
|
12
|
+
|
13
|
+
function validateCycle(value) {
|
14
|
+
return ISO_CYCLE_REGEX.test(value);
|
15
|
+
}
|
16
|
+
|
17
|
+
function validateDate(value) {
|
18
|
+
return ISO_DATE_REGEX.test(value);
|
19
|
+
}
|
20
|
+
|
21
|
+
function validateDuration(value) {
|
22
|
+
return ISO_DURATION_REGEX.test(value);
|
23
|
+
}
|