diff --git a/index.js b/index.js
index 6fdc492..20c5838 100644
--- a/index.js
+++ b/index.js
@@ -91,7 +91,8 @@ const camundaCloud86Rules = withConfig({
'duplicate-execution-listeners': 'error',
'execution-listener': 'error',
'priority-definition': 'error',
- 'version-tag': 'error'
+ 'version-tag': 'error',
+ 'zeebe-user-task': 'warn',
}, { version: '8.6' });
const camundaCloud87Rules = withConfig({
@@ -162,6 +163,7 @@ const rules = {
'no-zeebe-properties': './rules/camunda-cloud/no-zeebe-properties',
'no-zeebe-user-task': './rules/camunda-cloud/no-zeebe-user-task',
'priority-definition': './rules/camunda-cloud/priority-definition',
+ 'zeebe-user-task': './rules/camunda-cloud/zeebe-user-task',
'secrets': './rules/camunda-cloud/secrets',
'sequence-flow-condition': './rules/camunda-cloud/sequence-flow-condition',
'signal-reference': './rules/camunda-cloud/signal-reference',
diff --git a/rules/camunda-cloud/zeebe-user-task.js b/rules/camunda-cloud/zeebe-user-task.js
new file mode 100644
index 0000000..97e1f7f
--- /dev/null
+++ b/rules/camunda-cloud/zeebe-user-task.js
@@ -0,0 +1,25 @@
+const { is } = require('bpmnlint-utils');
+
+const { reportErrors } = require('../utils/reporter');
+
+const { skipInNonExecutableProcess } = require('../utils/rule');
+
+const { hasExtensionElement } = require('../utils/element');
+
+module.exports = skipInNonExecutableProcess(function() {
+ function check(node, reporter) {
+ if (!is(node, 'bpmn:UserTask')) {
+ return;
+ }
+
+ const errors = hasExtensionElement(node, 'zeebe:UserTask', node);
+
+ if (errors && errors.length) {
+ reportErrors(node, reporter, errors);
+ }
+ }
+
+ return {
+ check
+ };
+});
diff --git a/test/camunda-cloud/integration/zeebe-user-task-errors.bpmn b/test/camunda-cloud/integration/zeebe-user-task-errors.bpmn
new file mode 100644
index 0000000..03c9c1a
--- /dev/null
+++ b/test/camunda-cloud/integration/zeebe-user-task-errors.bpmn
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/camunda-cloud/integration/zeebe-user-task.bpmn b/test/camunda-cloud/integration/zeebe-user-task.bpmn
new file mode 100644
index 0000000..97895fb
--- /dev/null
+++ b/test/camunda-cloud/integration/zeebe-user-task.bpmn
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/test/camunda-cloud/integration/zeebe-user-task.spec.js b/test/camunda-cloud/integration/zeebe-user-task.spec.js
new file mode 100644
index 0000000..238afb6
--- /dev/null
+++ b/test/camunda-cloud/integration/zeebe-user-task.spec.js
@@ -0,0 +1,75 @@
+const { expect } = require('chai');
+
+const Linter = require('bpmnlint/lib/linter');
+
+const NodeResolver = require('bpmnlint/lib/resolver/node-resolver');
+
+const { readModdle } = require('../../helper');
+
+const versions = [
+ '8.6',
+ '8.7'
+];
+
+describe('integration - zeebe-user-task', function() {
+
+ versions.forEach(function(version) {
+
+ let linter;
+
+ beforeEach(function() {
+ linter = new Linter({
+ config: {
+ extends: `plugin:camunda-compat/camunda-cloud-${ version.replace('.', '-') }`
+ },
+ resolver: new NodeResolver()
+ });
+ });
+
+
+ describe(`Camunda Cloud ${ version }`, function() {
+
+ describe('no errors', function() {
+
+ it('should not have errors', async function() {
+
+ // given
+ const { root } = await readModdle('test/camunda-cloud/integration/zeebe-user-task.bpmn');
+
+ // when
+ const reports = await linter.lint(root);
+
+ // then
+ expect(reports[ 'camunda-compat/zeebe-user-task' ]).to.be.undefined;
+ });
+
+ });
+
+
+ describe('errors', function() {
+
+ it('should have errors', async function() {
+
+ // given
+ const { root } = await readModdle('test/camunda-cloud/integration/zeebe-user-task-errors.bpmn');
+
+ // when
+ const reports = await linter.lint(root);
+
+ // then
+ expect(reports[ 'camunda-compat/zeebe-user-task' ]).to.exist;
+ expect(reports[ 'camunda-compat/zeebe-user-task' ]).to.have.lengthOf(1);
+
+ const [ error ] = reports[ 'camunda-compat/zeebe-user-task' ];
+
+ expect(error.message).to.equal('Element of type must have one extension element of type ');
+ expect(error.id).to.equal('UserTask_1');
+ });
+
+ });
+
+ });
+
+ });
+
+});
diff --git a/test/camunda-cloud/zeebe-user-task.spec.js b/test/camunda-cloud/zeebe-user-task.spec.js
new file mode 100644
index 0000000..95efe9e
--- /dev/null
+++ b/test/camunda-cloud/zeebe-user-task.spec.js
@@ -0,0 +1,52 @@
+const RuleTester = require('bpmnlint/lib/testers/rule-tester');
+
+const rule = require('../../rules/camunda-cloud/zeebe-user-task');
+
+const {
+ createModdle,
+ createProcess,
+} = require('../helper');
+
+const { ERROR_TYPES } = require('../../rules/utils/element');
+
+const valid = [
+ {
+ name: 'UserTask with one zeebe:UserTask extension element',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+ `))
+ }
+];
+
+const invalid = [
+ {
+ name: 'UserTask with no zeebe:UserTask extension element (invalid if required)',
+ moddleElement: createModdle(createProcess(`
+
+
+
+
+
+ `)),
+ report: {
+ id: 'UserTask_5',
+ message: 'Element of type must have one extension element of type ',
+ path: [],
+ data: {
+ type: ERROR_TYPES.EXTENSION_ELEMENT_REQUIRED,
+ node: 'UserTask_5',
+ parentNode: null,
+ requiredExtensionElement: 'zeebe:UserTask'
+ }
+ }
+ }
+];
+
+RuleTester.verify('zeebe-user-task', rule, {
+ valid,
+ invalid
+});
diff --git a/test/config/configs.spec.js b/test/config/configs.spec.js
index 290536b..8e97d75 100644
--- a/test/config/configs.spec.js
+++ b/test/config/configs.spec.js
@@ -385,6 +385,7 @@ describe('configs', function() {
'no-multiple-none-start-events' : [ 'error', { version: '8.6' } ],
'no-task-listeners': [ 'error', { version: '8.6' } ],
'priority-definition': [ 'error', { version: '8.6' } ],
+ 'zeebe-user-task': [ 'warn', { version: '8.6' } ],
'secrets': [ 'warn', { version: '8.6' } ],
'sequence-flow-condition': [ 'error', { version: '8.6' } ],
'signal-reference': [ 'error', { version: '8.6' } ],
@@ -420,6 +421,7 @@ describe('configs', function() {
'no-loop': [ 'error', { version: '8.7' } ],
'no-multiple-none-start-events' : [ 'error', { version: '8.7' } ],
'priority-definition': [ 'error', { version: '8.7' } ],
+ 'zeebe-user-task': [ 'warn', { version: '8.7' } ],
'secrets': [ 'warn', { version: '8.7' } ],
'sequence-flow-condition': [ 'error', { version: '8.7' } ],
'signal-reference': [ 'error', { version: '8.7' } ],
@@ -495,6 +497,7 @@ describe('configs', function() {
'no-zeebe-properties': 'error',
'no-zeebe-user-task': 'error',
'priority-definition': 'error',
+ 'zeebe-user-task': 'warn',
'secrets': 'warn',
'sequence-flow-condition': 'error',
'signal-reference': 'error',