diff --git a/sdflint/sdf-validation.jso.json b/sdflint/sdf-validation.jso.json index 87c7d99..acd5f84 100644 --- a/sdflint/sdf-validation.jso.json +++ b/sdflint/sdf-validation.jso.json @@ -1,13 +1,10 @@ { - "title": "sdf-validation.cddl", + "title": "sdf-validation.cddl -- Generated: 2024-02-29T07:42:35Z", "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/sdf-syntax", "definitions": { "sdf-syntax": { "type": "object", - "required": [ - "info" - ], "properties": { "info": { "$ref": "#/definitions/sdfinfo" @@ -27,12 +24,6 @@ "$ref": "#/definitions/thingqualities" } }, - "sdfProduct": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/productqualities" - } - }, "sdfObject": { "type": "object", "additionalProperties": { @@ -40,44 +31,29 @@ } }, "sdfProperty": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/propertyqualities" - } + "$ref": "#/definitions/sdfProperty-" }, "sdfAction": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/actionqualities" - } + "$ref": "#/definitions/sdfAction-" }, "sdfEvent": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/eventqualities" - } + "$ref": "#/definitions/sdfEvent-" }, "sdfData": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } + "$ref": "#/definitions/sdfData-sdfChoice-properties-" } }, "additionalProperties": false }, "sdfinfo": { "type": "object", - "required": [ - "title", - "version", - "copyright", - "license" - ], "properties": { "title": { "type": "string" }, + "description": { + "type": "string" + }, "version": { "type": "string" }, @@ -86,10 +62,23 @@ }, "license": { "type": "string" + }, + "modified": { + "$ref": "#/definitions/modified-date-time" + }, + "features": { + "type": "array", + "maxItems": 0 + }, + "$comment": { + "type": "string" } }, "additionalProperties": false }, + "modified-date-time": { + "type": "string" + }, "thingqualities": { "type": "object", "properties": { @@ -119,12 +108,55 @@ "additionalProperties": { "$ref": "#/definitions/thingqualities" } + }, + "sdfProperty": { + "$ref": "#/definitions/sdfProperty-" + }, + "sdfAction": { + "$ref": "#/definitions/sdfAction-" + }, + "sdfEvent": { + "$ref": "#/definitions/sdfEvent-" + }, + "sdfData": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "minItems": { + "$ref": "#/definitions/uint" + }, + "maxItems": { + "$ref": "#/definitions/uint" } }, "additionalProperties": false }, "sdf-pointer": { - "type": "string" + "anyOf": [ + { + "$ref": "#/definitions/global" + }, + { + "$ref": "#/definitions/same-object" + }, + { + "$ref": "#/definitions/true" + } + ] + }, + "global": { + "type": "string", + "pattern": "^[^\\n\\r]*[:#][^\\n\\r]*$" + }, + "same-object": { + "$ref": "#/definitions/referenceable-name" + }, + "referenceable-name": { + "type": "string", + "pattern": "^[^:#]*$" + }, + "true": { + "type": "boolean", + "const": true }, "pointer-list": { "type": "array", @@ -151,56 +183,60 @@ "$ref": "#/definitions/pointer-list" }, "sdfProperty": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/propertyqualities" - } + "$ref": "#/definitions/sdfProperty-" }, "sdfAction": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/actionqualities" - } + "$ref": "#/definitions/sdfAction-" }, "sdfEvent": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/eventqualities" - } + "$ref": "#/definitions/sdfEvent-" }, "sdfData": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "minItems": { + "$ref": "#/definitions/uint" + }, + "maxItems": { + "$ref": "#/definitions/uint" } }, "additionalProperties": false }, "propertyqualities": { - "$ref": "#/definitions/dataqualities" - }, - "dataqualities": { "anyOf": [ { "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "number", - "string", - "boolean", - "integer", - "array" - ] + "$ref": "#/definitions/type-" }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 + "sdfChoice": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "observable": { + "type": "boolean" + }, + "readable": { + "type": "boolean" + }, + "writable": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" }, "const": { "$ref": "#/definitions/allowed-types" @@ -215,253 +251,46 @@ "type": "number" }, "exclusiveMinimum": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "number" - } - ] + "type": "number" }, "exclusiveMaximum": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "number" - } - ] + "type": "number" }, "multipleOf": { "type": "number" }, "minLength": { - "type": "number" + "$ref": "#/definitions/uint" }, "maxLength": { - "type": "number" + "$ref": "#/definitions/uint" }, "pattern": { "type": "string" }, "format": { - "type": "string", - "enum": [ - "date-time", - "date", - "time", - "uri", - "uri-reference", - "uuid" - ] + "$ref": "#/definitions/format-" }, "minItems": { - "type": "number" + "$ref": "#/definitions/uint" }, "maxItems": { - "type": "number" + "$ref": "#/definitions/uint" }, "uniqueItems": { "type": "boolean" }, "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "number", - "string", - "boolean", - "integer" - ] - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "object" - }, - "required": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "sdfChoice": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - } - ] - }, - "description": { - "type": "string" - }, - "label": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "sdfRequired": { - "$ref": "#/definitions/pointer-list" + "$ref": "#/definitions/jso-items" }, "unit": { "type": "string" }, - "scaleMinimum": { - "type": "number" - }, - "scaleMaximum": { - "type": "number" - }, - "observable": { - "type": "boolean" - }, - "readable": { - "type": "boolean" - }, - "writable": { - "type": "boolean" - }, "nullable": { "type": "boolean" }, - "subtype": { - "type": "string", - "enum": [ - "byte-string", - "unix-time" - ] - }, "sdfType": { - "type": "string", - "enum": [ - "byte-string", - "unix-time" - ] + "$ref": "#/definitions/sdfType-" }, "contentFormat": { "type": "string" @@ -484,17 +313,34 @@ "minItems": 1 }, "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } + "$ref": "#/definitions/sdfData-sdfChoice-properties-" }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 + "sdfChoice": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "observable": { + "type": "boolean" + }, + "readable": { + "type": "boolean" + }, + "writable": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" }, "const": { "$ref": "#/definitions/allowed-types" @@ -509,253 +355,46 @@ "type": "number" }, "exclusiveMinimum": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "number" - } - ] + "type": "number" }, "exclusiveMaximum": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "number" - } - ] + "type": "number" }, "multipleOf": { "type": "number" }, "minLength": { - "type": "number" + "$ref": "#/definitions/uint" }, "maxLength": { - "type": "number" + "$ref": "#/definitions/uint" }, "pattern": { "type": "string" }, "format": { - "type": "string", - "enum": [ - "date-time", - "date", - "time", - "uri", - "uri-reference", - "uuid" - ] + "$ref": "#/definitions/format-" }, "minItems": { - "type": "number" + "$ref": "#/definitions/uint" }, "maxItems": { - "type": "number" + "$ref": "#/definitions/uint" }, "uniqueItems": { "type": "boolean" }, "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "number", - "string", - "boolean", - "integer" - ] - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "object" - }, - "required": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "sdfChoice": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - } - ] - }, - "description": { - "type": "string" - }, - "label": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "sdfRequired": { - "$ref": "#/definitions/pointer-list" + "$ref": "#/definitions/jso-items" }, "unit": { "type": "string" }, - "scaleMinimum": { - "type": "number" - }, - "scaleMaximum": { - "type": "number" - }, - "observable": { - "type": "boolean" - }, - "readable": { - "type": "boolean" - }, - "writable": { - "type": "boolean" - }, "nullable": { "type": "boolean" }, - "subtype": { - "type": "string", - "enum": [ - "byte-string", - "unix-time" - ] - }, "sdfType": { - "type": "string", - "enum": [ - "byte-string", - "unix-time" - ] + "$ref": "#/definitions/sdfType-" }, "contentFormat": { "type": "string" @@ -766,11 +405,8 @@ { "type": "object", "properties": { - "sdfChoice": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } + "type": { + "$ref": "#/definitions/type-" }, "enum": { "type": "array", @@ -779,6 +415,30 @@ }, "minItems": 1 }, + "observable": { + "type": "boolean" + }, + "readable": { + "type": "boolean" + }, + "writable": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" + }, "const": { "$ref": "#/definitions/allowed-types" }, @@ -792,253 +452,524 @@ "type": "number" }, "exclusiveMinimum": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "number" - } - ] + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "multipleOf": { + "type": "number" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + }, + "pattern": { + "type": "string" + }, + "format": { + "$ref": "#/definitions/format-" + }, + "minItems": { + "$ref": "#/definitions/uint" + }, + "maxItems": { + "$ref": "#/definitions/uint" + }, + "uniqueItems": { + "type": "boolean" + }, + "items": { + "$ref": "#/definitions/jso-items" + }, + "unit": { + "type": "string" + }, + "nullable": { + "type": "boolean" + }, + "sdfType": { + "$ref": "#/definitions/sdfType-" + }, + "contentFormat": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "object" + }, + "required": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "properties": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "observable": { + "type": "boolean" + }, + "readable": { + "type": "boolean" + }, + "writable": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" + }, + "const": { + "$ref": "#/definitions/allowed-types" + }, + "default": { + "$ref": "#/definitions/allowed-types" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "multipleOf": { + "type": "number" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + }, + "pattern": { + "type": "string" + }, + "format": { + "$ref": "#/definitions/format-" + }, + "minItems": { + "$ref": "#/definitions/uint" + }, + "maxItems": { + "$ref": "#/definitions/uint" + }, + "uniqueItems": { + "type": "boolean" + }, + "items": { + "$ref": "#/definitions/jso-items" + }, + "unit": { + "type": "string" + }, + "nullable": { + "type": "boolean" + }, + "sdfType": { + "$ref": "#/definitions/sdfType-" + }, + "contentFormat": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "dataqualities": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/type-" + }, + "sdfChoice": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" + }, + "const": { + "$ref": "#/definitions/allowed-types" + }, + "default": { + "$ref": "#/definitions/allowed-types" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "multipleOf": { + "type": "number" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + }, + "pattern": { + "type": "string" + }, + "format": { + "$ref": "#/definitions/format-" + }, + "minItems": { + "$ref": "#/definitions/uint" + }, + "maxItems": { + "$ref": "#/definitions/uint" + }, + "uniqueItems": { + "type": "boolean" + }, + "items": { + "$ref": "#/definitions/jso-items" + }, + "unit": { + "type": "string" + }, + "nullable": { + "type": "boolean" + }, + "sdfType": { + "$ref": "#/definitions/sdfType-" + }, + "contentFormat": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "object" + }, + "required": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "properties": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "sdfChoice": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" + }, + "const": { + "$ref": "#/definitions/allowed-types" + }, + "default": { + "$ref": "#/definitions/allowed-types" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "multipleOf": { + "type": "number" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + }, + "pattern": { + "type": "string" + }, + "format": { + "$ref": "#/definitions/format-" + }, + "minItems": { + "$ref": "#/definitions/uint" + }, + "maxItems": { + "$ref": "#/definitions/uint" + }, + "uniqueItems": { + "type": "boolean" + }, + "items": { + "$ref": "#/definitions/jso-items" + }, + "unit": { + "type": "string" + }, + "nullable": { + "type": "boolean" + }, + "sdfType": { + "$ref": "#/definitions/sdfType-" + }, + "contentFormat": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/type-" + }, + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" + }, + "const": { + "$ref": "#/definitions/allowed-types" + }, + "default": { + "$ref": "#/definitions/allowed-types" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "multipleOf": { + "type": "number" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + }, + "pattern": { + "type": "string" + }, + "format": { + "$ref": "#/definitions/format-" + }, + "minItems": { + "$ref": "#/definitions/uint" + }, + "maxItems": { + "$ref": "#/definitions/uint" + }, + "uniqueItems": { + "type": "boolean" + }, + "items": { + "$ref": "#/definitions/jso-items" + }, + "unit": { + "type": "string" + }, + "nullable": { + "type": "boolean" + }, + "sdfType": { + "$ref": "#/definitions/sdfType-" + }, + "contentFormat": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "object" + }, + "required": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "properties": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "description": { + "type": "string" + }, + "label": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "sdfRequired": { + "$ref": "#/definitions/pointer-list" + }, + "const": { + "$ref": "#/definitions/allowed-types" + }, + "default": { + "$ref": "#/definitions/allowed-types" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" }, "exclusiveMaximum": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "number" - } - ] + "type": "number" }, "multipleOf": { "type": "number" }, "minLength": { - "type": "number" + "$ref": "#/definitions/uint" }, "maxLength": { - "type": "number" + "$ref": "#/definitions/uint" }, "pattern": { "type": "string" }, "format": { - "type": "string", - "enum": [ - "date-time", - "date", - "time", - "uri", - "uri-reference", - "uuid" - ] + "$ref": "#/definitions/format-" }, "minItems": { - "type": "number" + "$ref": "#/definitions/uint" }, "maxItems": { - "type": "number" + "$ref": "#/definitions/uint" }, "uniqueItems": { "type": "boolean" }, "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "number", - "string", - "boolean", - "integer" - ] - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "object" - }, - "required": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "sdfChoice": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "description": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - } - }, - "additionalProperties": false - } - ] - }, - "description": { - "type": "string" - }, - "label": { - "type": "string" - }, - "$comment": { - "type": "string" - }, - "sdfRef": { - "$ref": "#/definitions/sdf-pointer" - }, - "sdfRequired": { - "$ref": "#/definitions/pointer-list" + "$ref": "#/definitions/jso-items" }, "unit": { "type": "string" }, - "scaleMinimum": { - "type": "number" - }, - "scaleMaximum": { - "type": "number" - }, - "observable": { - "type": "boolean" - }, - "readable": { - "type": "boolean" - }, - "writable": { - "type": "boolean" - }, "nullable": { "type": "boolean" }, - "subtype": { - "type": "string", - "enum": [ - "byte-string", - "unix-time" - ] - }, "sdfType": { - "type": "string", - "enum": [ - "byte-string", - "unix-time" - ] + "$ref": "#/definitions/sdfType-" }, "contentFormat": { "type": "string" @@ -1087,6 +1018,200 @@ } ] }, + "uint": { + "type": "integer", + "minimum": 0 + }, + "jso-items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "number", + "string", + "boolean", + "integer" + ] + }, + "sdfChoice": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "description": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "format": { + "type": "string" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "object" + }, + "required": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "properties": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "sdfChoice": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "description": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "format": { + "type": "string" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "number", + "string", + "boolean", + "integer" + ] + }, + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "description": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "format": { + "type": "string" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "object" + }, + "required": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "properties": { + "$ref": "#/definitions/sdfData-sdfChoice-properties-" + }, + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "sdfRef": { + "$ref": "#/definitions/sdf-pointer" + }, + "description": { + "type": "string" + }, + "$comment": { + "type": "string" + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "format": { + "type": "string" + }, + "minLength": { + "$ref": "#/definitions/uint" + }, + "maxLength": { + "$ref": "#/definitions/uint" + } + }, + "additionalProperties": false + } + ] + }, "actionqualities": { "type": "object", "properties": { @@ -1112,10 +1237,7 @@ "$ref": "#/definitions/parameter-list" }, "sdfData": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } + "$ref": "#/definitions/sdfData-sdfChoice-properties-" } }, "additionalProperties": false @@ -1145,16 +1267,62 @@ "$ref": "#/definitions/parameter-list" }, "sdfData": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataqualities" - } + "$ref": "#/definitions/sdfData-sdfChoice-properties-" } }, "additionalProperties": false }, - "productqualities": { - "$ref": "#/definitions/thingqualities" + "format-": { + "type": "string", + "enum": [ + "date-time", + "date", + "time", + "uri", + "uri-reference", + "uuid" + ] + }, + "sdfData-sdfChoice-properties-": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dataqualities" + } + }, + "type-": { + "type": "string", + "enum": [ + "number", + "string", + "boolean", + "integer", + "array" + ] + }, + "sdfAction-": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/actionqualities" + } + }, + "sdfProperty-": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/propertyqualities" + } + }, + "sdfEvent-": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/eventqualities" + } + }, + "sdfType-": { + "type": "string", + "enum": [ + "byte-string", + "unix-time" + ] } } } diff --git a/sdflint/sdflint.js b/sdflint/sdflint.js index 35dd9ea..e91c1c4 100644 --- a/sdflint/sdflint.js +++ b/sdflint/sdflint.js @@ -34,7 +34,7 @@ if (require.main === module) { /* run as stand-alone? */ let options = {}; let schemaFile; - process.argv.slice(3).forEach( (option) => { + process.argv.slice(3).forEach((option) => { let name = option.substring(0, option.indexOf("=")); let value = option.substring(option.indexOf("=") + 1); options[name] = value; @@ -48,15 +48,20 @@ if (require.main === module) { /* run as stand-alone? */ try { sdfFile = fs.readFileSync(inFile, { encoding: 'utf-8' }); + } catch (err) { + console.error("Can't read input SDF file: " + err.message); + process.exit(1); + } + try { schema = JSON.parse(fs.readFileSync(schemaFile, { encoding: 'utf-8' })); - } catch (err) { - res.errorCount++; - res.errors.parse = err.message; - } + } catch (err) { + console.error("Can't read schema file: " + err.message); + process.exit(1); + } sdfLint(sdfFile, schema, res, options); - console.dir(res, {depth: null}); + console.dir(res, { depth: null }); process.exit(res.errorCount); } @@ -68,7 +73,7 @@ if (require.main === module) { /* run as stand-alone? */ schema The filename of the JSON schema to use license The license string to accept as valid ` - ); + ); } } @@ -77,7 +82,7 @@ function fileNameCheck(fileName, res) { let fileNameRe = new RegExp(FILENAME_RE); let baseFileName = path.parse(fileName).base; - if (! baseFileName.match(fileNameRe)) { + if (!baseFileName.match(fileNameRe)) { res.errorCount++; res.errors.fileName = "File name " + baseFileName + " does not match " + FILENAME_RE; @@ -87,6 +92,11 @@ function fileNameCheck(fileName, res) { function validCharsCheck(sdfFile, res) { let sdfStr = JSON.stringify(sdfFile); + if (!sdfStr) { + res.errorCount++; + res.errors.file = "Invalid SDF file"; + return; + }; let invalidLoc = sdfStr.search(new RegExp(VALID_CHARS_RE)); if (invalidLoc != -1) { res.errorCount++; @@ -97,13 +107,13 @@ function validCharsCheck(sdfFile, res) { function licenseCheck(sdf, license, res) { - if (! sdf.info || ! sdf.info.license) { + if (!sdf.info || !sdf.info.license) { res.errorCount++; res.errors.license = "No license defined in model" } else if (sdf.info.license != license) { res.errorCount++; res.errors.license = "Model has license '" + sdf.info.license + - "' expected '" + license + "'"; + "' expected '" + license + "'"; } } @@ -112,25 +122,29 @@ function sdfLint(sdfFile, schema, res, options) { let ajv = new Ajv(AJV_OPTIONS); let sdf; - if (! res) { + if (!res) { res = { errorCount: 0, errors: {} }; } - if (! options) { + if (!options) { options = {}; } - try { - sdf = JSON.parse(sdfFile); - } catch (err) { - res.errorCount++; - res.errors.parse = err.message; + if (options.isJSON) { + sdf = sdfFile; /* already parsed as JSON */ + } else { + try { + sdf = JSON.parse(sdfFile); + } catch (err) { + res.errorCount++; + res.errors.parse = err.message; + } + validCharsCheck(sdfFile, res); } - validCharsCheck(sdfFile, res); - if (! schema) { + if (!schema) { schema = JSON.parse(fs.readFileSync( DEF_SCHEMA_FILE, { encoding: 'utf-8' })); } diff --git a/sdflint/tests/README.md b/sdflint/tests/README.md new file mode 100644 index 0000000..7eeb264 --- /dev/null +++ b/sdflint/tests/README.md @@ -0,0 +1,15 @@ +# Test cases for SDF schema and linter + +This folder contains a simple test program and test cases for testing if the SDF linter program and JSO schema catch various errors in SDF documents. + +The program `test-schema.js` takes at minimum two parameters: a (valid) SDF document used as basis for testing and a schema file used to validate the document. + +For example: `node test-schema.js sdfobject-test.sdf.json ../sdf-validation.jso.json` + +The `sdfobject-test.sdf.json` is an SDF document that exercises many of the features of SDF Object definition and is a good start for the SDF document used with testing. + +For schema file the default validation schema of the linter can be used with `../sdf-validation.jso.json`. + +In this mode the program changes one by one each quality in the SDF document first to a (semi random) integer value and then to a string value. If the linter does not detect this as an error, information about this is printed to console. For example `Missed change: info/title -> "FOOBAR"` indicates a change in the info block's title quality (that is in fact a change that is OK to not flag as error since title can be any string). + +If further parameters are given to the program, those are assumed to be JSON merge-patch documents that alter the base SDF document. After applying each merge-patch, validation is run and if no error is detected, information about this is printed (if error is detected, nothing is printed unless verbose mode is used). Set of example merge-patch documents are available in the `schema-errors` folder. This mode also needs to have the "merge-patch" [command line program](https://rubygems.org/gems/merge-patch/versions/0.1.0) installed ("gem install merge-patch"). diff --git a/sdflint/tests/schema-errors/misspell-type.json b/sdflint/tests/schema-errors/misspell-type.json new file mode 100644 index 0000000..4f9e163 --- /dev/null +++ b/sdflint/tests/schema-errors/misspell-type.json @@ -0,0 +1,12 @@ +{ + "sdfObject": { + "TestObject": { + "sdfProperty": { + "IntegerTestProperty": { + "type": null, + "typemisspelled" : "integer" + } + } + } + } +} \ No newline at end of file diff --git a/sdflint/tests/schema-errors/numeric-description.json b/sdflint/tests/schema-errors/numeric-description.json new file mode 100644 index 0000000..e50e64d --- /dev/null +++ b/sdflint/tests/schema-errors/numeric-description.json @@ -0,0 +1,11 @@ +{ + "sdfObject": { + "TestObject": { + "sdfProperty": { + "IntegerTestProperty": { + "description": 42 + } + } + } + } +} \ No newline at end of file diff --git a/sdflint/tests/sdfobject-test.sdf.json b/sdflint/tests/sdfobject-test.sdf.json new file mode 100644 index 0000000..77c68d0 --- /dev/null +++ b/sdflint/tests/sdfobject-test.sdf.json @@ -0,0 +1,123 @@ +{ + "info": { + "title": "Test object", + "copyright": "Example Test Corp 2023", + "license": "BSD-3-Clause", + "version": "2023-11-04", + "features": [], + "$comment": "Test comment", + "modified": "2023-11-05" + }, + "namespace": { + "foo": "https://foo.example.com/models", + "bar": "https://foo.example.com/bar" + }, + "defaultNamespace": "foo", + "sdfObject": { + "TestObject": { + "sdfProperty": { + "IntegerTestProperty": { + "description": "Integer temperature read-only", + "type": "integer", + "minimum": -3, + "maximum": 50, + "unit": "Cel", + "writable": false + }, + "StringTestProperty": { + "label": "String Test Property", + "type": "string", + "readable": true + }, + "BooleanTestProperty": { + "type": "boolean", + "nullable": true + }, + "ArrayTestProperty": { + "type": "array", + "maxItems": 10, + "minItems": 2, + "items" : { + "type": "integer" + } + }, + "FormatTestProperty": { + "type": "string", + "format": "uuid" + }, + "EnumTestProperty": { + "type": "string", + "enum": ["foo", "bar", "baz"] + }, + "ChoiceTestProperty": { + "type": "string", + "sdfChoice": { + "foo" : { + "description": "Foo value", + "const": 1 + }, + "bar": { + "description": "Bar value", + "const": 2 + } + } + }, + "DefaultValueTestProperty": { + "default": "foo", + "type": "string" + }, + "ExclusiveLimitsTestProperty": { + "type": "integer", + "exclusiveMaximum": 50, + "exclusiveMinimum": -3 + }, + "RequiredTestProperty": { + "type": "boolean", + "sdfRequired": [ + true + ] + }, + "RefTestProperty": { + "sdfRef": "#/sdfObject/TestObject/sdfData/TestData", + "description": "sdfRef test property" + } + }, + "sdfRequired": [ + "TestAction", + "#/sdfObject/TestObject/sdfProperty/IntegerTestProperty" + ], + "sdfAction": { + "TestAction": { + "description": "Action for testing", + "sdfInputData": { + "type": "number" + }, + "sdfOutputData": { + "type": "string" + } + }, + "TestActionObjectOutput": { + "description": "Test action with Object output data", + "sdfOutputData": { + "type": "object", + "properties": { + "a": { + "type": "integer" + }, + "b": { + "type": "string" + } + } + } + } + }, + "sdfData": { + "TestData": { + "type": "string", + "sdfType": "byte-string", + "contentFormat": "60" + } + } + } + } +} \ No newline at end of file diff --git a/sdflint/tests/test-schema.js b/sdflint/tests/test-schema.js new file mode 100644 index 0000000..0cfd26f --- /dev/null +++ b/sdflint/tests/test-schema.js @@ -0,0 +1,117 @@ +const fs = require('fs'); +const sdfLint = require('../sdflint'); +const { exec } = require('child_process'); + +/* Requires merge-patch command installed */ +const MERGE_PATCH_CMD = "merge-patch"; +const VERBOSE = false; + +let modelFile = process.argv[2]; +let schemaFile = process.argv[3]; +let testCases = process.argv.slice(4); + +let schema; + +function fail(reason) { + console.error(reason); + process.exit(1); +} + +try { + schema = JSON.parse(fs.readFileSync(schemaFile, + { encoding: 'utf-8' })); +} catch (err) { + fail("Can't read schema file: " + err.message); +} + +if (testCases.length > 0) { + runTestCases(testCases); +} else { + let sdfData; + try { + sdfData = fs.readFileSync(modelFile, + { encoding: 'utf-8' }); + } catch (err) { + fail("Can't read SDF model file: " + err.message); + } + + let lintRes = sdfLint.sdfLint(sdfData, schema); + if (lintRes.errorCount > 1) { + console.dir(sdfData, { "depth": null }); + fail("SDF model has errors"); + } + + let origSDF = JSON.parse(sdfData); + changeAndValidate(origSDF, "FOOBAR", "", origSDF); + changeAndValidate(origSDF, 42, "", origSDF); + +} + +function runTestCases(testCases) { + testCases.forEach(patchFile => { + let patchCmd = MERGE_PATCH_CMD + " " + modelFile + " " + patchFile; + let caseName = patchFile.substring(0, patchFile.lastIndexOf('.')); + + exec(patchCmd, (error, stdout, stderr) => { + if (error) { + fail(error.message); + } + if (stderr) { + fail(stderr); + } + + let lintResult = sdfLint.sdfLint(stdout, schema); + if (lintResult.errorCount < 1) { + console.error("Didn't detect error case " + caseName); + } else if (VERBOSE) { + let schemaErr = lintResult.errors.schema[0]; + console.log("Case " + caseName); + console.log(" Path: " + schemaErr.dataPath); + console.log(" Error: " + schemaErr.message); + } + }); + + }); + +} + + +function validate(sdf, schema, changedElement, newValue){ + let lintRes = sdfLint.sdfLint(sdf, schema, undefined, { "isJSON": true }); + if (lintRes.errorCount == 0) { + console.log("Missed change: " + changedElement + " -> " + + JSON.stringify(newValue)); + } else if (VERBOSE) { + console.log("Caught change: " + changedElement + " -> " + + JSON.stringify(newValue)); + console.log(lintRes.errors.schema); + } +} + + +/* one by one change qualities in the SDF file and validate result */ +function changeAndValidate(json, newVal, path, origSDF) { + const keys = Object.keys(json); + if (VERBOSE) { + console.log("At " + path); + } + keys.forEach (key => { + if (typeof json[key] === 'object') { + return changeAndValidate(json[key], newVal, path + key + '/', origSDF); + } else if (Array.isArray(json)) { + if (json.length == 0) { + key = 0; + } + let oldVal = json[key]; + json[key] = newVal; + validate(origSDF, schema, path + key, newVal); + json[key] = oldVal; + } + else { + let oldVal = json[key]; + json[key] = newVal; + validate(origSDF, schema, path + key, newVal); + json[key] = oldVal; + } + }); +} diff --git a/sdflint/tests/type-to-foo.json b/sdflint/tests/type-to-foo.json new file mode 100644 index 0000000..ae6a058 --- /dev/null +++ b/sdflint/tests/type-to-foo.json @@ -0,0 +1,11 @@ +{ + "sdfObject": { + "TestObject": { + "sdfProperty": { + "IntegerTestProperty": { + "type": "foo" + } + } + } + } +} \ No newline at end of file