From b934340576656ad5c60db0fa68874e62f1b16f3d Mon Sep 17 00:00:00 2001 From: Jerome Simeon Date: Thu, 8 Apr 2021 14:39:40 -0400 Subject: [PATCH] feature(validator) Add optional UTC offset parameter to validation Signed-off-by: Jerome Simeon --- package-lock.json | 6 +++--- package.json | 2 +- packages/concerto-core/api.txt | 4 ++-- packages/concerto-core/changelog.txt | 3 +++ packages/concerto-core/lib/serializer.js | 10 +++++++--- .../concerto-core/lib/serializer/jsongenerator.js | 10 +++++++--- .../concerto-core/lib/serializer/jsonpopulator.js | 14 +++++++++++--- .../lib/serializer/resourcevalidator.js | 3 +-- packages/concerto-core/package.json | 2 +- 9 files changed, 36 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95d781c034..87522175f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3081,9 +3081,9 @@ "dev": true }, "dayjs": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.2.tgz", - "integrity": "sha512-h/YtykNNTR8Qgtd1Fxl5J1/SFP1b7SOk/M1P+Re+bCdFMV0IMkuKNgHPN7rlvvuhfw24w0LX78iYKt4YmePJNQ==", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz", + "integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==", "dev": true }, "debug": { diff --git a/package.json b/package.json index a299c13998..0c5a9fb1fc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "devDependencies": { "colors": "1.4.0", "coveralls": "3.0.4", - "dayjs": "1.10.2", + "dayjs": "1.10.4", "eslint": "6.0.1", "istanbul-combine": "0.3.0", "istanbul-merge": "1.1.1", diff --git a/packages/concerto-core/api.txt b/packages/concerto-core/api.txt index 936e4076b9..ffd11542ae 100644 --- a/packages/concerto-core/api.txt +++ b/packages/concerto-core/api.txt @@ -223,7 +223,7 @@ class SecurityException extends BaseException { class Serializer { + void constructor(Factory,ModelManager) + void setDefaultOptions(Object) - + Object toJSON(Resource,Object,boolean,boolean,boolean,boolean,boolean) throws Error - + Resource fromJSON(Object,Object,boolean,boolean) + + Object toJSON(Resource,Object,boolean,boolean,boolean,boolean,boolean,number) throws Error + + Resource fromJSON(Object,Object,boolean,boolean,number) + boolean hasInstance(object) } diff --git a/packages/concerto-core/changelog.txt b/packages/concerto-core/changelog.txt index c392effb22..fedc673fe1 100644 --- a/packages/concerto-core/changelog.txt +++ b/packages/concerto-core/changelog.txt @@ -24,6 +24,9 @@ # Note that the latest public API is documented using JSDocs and is available in api.txt. # +Version 1.0.0-alpha.4 {3b7ebc06a536a5e81624225c7b0079ee} 2021-04-08 +- Allow optional UTC offset for validating DateTime values + Version 1.0.0-alpha.3 {7b8763c243f937d84579350aef348c3c} 2020-12-25 - Concepts may now be identified - assets/participants have a system identifier created ($identifier) automatically diff --git a/packages/concerto-core/lib/serializer.js b/packages/concerto-core/lib/serializer.js index 2538527cce..12dd389a20 100644 --- a/packages/concerto-core/lib/serializer.js +++ b/packages/concerto-core/lib/serializer.js @@ -27,7 +27,8 @@ const TypedStack = require('./serializer/typedstack'); const baseDefaultOptions = { validate: true, - ergo: false + ergo: false, + utcOffset: 0, }; /** @@ -85,6 +86,7 @@ class Serializer { * serialized in full, subsequent instances are replaced with a reference to the $id * @param {boolean} [options.convertResourcesToId] - Convert resources that * are specified for relationship fields into their id, false by default. + * @param {number} [options.utcOffset] - UTC Offset for DateTime values. * @return {Object} - The Javascript Object that represents the resource * @throws {Error} - throws an exception if resource is not an instance of * Resource or fails validation. @@ -114,7 +116,8 @@ class Serializer { options.permitResourcesForRelationships === true, options.deduplicateResources === true, options.convertResourcesToId === true, - options.ergo === true + options.ergo === true, + options.utcOffset, ); parameters.stack.clear(); @@ -138,6 +141,7 @@ class Serializer { * in the place of strings for relationships, defaults to false. * @param {boolean} options.validate - validate the structure of the Resource * with its model prior to serialization (default to true) + * @param {number} [options.utcOffset] - UTC Offset for DateTime values. * @return {Resource} The new populated resource */ fromJSON(jsonObject, options) { @@ -180,7 +184,7 @@ class Serializer { parameters.resourceStack = new TypedStack(resource); parameters.modelManager = this.modelManager; parameters.factory = this.factory; - const populator = new JSONPopulator(options.acceptResourcesForRelationships === true, options.ergo === true); + const populator = new JSONPopulator(options.acceptResourcesForRelationships === true, options.ergo === true, options.utcOffset); classDeclaration.accept(populator, parameters); // validate the resource against the model diff --git a/packages/concerto-core/lib/serializer/jsongenerator.js b/packages/concerto-core/lib/serializer/jsongenerator.js index ba0d068833..0e315e6290 100644 --- a/packages/concerto-core/lib/serializer/jsongenerator.js +++ b/packages/concerto-core/lib/serializer/jsongenerator.js @@ -46,13 +46,15 @@ class JSONGenerator { * @param {boolean} [convertResourcesToId] Convert resources that * are specified for relationship fields into their id, false by default. * @param {boolean} [ergo] target ergo. + * @param {number} [utcOffset] UTC Offset for DateTime values. */ - constructor(convertResourcesToRelationships, permitResourcesForRelationships, deduplicateResources, convertResourcesToId, ergo) { + constructor(convertResourcesToRelationships, permitResourcesForRelationships, deduplicateResources, convertResourcesToId, ergo, utcOffset) { this.convertResourcesToRelationships = convertResourcesToRelationships; this.permitResourcesForRelationships = permitResourcesForRelationships; this.deduplicateResources = deduplicateResources; this.convertResourcesToId = convertResourcesToId; this.ergo = ergo; + this.utcOffset = utcOffset || 0; // Defaults to UTC } /** @@ -193,10 +195,12 @@ class JSONGenerator { switch (field.getType()) { case 'DateTime': { + const objWithOffset = obj.utc().utcOffset(this.utcOffset); if (this.ergo) { - return obj; + return objWithOffset; } else { - return obj.utc().format('YYYY-MM-DDTHH:mm:ss.SSS[Z]'); + const inZ = objWithOffset.utcOffset() === 0; + return objWithOffset.format(`YYYY-MM-DDTHH:mm:ss.SSS${inZ ? '[Z]': 'Z'}`); } } case 'Integer': diff --git a/packages/concerto-core/lib/serializer/jsonpopulator.js b/packages/concerto-core/lib/serializer/jsonpopulator.js index 81fe85741d..3d79c54ebf 100644 --- a/packages/concerto-core/lib/serializer/jsonpopulator.js +++ b/packages/concerto-core/lib/serializer/jsonpopulator.js @@ -24,6 +24,12 @@ const ValidationException = require('./validationexception'); const dayjs = require('dayjs'); const utc = require('dayjs/plugin/utc'); dayjs.extend(utc); +const quarterOfYear = require('dayjs/plugin/quarterOfYear'); +dayjs.extend(quarterOfYear); +const minMax = require('dayjs/plugin/minMax'); +dayjs.extend(minMax); +const duration = require('dayjs/plugin/duration'); +dayjs.extend(duration); /** * Check if a given property name is a system property, e.g. '$class'. @@ -83,10 +89,12 @@ class JSONPopulator { * @param {boolean} [acceptResourcesForRelationships] Permit resources in the * place of relationships, false by default. * @param {boolean} [ergo] target ergo. + * @param {number} [utcOffset] - UTC Offset for DateTime values. */ - constructor(acceptResourcesForRelationships, ergo) { + constructor(acceptResourcesForRelationships, ergo, utcOffset) { this.acceptResourcesForRelationships = acceptResourcesForRelationships; this.ergo = ergo; + this.utcOffset = utcOffset || 0; // Defaults to UTC } /** @@ -226,12 +234,12 @@ class JSONPopulator { switch(field.getType()) { case 'DateTime': { - if (dayjs.isDayjs(json)) { + if (json && typeof json === 'object' && typeof json.isBefore === 'function') { result = json; } else if (typeof json !== 'string') { throw new ValidationException(`Expected value ${JSON.stringify(json)} to be of type ${field.getType()}`); } else { - result = dayjs.utc(json); + result = dayjs.utc(json).utcOffset(this.utcOffset); } if (!result.isValid()) { throw new ValidationException(`Expected value ${JSON.stringify(json)} to be of type ${field.getType()}`); diff --git a/packages/concerto-core/lib/serializer/resourcevalidator.js b/packages/concerto-core/lib/serializer/resourcevalidator.js index 676973c9a3..0a8319da97 100644 --- a/packages/concerto-core/lib/serializer/resourcevalidator.js +++ b/packages/concerto-core/lib/serializer/resourcevalidator.js @@ -25,7 +25,6 @@ const Util = require('../util'); const ModelUtil = require('../modelutil'); const ValidationException = require('./validationexception'); const Globalize = require('../globalize'); -const dayjs = require('dayjs'); /** *

@@ -315,7 +314,7 @@ class ResourceValidator { } break; case 'DateTime': - if(!(dayjs.isDayjs(obj))) { + if(!(typeof obj === 'object' && typeof obj.isBefore === 'function')) { invalid = true; } break; diff --git a/packages/concerto-core/package.json b/packages/concerto-core/package.json index 58f7a173f9..0ef3ff76dc 100644 --- a/packages/concerto-core/package.json +++ b/packages/concerto-core/package.json @@ -65,7 +65,7 @@ "dependencies": { "axios": "0.21.1", "colors": "1.4.0", - "dayjs": "1.10.2", + "dayjs": "1.10.4", "debug": "4.1.1", "jsome": "2.5.0", "lorem-ipsum": "1.0.6",