diff options
-rw-r--r-- | contacts_validation/.gitignore | 16 | ||||
-rw-r--r-- | contacts_validation/MANIFEST.in | 5 | ||||
-rw-r--r-- | contacts_validation/contacts_validation/__init__.py | 78 | ||||
-rw-r--r-- | contacts_validation/contacts_validation/command_line.py | 51 | ||||
-rw-r--r-- | contacts_validation/contacts_validation/data/contacts_schema.yaml | 314 | ||||
-rw-r--r-- | contacts_validation/license.txt | 21 | ||||
-rw-r--r-- | contacts_validation/scripts/.gitignore | 1 | ||||
-rw-r--r-- | contacts_validation/scripts/draft4.json | 224 | ||||
-rw-r--r-- | contacts_validation/scripts/draft4_strict.json | 225 | ||||
-rw-r--r-- | contacts_validation/scripts/json_meta_schema_draft_07.json | 168 | ||||
-rw-r--r-- | contacts_validation/scripts/json_meta_schema_draft_07_strict.json | 168 | ||||
-rwxr-xr-x | contacts_validation/scripts/validate-schema.sh | 8 | ||||
-rwxr-xr-x | contacts_validation/setup.py | 47 |
13 files changed, 1326 insertions, 0 deletions
diff --git a/contacts_validation/.gitignore b/contacts_validation/.gitignore new file mode 100644 index 0000000..681d774 --- /dev/null +++ b/contacts_validation/.gitignore @@ -0,0 +1,16 @@ +# safely ignore these filetypes/extensions +*.pyo +*.pyc +*.log +*.swp +*.whl + +# safely ignore tmp dirs +tmp +build + +# ignore setuptools distribution folder +dist + +# ignore python egg metadata +*.egg-info diff --git a/contacts_validation/MANIFEST.in b/contacts_validation/MANIFEST.in new file mode 100644 index 0000000..ded5682 --- /dev/null +++ b/contacts_validation/MANIFEST.in @@ -0,0 +1,5 @@ +#include README.rst +#include docs/*.txt +#include contacts_schema.yaml +recursive-include contacts_validation/data * +recursive-include scripts * diff --git a/contacts_validation/contacts_validation/__init__.py b/contacts_validation/contacts_validation/__init__.py new file mode 100644 index 0000000..822e789 --- /dev/null +++ b/contacts_validation/contacts_validation/__init__.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# Copyright 2018 vg@devys.org +# SPDX-License-Identifier: MIT + +import collections +import datetime +import os +import pprint +import sys + +import jsonschema +import yaml + + +def convert_types(obj): + if isinstance(obj, str): + return obj + elif isinstance(obj, datetime.date): + return obj.strftime('%F') + elif isinstance(obj, collections.Sequence): + for i, _ in enumerate(obj): + obj[i] = convert_types(obj[i]) + elif isinstance(obj, collections.Mapping): + for k, v in obj.items(): + obj[k] = convert_types(v) + + return obj + + +def print_contact(contact): + cfield = '\033[31;1m' + cnormal = '\033[0m' + print(f'{cfield}Contact{cnormal}: {contact["display"]}' + f' [{cfield}uuid{cnormal}: {contact["uuid"]}]') + + +def print_validation_error(data_obj, exception): + print('Best match error:') + print(exception) + print() + + cfield = '\033[31;1m' + cnormal = '\033[0m' + print('Errors in context sorted by relevance:') + for i, suberror in enumerate(sorted(exception.context, + key=jsonschema.exceptions.relevance)): + if i: + print() + print(f'=> {cfield}schema{cnormal}:', + '.'.join(str(e) for e in suberror.absolute_schema_path)) + print(f' {cfield}instance{cnormal}:', + '.'.join(str(e) for e in suberror.absolute_path)) + print(f' {cfield}E{cnormal}:', suberror.message) + + print(f'Error occured on: {".".join(str(i) for i in exception.absolute_path)}') + print_contact(data_obj[exception.path[0]]) + print() + + +def validate_yaml_data(data_obj, schema_obj): + try: + jsonschema.validate(convert_types(data_obj), schema_obj) + except jsonschema.ValidationError as e: + print_validation_error(data_obj, e) + + # schema ok, check for duplicates in display and uuids + display_dups = collections.defaultdict(list) + uuid_dups = collections.defaultdict(list) + for index, item in enumerate(data_obj): + display_dups[item['display']].append(index) + uuid_dups[item['uuid']].append(index) + for e in (data_obj[e] for e in display_dups.values() if len(e) > 2): + print('WARNING: duplicated display value for these contacts:') + print(f'=> display: {e["display"]} uuid: {e["uuid"]}') + for e in (data_obj[e] for e in uuid_dups.values() if len(e) > 2): + print('ERROR: duplicated uuid value for these contacts:') + print(f'=> display: {e["display"]} uuid: {e["uuid"]}') + diff --git a/contacts_validation/contacts_validation/command_line.py b/contacts_validation/contacts_validation/command_line.py new file mode 100644 index 0000000..0310c78 --- /dev/null +++ b/contacts_validation/contacts_validation/command_line.py @@ -0,0 +1,51 @@ +# Copyright 2018 vg@devys.org +# SPDX-License-Identifier: MIT + +''' +Validate my contacts. + +if no FILENAME or one filename is -, stdin will be used. + +Usage: contacts-validation [options] [--] [FILENAME...] + contacts-validation -h|--help|--help-format + +Options: + -h, --help Display this help message + --help-format display schema fields name/title/descriptions +''' + +import os +import sys + +import docopt +import yaml + +from . import validate_yaml_data + + +def gen_streams(filenames): + for filename in filenames: + if filename == '-': + yield sys.stdin + else: + with open(filename, 'r', encoding='utf8') as stream: + yield stream + + +def main(): + 'function called only when script invoked directly on command line' + args = docopt.docopt(__doc__) + + if args['--help-format']: + print(f'for now see the schema yaml file for description') + raise SystemExit(0) + + with open(f'{os.path.dirname(__file__)}/data/contacts_schema.yaml') as schema_fh: + schema_obj = yaml.safe_load(schema_fh.read()) + assert schema_obj + + for i, stream in enumerate(gen_streams(args['FILENAME'] or ['-'])): + print('#'*60, f'# Valdating stream {i}', '#'*60, sep='\n') + yaml_data = yaml.safe_load(stream.read()) + assert yaml_data + validate_yaml_data(yaml_data, schema_obj) diff --git a/contacts_validation/contacts_validation/data/contacts_schema.yaml b/contacts_validation/contacts_validation/data/contacts_schema.yaml new file mode 100644 index 0000000..7431d06 --- /dev/null +++ b/contacts_validation/contacts_validation/data/contacts_schema.yaml @@ -0,0 +1,314 @@ +# vim: set ts=2 sts=2 sw=2 : +$schema: http://json-schema.org/draft-07/schema# +title: contacts +description: schema of my personal contacts book +type: array +items: + title: contact + type: object + additionalProperties: false + required: [display, uuid] + properties: + + ##################### + # required properties + ##################### + + display: + description: contact name, displayed as is + type: string + + uuid: + description: unique identifier of the contact (ex. uuidgen command) + type: string + pattern: '^[\dabcdef]{8}-[\dabcdef]{4}-[\dabcdef]{4}-[\dabcdef]{4}-[\dabcdef]{12}$' + + ####################### + # additional properties + ####################### + # note: I made the choice to have to explicitely put property names (like + # phone: 'numberxxx' for phones) instead of having the possibility to + # either have phones: ['numberxxx', {phone: 'numberyyy', otherprop: + # xxx}]). It is a little less easier to write, but it is more clear when + # manually reading a contact (and less confusing). + + firstname: + $ref: '#/definitions/simple_strings' + + lastname: + $ref: '#/definitions/simple_string' + + lastnamebm: + title: last name (surname) before being married (birth last name) + $ref: '#/definitions/simple_string' + + nickname: + $ref: '#/definitions/simple_strings' + + title: + $ref: '#/definitions/simple_string' + + comments: + $ref: '#/definitions/comments' + + role: + #$ref: '#/definitions/simple_string' + type: string + + org: + type: array + items: {type: string} + + birthday: + $ref: '#/definitions/datetime' + + birthday_ignore: + description: + ignore this birthday when generating calendar events (ex. with + rem-generate-birthdays) + type: boolean + + married: + $ref: '#/definitions/datetime' + + favorite: + description: + use this contact as favorite, a special field in vcard format, used by + phones supporting it + type: boolean + + related: + description: this contact is related to another pointed by this uri + type: string + format: uri + + kind: + description: the kind of contact, or why this contact has been added + type: string + enum: + - job # here because of job + - com # here because it is a commercial contact of anything I use + - friend # here because it is one of my friend, or friend of friend + - family # here because it is part of my family + - school # contact here because of the school + + urls: + type: array + items: + description: a uri associated with the contact (ex. cv site) + type: string + format: uri + + notes: + $ref: '#/definitions/simple_strings' + + websites: + type: array + items: {$ref: '#/definitions/website'} + + associations: + type: array + items: {$ref: '#/definitions/association'} + + emails: + type: array + items: {$ref: '#/definitions/email'} + + ims: + title: instant messages addresses + type: array + items: {$ref: '#/definitions/im'} + + phones: + type: array + items: {$ref: '#/definitions/phone'} + + addresses: + type: array + items: {$ref: '#/definitions/address'} + + events: + type: array + items: {$ref: '#/definitions/event'} + + #imported: + # description: free form, used for the first time import of contact + # type: [array, object] + + +definitions: + + email: + type: object + additionalProperties: false + required: [email] + properties: + email: + type: string + # limitation: a true e-mail address can contain multiple '@' symbols, + # but I accept to not store them in my contacts. + pattern: '^[^@]+@[^@]+$' + comments: {$ref: '#/definitions/comments'} + tags: {$ref: '#/definitions/tags'} + + im: + type: object + additionalProperties: false + required: [im] + properties: + im: {type: string, format: uri} + type: + type: string + enum: [xmpp] + comments: {$ref: '#/definitions/comments'} + tags: {$ref: '#/definitions/tags'} + + phone: + type: object + additionalProperties: false + required: [phone] + properties: + phone: + type: string + pattern: '^\+[1-9]{2}\d{9}|[1-9]\d{2}|[1-9]\d{4}$' + comments: {$ref: '#/definitions/comments'} + tags: {$ref: '#/definitions/tags'} + + website: + type: object + additionalProperties: false + required: [website] + properties: + website: {type: string, format: uri} + comments: {$ref: '#/definitions/comments'} + tags: {$ref: '#/definitions/tags'} + + address: + type: object + additionalProperties: false + required: [street, code, city, country] + properties: + label: {$ref: '#/definitions/simple_string'} + street: + type: string + pattern: "^[\\w' .-]+$" + code: + type: [string, number] + $ref: '#/definitions/simple_string_pattern' + city: {type: string, pattern: "^[\\w' ,-]+$"} + country: {$ref: '#/definitions/simple_string'} + box: + type: [string, number] + $ref: '#/definitions/simple_string_pattern' + extended: {$ref: '#/definitions/simple_string'} + region: {$ref: '#/definitions/simple_string'} + + comments: {$ref: '#/definitions/comments'} + tags: {$ref: '#/definitions/tags'} + + association: + type: object + additionalProperties: false + required: [association] + properties: + association: {$ref: '#/definitions/simple_string'} + comments: {$ref: '#/definitions/comments'} + tags: {$ref: '#/definitions/tags'} + + event: + description: + any date of importance for this contact (assume 1 day if duration or end + date not given) + type: object + additionalProperties: false + # oneOf below define until and duration as mutually exclusive, see + # https://stackoverflow.com/questions/28162509/mutually-exclusive-property-groups + # for details + oneOf: + - {required: [event, date], not: {anyOf: [{required: [until]}, {required: [duration]}]}} + - {required: [event, date, until], not: {required: [duration]}} + - {required: [event, date, duration], not: {required: [until]}} + properties: + event: {type: string, description: description of the event} + date: {$ref: '#/definitions/datetime'} + comments: {$ref: '#/definitions/comments'} + tags: {$ref: '#/definitions/tags'} + until: + description: + the event was on-going until this date, mutually exclusive with + duration + $ref: '#/definition/datetime' + duration: + type: number + minimum: 1 + multipleOf: 1 # force to be an integer + default: 1 + description: + duration of the event expressed in days, mutually exclusive with + until + + comments: + type: string + description: + free form comments (in plural even though being a single block of text + since the text often contains multiple comments) + + tags: + type: array + uniqueItems: true + items: + type: string + title: tags + description: possible tags list + # note: not sure if I keep the enum + enum: + - main + - wired + - phone + - private # a private phone, not for work, not for professional + - home # a phone shared for all person residing in the same house + - mobile # a mobile/cell phone + - old + - box + - short + - voicemail + - voice + - work # a phone number dedicated for work, professional use + - parents + - down + - gateway + - maybe_invalid + - school + - uk + - fax + + strings: + description: validate a string or list of strings, no pattern restriction + type: [string, array] + items: + type: string + + simple_string_pattern: + description: + validate a simple string, simple means they contains alphanumeric and + space and dash and simple quote characters only + pattern: "^[\\w' -]+$" + + simple_string: + type: string + $ref: '#/definitions/simple_string_pattern' + + simple_strings: + description: + validate a simple string or list of simple strings + type: [string, array] + $ref: '#/definitions/simple_string_pattern' + items: + type: string + pattern: '#/definitions/simple_string_pattern' + + datetime: + type: string + description: matches a date in the form YYYY-MM-DD + pattern: '^\d{4}-\d{2}-\d{2}$' + diff --git a/contacts_validation/license.txt b/contacts_validation/license.txt new file mode 100644 index 0000000..d381114 --- /dev/null +++ b/contacts_validation/license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright © 2018 vg <vg@devys.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/contacts_validation/scripts/.gitignore b/contacts_validation/scripts/.gitignore new file mode 100644 index 0000000..ceeb05b --- /dev/null +++ b/contacts_validation/scripts/.gitignore @@ -0,0 +1 @@ +/tmp diff --git a/contacts_validation/scripts/draft4.json b/contacts_validation/scripts/draft4.json new file mode 100644 index 0000000..bc7b2ee --- /dev/null +++ b/contacts_validation/scripts/draft4.json @@ -0,0 +1,224 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "default": {}, + "definitions": { + "positiveInteger": { + "minimum": 0, + "type": "integer" + }, + "positiveIntegerDefault0": { + "allOf": [ + { + "$ref": "#/definitions/positiveInteger" + }, + { + "default": 0 + } + ] + }, + "schemaArray": { + "items": { + "$ref": "#" + }, + "minItems": 1, + "type": "array" + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + }, + "dependencies": { + "exclusiveMaximum": [ + "maximum" + ], + "exclusiveMinimum": [ + "minimum" + ] + }, + "description": "Core schema meta-schema", + "id": "http://json-schema.org/draft-04/schema#", + "properties": { + "$schema": { + "format": "uri", + "type": "string" + }, + "additionalItems": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#" + } + ], + "default": {} + }, + "additionalProperties": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#" + } + ], + "default": {} + }, + "allOf": { + "$ref": "#/definitions/schemaArray" + }, + "anyOf": { + "$ref": "#/definitions/schemaArray" + }, + "default": {}, + "definitions": { + "additionalProperties": { + "$ref": "#" + }, + "default": {}, + "type": "object" + }, + "dependencies": { + "additionalProperties": { + "anyOf": [ + { + "$ref": "#" + }, + { + "$ref": "#/definitions/stringArray" + } + ] + }, + "type": "object" + }, + "description": { + "type": "string" + }, + "enum": { + "minItems": 1, + "type": "array", + "uniqueItems": true + }, + "exclusiveMaximum": { + "default": false, + "type": "boolean" + }, + "exclusiveMinimum": { + "default": false, + "type": "boolean" + }, + "format": { + "type": "string" + }, + "id": { + "format": "uri", + "type": "string" + }, + "items": { + "anyOf": [ + { + "$ref": "#" + }, + { + "$ref": "#/definitions/schemaArray" + } + ], + "default": {} + }, + "maxItems": { + "$ref": "#/definitions/positiveInteger" + }, + "maxLength": { + "$ref": "#/definitions/positiveInteger" + }, + "maxProperties": { + "$ref": "#/definitions/positiveInteger" + }, + "maximum": { + "type": "number" + }, + "minItems": { + "$ref": "#/definitions/positiveIntegerDefault0" + }, + "minLength": { + "$ref": "#/definitions/positiveIntegerDefault0" + }, + "minProperties": { + "$ref": "#/definitions/positiveIntegerDefault0" + }, + "minimum": { + "type": "number" + }, + "multipleOf": { + "exclusiveMinimum": true, + "minimum": 0, + "type": "number" + }, + "not": { + "$ref": "#" + }, + "oneOf": { + "$ref": "#/definitions/schemaArray" + }, + "pattern": { + "format": "regex", + "type": "string" + }, + "patternProperties": { + "additionalProperties": { + "$ref": "#" + }, + "default": {}, + "type": "object" + }, + "properties": { + "additionalProperties": { + "$ref": "#" + }, + "default": {}, + "type": "object" + }, + "required": { + "$ref": "#/definitions/stringArray" + }, + "title": { + "type": "string" + }, + "type": { + "anyOf": [ + { + "$ref": "#/definitions/simpleTypes" + }, + { + "items": { + "$ref": "#/definitions/simpleTypes" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + }, + "uniqueItems": { + "default": false, + "type": "boolean" + } + }, + "type": "object" +} diff --git a/contacts_validation/scripts/draft4_strict.json b/contacts_validation/scripts/draft4_strict.json new file mode 100644 index 0000000..29fc193 --- /dev/null +++ b/contacts_validation/scripts/draft4_strict.json @@ -0,0 +1,225 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "default": {}, + "definitions": { + "positiveInteger": { + "minimum": 0, + "type": "integer" + }, + "positiveIntegerDefault0": { + "allOf": [ + { + "$ref": "#/definitions/positiveInteger" + }, + { + "default": 0 + } + ] + }, + "schemaArray": { + "items": { + "$ref": "#" + }, + "minItems": 1, + "type": "array" + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "items": { + "type": "string" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + }, + "dependencies": { + "exclusiveMaximum": [ + "maximum" + ], + "exclusiveMinimum": [ + "minimum" + ] + }, + "description": "Core schema meta-schema", + "id": "http://json-schema.org/draft-04/schema#", + "properties": { + "$schema": { + "format": "uri", + "type": "string" + }, + "additionalItems": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#" + } + ], + "default": {} + }, + "additionalProperties": { + "anyOf": [ + { + "type": "boolean" + }, + { + "$ref": "#" + } + ], + "default": {} + }, + "allOf": { + "$ref": "#/definitions/schemaArray" + }, + "anyOf": { + "$ref": "#/definitions/schemaArray" + }, + "default": {}, + "definitions": { + "additionalProperties": { + "$ref": "#" + }, + "default": {}, + "type": "object" + }, + "dependencies": { + "additionalProperties": { + "anyOf": [ + { + "$ref": "#" + }, + { + "$ref": "#/definitions/stringArray" + } + ] + }, + "type": "object" + }, + "description": { + "type": "string" + }, + "enum": { + "minItems": 1, + "type": "array", + "uniqueItems": true + }, + "exclusiveMaximum": { + "default": false, + "type": "boolean" + }, + "exclusiveMinimum": { + "default": false, + "type": "boolean" + }, + "format": { + "type": "string" + }, + "id": { + "format": "uri", + "type": "string" + }, + "items": { + "anyOf": [ + { + "$ref": "#" + }, + { + "$ref": "#/definitions/schemaArray" + } + ], + "default": {} + }, + "maxItems": { + "$ref": "#/definitions/positiveInteger" + }, + "maxLength": { + "$ref": "#/definitions/positiveInteger" + }, + "maxProperties": { + "$ref": "#/definitions/positiveInteger" + }, + "maximum": { + "type": "number" + }, + "minItems": { + "$ref": "#/definitions/positiveIntegerDefault0" + }, + "minLength": { + "$ref": "#/definitions/positiveIntegerDefault0" + }, + "minProperties": { + "$ref": "#/definitions/positiveIntegerDefault0" + }, + "minimum": { + "type": "number" + }, + "multipleOf": { + "exclusiveMinimum": true, + "minimum": 0, + "type": "number" + }, + "not": { + "$ref": "#" + }, + "oneOf": { + "$ref": "#/definitions/schemaArray" + }, + "pattern": { + "format": "regex", + "type": "string" + }, + "patternProperties": { + "additionalProperties": { + "$ref": "#" + }, + "default": {}, + "type": "object" + }, + "properties": { + "additionalProperties": { + "$ref": "#" + }, + "default": {}, + "type": "object" + }, + "required": { + "$ref": "#/definitions/stringArray" + }, + "title": { + "type": "string" + }, + "type": { + "anyOf": [ + { + "$ref": "#/definitions/simpleTypes" + }, + { + "items": { + "$ref": "#/definitions/simpleTypes" + }, + "minItems": 1, + "type": "array", + "uniqueItems": true + } + ] + }, + "uniqueItems": { + "default": false, + "type": "boolean" + } + }, + "type": "object", + "additionalProperties": false +} diff --git a/contacts_validation/scripts/json_meta_schema_draft_07.json b/contacts_validation/scripts/json_meta_schema_draft_07.json new file mode 100644 index 0000000..85079d8 --- /dev/null +++ b/contacts_validation/scripts/json_meta_schema_draft_07.json @@ -0,0 +1,168 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": { "$ref": "#" }, + "then": { "$ref": "#" }, + "else": { "$ref": "#" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": true +} diff --git a/contacts_validation/scripts/json_meta_schema_draft_07_strict.json b/contacts_validation/scripts/json_meta_schema_draft_07_strict.json new file mode 100644 index 0000000..dd5903c --- /dev/null +++ b/contacts_validation/scripts/json_meta_schema_draft_07_strict.json @@ -0,0 +1,168 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": false, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": { "$ref": "#" }, + "then": { "$ref": "#" }, + "else": { "$ref": "#" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": true +} diff --git a/contacts_validation/scripts/validate-schema.sh b/contacts_validation/scripts/validate-schema.sh new file mode 100755 index 0000000..2aad40b --- /dev/null +++ b/contacts_validation/scripts/validate-schema.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -eux +mkdir -p tmp +cp ../contacts_schema.yaml tmp/myschema.yaml +convert-yaml2json tmp/myschema.yaml +#python3 -m jsonschema -i tmp/myschema.yaml.json draft4_strict.json +python3 -m jsonschema -i tmp/myschema.yaml.json draft4.json +rm -r tmp diff --git a/contacts_validation/setup.py b/contacts_validation/setup.py new file mode 100755 index 0000000..383d6cd --- /dev/null +++ b/contacts_validation/setup.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# Copyright 2018 vg@devys.org +# SPDX-License-Identifier: MIT + +from setuptools import setup + +def readme(): + with open('README.rst') as f: + return f.read() + +setup( + name='contacts_validation', + version='0.1', + description='Validate contact database in yaml format', + #long_description=readme(), + # description at https://pypi.org/pypi?%3Aaction=list_classifiers + classifiers=[ + 'Development Status :: 3 - Alpha', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3.6', + 'Environment :: Console', + 'Intended Audience :: End Users/Desktop', + 'Natural Language :: English', + 'Operating System :: POSIX', + 'Topic :: Utilities', + ], + keywords='contacts contact validation schema', + url='https://git.devys.org/contacts_validation', + author='vg', + author_email='vg+dev@devys.org', + license='MIT', + packages=['contacts_validation'], # module packages + install_requires=[ + 'docopt', + 'pyyaml', + 'jsonschema', + ], + include_package_data=True, + zip_safe=False, + #scripts=['./jsonschema_yaml.py'], + entry_points = { + 'console_scripts': + ['contacts-validation=contacts_validation.command_line:main'], + }, + #test_suite='pytest', + #tests_require=['pytest'], +) |