From 07b580e8a984f0240d9735086bca33c567bf65b0 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Mon, 9 Dec 2024 17:53:06 +0100 Subject: [PATCH] fix: implement validation for both element templates versions test: verify features across both element template versions --- src/cloud-element-templates/Validator.js | 44 +++++++--- .../ElementTemplates.engines-templates.json | 10 +-- .../ElementTemplates.spec.js | 25 +++++- .../cloud-element-templates/Validator.spec.js | 37 +++++++++ .../fixtures/engines-invalid.json | 16 ++++ .../fixtures/engines.json | 23 +----- .../fixtures/falsy-version.json | 10 +++ .../ElementTemplates.spec.js | 34 ++++++++ test/spec/element-templates/Validator.spec.js | 1 + .../fixtures/engines-invalid.json | 1 - .../element-templates/fixtures/engines.json | 81 ++++++++++++++++++- 11 files changed, 243 insertions(+), 39 deletions(-) create mode 100644 test/spec/cloud-element-templates/fixtures/engines-invalid.json create mode 100644 test/spec/cloud-element-templates/fixtures/falsy-version.json diff --git a/src/cloud-element-templates/Validator.js b/src/cloud-element-templates/Validator.js index a2b95189..8b51d3ea 100644 --- a/src/cloud-element-templates/Validator.js +++ b/src/cloud-element-templates/Validator.js @@ -6,11 +6,16 @@ import { import semverCompare from 'semver-compare'; +import { + validRange as isSemverRangeValid +} from 'semver'; + import { validateZeebe as validateAgainstSchema, getZeebeSchemaPackage as getTemplateSchemaPackage, getZeebeSchemaVersion as getTemplateSchemaVersion } from '@bpmn-io/element-templates-validator'; +import { forEach } from 'min-dash'; const SUPPORTED_SCHEMA_VERSION = getTemplateSchemaVersion(); const SUPPORTED_SCHEMA_PACKAGE = getTemplateSchemaPackage(); @@ -31,8 +36,6 @@ export class Validator extends BaseValidator { * @return {Error} validation error, if any */ _validateTemplate(template) { - let err; - const id = template.id, version = template.version || '_', schema = template.$schema, @@ -78,25 +81,48 @@ export class Validator extends BaseValidator { } // (5) JSON schema compliance - const validationResult = validateAgainstSchema(template); + const schemaValidationResult = validateAgainstSchema(template); const { - errors, + errors: schemaErrors, valid - } = validationResult; + } = schemaValidationResult; if (!valid) { - err = new Error('invalid template'); - - filteredSchemaErrors(errors).forEach((error) => { + filteredSchemaErrors(schemaErrors).forEach((error) => { this._logError(error.message, template); }); + + return new Error('invalid template'); } - return err; + // (6) engines validation + const enginesError = this._validateEngines(template); + + if (enginesError) { + return enginesError; + } + + return null; } isSchemaValid(schema) { return schema && schema.includes(SUPPORTED_SCHEMA_PACKAGE); } + + _validateEngines(template) { + + let err; + + forEach(template.engines, (rangeStr, engine) => { + + if (!isSemverRangeValid(rangeStr)) { + err = this._logError(new Error( + `Engine <${engine}> specifies invalid semver range <${rangeStr}>` + ), template); + } + }); + + return err; + } } diff --git a/test/spec/cloud-element-templates/ElementTemplates.engines-templates.json b/test/spec/cloud-element-templates/ElementTemplates.engines-templates.json index 688fe380..da5ffbb9 100644 --- a/test/spec/cloud-element-templates/ElementTemplates.engines-templates.json +++ b/test/spec/cloud-element-templates/ElementTemplates.engines-templates.json @@ -2,7 +2,7 @@ { "id": "example.engines.test.multiple", "name": " Test - Multiple", - "description": "does not match , if explicit =1> is provided", + "description": "does not match if { desktopModeler: >=1 } is provided", "version": 2, "engines": { "camunda": "^8.6", @@ -17,7 +17,7 @@ { "id": "example.engines.test.multiple", "name": " Test - Multiple", - "description": "matches when compatible and/or run-time is indicated, or properties are not provided", + "description": "matches if { camunda: ^8.6, webModeler: ^4.1 } engine is indicated, or properties are not provided", "version": 1, "engines": { "camunda": "^8.6", @@ -32,7 +32,7 @@ { "id": "example.engines.test.basic", "name": " Test - Basic", - "description": "matches when compatible run-time is indicated, or property is not provided", + "description": "matches if { camunda: ^8.6 }, or if no engine is provided", "version": 3, "engines": { "camunda": "^8.6" @@ -45,7 +45,7 @@ { "id": "example.engines.test.basic", "name": " Test - Basic", - "description": "matches when compatible run-time is indicated, or property is not provided", + "description": "matches if { camunda: ^8.5 }, or if no engine is provided", "version": 2, "engines": { "camunda": "^8.5" @@ -72,7 +72,7 @@ "description": "specifies broken semver range", "version": 1, "engines": { - "camunda": "-foobar" + "camunda": "invalid-version" }, "appliesTo": [ "bpmn:Task" diff --git a/test/spec/cloud-element-templates/ElementTemplates.spec.js b/test/spec/cloud-element-templates/ElementTemplates.spec.js index 1ed52f78..0ba6e88d 100644 --- a/test/spec/cloud-element-templates/ElementTemplates.spec.js +++ b/test/spec/cloud-element-templates/ElementTemplates.spec.js @@ -27,6 +27,7 @@ import messageTemplates from './ElementTemplates.message-templates.json'; import enginesTemplates from './ElementTemplates.engines-templates.json'; import templates from './fixtures/simple'; +import falsyVersionTemplate from './fixtures/falsy-version'; import complexTemplates from './fixtures/complex'; import integrationTemplates from './fixtures/integration'; import { findExtensions, findExtension } from 'src/cloud-element-templates/Helper'; @@ -728,6 +729,26 @@ describe('provider/cloud-element-templates - ElementTemplates', function() { describe('set', function() { + it('should set templates', inject(function(elementTemplates) { + + // when + elementTemplates.set(templates.slice(0, 3)); + + // then + expect(elementTemplates.getAll()).to.have.length(3); + })); + + + it('should not ignore version set to 0', inject(function(elementTemplates) { + + // when + elementTemplates.set(falsyVersionTemplate); + + // then + expect(elementTemplates.get(falsyVersionTemplate[0].id, 0)).to.exist; + })); + + it('should emit event', inject(function(elementTemplates, eventBus) { // given @@ -739,7 +760,7 @@ describe('provider/cloud-element-templates - ElementTemplates', function() { elementTemplates.set(templates); // then - expect(spy).to.have.been.called; + expect(spy).to.have.been.calledOnce; })); }); @@ -758,7 +779,7 @@ describe('provider/cloud-element-templates - ElementTemplates', function() { elementTemplates.setEngines({}); // then - expect(spy).to.have.been.called; + expect(spy).to.have.been.calledOnce; })); }); diff --git a/test/spec/cloud-element-templates/Validator.spec.js b/test/spec/cloud-element-templates/Validator.spec.js index cf5d5b30..1b2a8e8e 100644 --- a/test/spec/cloud-element-templates/Validator.spec.js +++ b/test/spec/cloud-element-templates/Validator.spec.js @@ -586,4 +586,41 @@ describe('provider/cloud-element-templates - Validator', function() { }); + describe('engines validation', function() { + + it('should accept template with valid semver range', function() { + + // given + const templates = new Validator(moddle); + + const templateDescriptor = require('./fixtures/engines'); + + // when + templates.addAll(templateDescriptor); + + // then + expect(errors(templates)).to.be.empty; + + expect(valid(templates)).to.have.length(templateDescriptor.length); + }); + + + it('should reject template with invalid semver range', function() { + + // given + const templates = new Validator(moddle); + + const templateDescriptor = require('./fixtures/engines-invalid'); + + // when + templates.addAll(templateDescriptor); + + // then + expect(errors(templates)).to.contain('Engine specifies invalid semver range '); + + expect(valid(templates)).to.be.empty; + }); + + }); + }); diff --git a/test/spec/cloud-element-templates/fixtures/engines-invalid.json b/test/spec/cloud-element-templates/fixtures/engines-invalid.json new file mode 100644 index 00000000..540001e3 --- /dev/null +++ b/test/spec/cloud-element-templates/fixtures/engines-invalid.json @@ -0,0 +1,16 @@ +[ + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "id": "example.engines.test.broken", + "name": " Test - broken Semver range", + "description": "specifies broken semver range", + "version": 1, + "engines": { + "camunda": "invalid-version" + }, + "appliesTo": [ + "bpmn:Task" + ], + "properties": [] + } +] \ No newline at end of file diff --git a/test/spec/cloud-element-templates/fixtures/engines.json b/test/spec/cloud-element-templates/fixtures/engines.json index 67cd1568..c525f144 100644 --- a/test/spec/cloud-element-templates/fixtures/engines.json +++ b/test/spec/cloud-element-templates/fixtures/engines.json @@ -3,7 +3,7 @@ "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "id": "example.engines.test.multiple", "name": " Test - Multiple", - "description": "does not match , if explicit =1> is provided", + "description": "does not match if { desktopModeler: >=1 } is provided", "version": 2, "engines": { "camunda": "^8.6", @@ -19,7 +19,7 @@ "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "id": "example.engines.test.multiple", "name": " Test - Multiple", - "description": "matches when compatible and/or run-time is indicated, or properties are not provided", + "description": "matches if { camunda: ^8.6, webModeler: ^4.1 } engine is indicated, or properties are not provided", "version": 1, "engines": { "camunda": "^8.6", @@ -35,7 +35,7 @@ "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "id": "example.engines.test.basic", "name": " Test - Basic", - "description": "matches when compatible run-time is indicated, or property is not provided", + "description": "matches if { camunda: ^8.6 }, or if no engine is provided", "version": 3, "engines": { "camunda": "^8.6" @@ -49,7 +49,7 @@ "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "id": "example.engines.test.basic", "name": " Test - Basic", - "description": "matches when compatible run-time is indicated, or property is not provided", + "description": "matches if { camunda: ^8.5 }, or if no engine is provided", "version": 2, "engines": { "camunda": "^8.5" @@ -71,21 +71,6 @@ "properties": [] }, - { - "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", - "id": "example.engines.test.broken", - "name": " Test - broken Semver range", - "description": "specifies broken semver range", - "version": 1, - "engines": { - "camunda": "-foobar" - }, - "appliesTo": [ - "bpmn:Task" - ], - "properties": [] - }, - { "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "id": "example.engines.test.incompatible", diff --git a/test/spec/cloud-element-templates/fixtures/falsy-version.json b/test/spec/cloud-element-templates/fixtures/falsy-version.json new file mode 100644 index 00000000..86d6fd75 --- /dev/null +++ b/test/spec/cloud-element-templates/fixtures/falsy-version.json @@ -0,0 +1,10 @@ +[ + { + "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "id": "foo", + "name":"Foo 1", + "version": 0, + "appliesTo": [ "bpmn:Task" ], + "properties": [] + } +] \ No newline at end of file diff --git a/test/spec/element-templates/ElementTemplates.spec.js b/test/spec/element-templates/ElementTemplates.spec.js index cd105620..dc0473ea 100644 --- a/test/spec/element-templates/ElementTemplates.spec.js +++ b/test/spec/element-templates/ElementTemplates.spec.js @@ -378,6 +378,40 @@ describe('provider/element-templates - ElementTemplates', function() { expect(elementTemplates.get(falsyVersionTemplate[0].id, 0)).to.exist; })); + + it('should emit event', inject(function(elementTemplates, eventBus) { + + // given + const spy = sinon.spy(); + + eventBus.on('elementTemplates.changed', spy); + + // when + elementTemplates.set(templates); + + // then + expect(spy).to.have.been.calledOnce; + })); + + }); + + + describe('setEngines', function() { + + it('should emit event', inject(function(elementTemplates, eventBus) { + + // given + const spy = sinon.spy(); + + eventBus.on('elementTemplates.engines.changed', spy); + + // when + elementTemplates.setEngines({}); + + // then + expect(spy).to.have.been.calledOnce; + })); + }); diff --git a/test/spec/element-templates/Validator.spec.js b/test/spec/element-templates/Validator.spec.js index 0f1fe13d..817f41fc 100644 --- a/test/spec/element-templates/Validator.spec.js +++ b/test/spec/element-templates/Validator.spec.js @@ -712,6 +712,7 @@ describe('provider/element-templates - Validator', function() { }); + describe('engines validation', function() { it('should accept template with valid semver range', function() { diff --git a/test/spec/element-templates/fixtures/engines-invalid.json b/test/spec/element-templates/fixtures/engines-invalid.json index 2de0ff4e..a01cce2f 100644 --- a/test/spec/element-templates/fixtures/engines-invalid.json +++ b/test/spec/element-templates/fixtures/engines-invalid.json @@ -1,6 +1,5 @@ [ { - "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", "id": "example.engines.test.basic", "name": " Test - Basic", "description": "basic template with invalid engines", diff --git a/test/spec/element-templates/fixtures/engines.json b/test/spec/element-templates/fixtures/engines.json index 900523a6..03e5b3e5 100644 --- a/test/spec/element-templates/fixtures/engines.json +++ b/test/spec/element-templates/fixtures/engines.json @@ -1,16 +1,91 @@ [ { - "$schema": "https://unpkg.com/@camunda/zeebe-element-templates-json-schema/resources/schema.json", + "id": "example.engines.test.multiple", + "name": " Test - Multiple", + "description": "does not match if { desktopModeler: >=1 } is provided", + "version": 2, + "engines": { + "camunda": "^7.13", + "webModeler": "^4.1", + "desktopModeler": "^0" + }, + "appliesTo": [ + "bpmn:Task" + ], + "properties": [] + }, + { + "id": "example.engines.test.multiple", + "name": " Test - Multiple", + "description": "matches if { camunda: ^7.13, webModeler: ^4.1 } engine is indicated, or properties are not provided", + "version": 1, + "engines": { + "camunda": "^7.13", + "webModeler": "^4.1" + }, + "appliesTo": [ + "bpmn:Task" + ], + "properties": [] + }, + + { "id": "example.engines.test.basic", "name": " Test - Basic", - "description": "basic template with valid engines", + "description": "matches if { camunda: ^7.13 }, or if no engine is provided", "version": 3, "engines": { - "camunda": "^8.6" + "camunda": "^7.13" + }, + "appliesTo": [ + "bpmn:Task" + ], + "properties": [] + }, + { + "id": "example.engines.test.basic", + "name": " Test - Basic", + "description": "matches if { camunda: <=7.12 }, or if no engine is provided", + "version": 2, + "engines": { + "camunda": "<=7.12" }, "appliesTo": [ "bpmn:Task" ], "properties": [] + }, + { + "id": "example.engines.test.basic", + "name": " Test - Basic", + "description": "always matches", + "version": 1, + "appliesTo": [ + "bpmn:Task" + ], + "properties": [] + }, + + { + "id": "example.engines.test.incompatible", + "name": " Test - incompatible", + "description": "incompatible with all camunda versions", + "engines": { + "camunda": "0" + }, + "appliesTo": [ + "bpmn:Task" + ], + "properties": [ + { + "label": "Custom Property 2", + "type": "String", + "value": "property-2-value", + "binding": { + "type": "camunda:property", + "key": "property-2-key" + } + } + ] } ] \ No newline at end of file