From 0e3eaff30d80347908313649e6be2cfeacbbd9de Mon Sep 17 00:00:00 2001 From: keenangraham Date: Tue, 1 Feb 2022 12:35:36 -0800 Subject: [PATCH 01/29] rdfjson-bump --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9e1bf9fd7..dd2d2d526 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ "pyramid_retry==2.1.1", "python-magic==0.4.15", "pytz==2019.3", - "rdflib-jsonld==0.4.0", + "rdflib-jsonld==0.6.0", "rdflib==4.2.2", "redis==3.5.3", "requests==2.22.0", From f80f5a5dd7c39d3032ac982ba7c4140309a584f8 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Tue, 1 Feb 2022 14:16:48 -0800 Subject: [PATCH 02/29] Add some validator tests --- src/snovault/schema_validation.py | 1 + src/snovault/tests/test_validator.py | 225 +++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/snovault/schema_validation.py create mode 100644 src/snovault/tests/test_validator.py diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py new file mode 100644 index 000000000..6d56d3005 --- /dev/null +++ b/src/snovault/schema_validation.py @@ -0,0 +1 @@ +NO_DEFAULT = object() diff --git a/src/snovault/tests/test_validator.py b/src/snovault/tests/test_validator.py new file mode 100644 index 000000000..37a564354 --- /dev/null +++ b/src/snovault/tests/test_validator.py @@ -0,0 +1,225 @@ +import pytest + +from collections import OrderedDict + +from jsonschema_serialize_fork.validators import Draft4Validator +from jsonschema_serialize_fork import NO_DEFAULT +from jsonschema_serialize_fork.exceptions import ValidationError + + +validator_class = Draft4Validator + + +def make_default(instance, subschema): + if instance.get('skip'): + return NO_DEFAULT + assert subschema == {'serverDefault': 'test'} + return 'bar' + + +def test_validator_serializes_default_properties(): + schema = { + 'properties': { + 'foo': { + 'default': 'bar' + } + } + } + result, errors = validator_class( + schema, + serialize=True + ).serialize( + {} + ) + assert result == {'foo': 'bar'} + assert errors == [] + + +def test_validator_serializes_default_properties_in_items(): + schema = { + 'items': { + 'properties': { + 'foo': { + 'default': 'bar' + } + } + } + } + result, errors = validator_class( + schema, + serialize=True + ).serialize( + [ + {} + ] + ) + assert result == [{'foo': 'bar'}] + assert errors == [] + + +def test_validator_serializes_server_default_properties(): + schema = { + 'properties': { + 'foo': { + 'serverDefault': 'test' + } + } + } + result, errors = validator_class( + schema, + serialize=True, + server_defaults={ + 'test': make_default + }, + ).serialize( + {} + ) + assert result == {'foo': 'bar'} + assert errors == [] + + +def test_validator_ignores_server_default_returning_no_default(): + schema = { + 'properties': { + 'foo': { + 'serverDefault': 'test' + }, + 'skip': {} + } + } + result, errors = validator_class( + schema, + serialize=True, + server_defaults={ + 'test': make_default + }, + ).serialize( + { + 'skip': True + } + ) + assert result == {'skip': True} + assert errors == [] + + +def test_validator_serializes_server_default_properties_in_items(): + schema = { + 'items': { + 'properties': { + 'foo': { + 'serverDefault': 'test' + } + } + } + } + result, errors = validator_class( + schema, + serialize=True, + server_defaults={ + 'test': make_default + }, + ).serialize( + [ + {} + ] + ) + assert result == [{'foo': 'bar'}] + assert errors == [] + + +def test_validator_serializes_properties_in_order(): + schema = { + 'properties': OrderedDict( + [ + ('foo', {}), + ('bar', {}) + ] + ) + } + validator = validator_class( + schema, + types={'object': OrderedDict}, + serialize=True, + ) + value = OrderedDict( + [ + ('bar', 1), + ('foo', 2) + ] + ) + result, errors = validator.serialize(value) + assert isinstance(result, OrderedDict) + assert list(result) == ['foo', 'bar'] + assert errors == [] + + +def test_validator_serializes_properties_in_order_with_dict(): + schema = { + 'properties': OrderedDict( + [ + ('foo', {}), + ('bar', {}) + ] + ) + } + validator = validator_class( + schema, + types={ + 'object': (OrderedDict, dict) + }, + serialize=True, + ) + value = dict( + [ + ('bar', 1), + ('foo', 2) + ] + ) + result, errors = validator.serialize(value) + assert isinstance(result, OrderedDict) + assert list(result) == ['foo', 'bar'] + assert errors == [] + + +def test_validator_returns_error(): + schema = { + 'type': 'object', + 'properties': { + 'name': { + 'type': 'string' + } + }, + 'required': ['name'] + } + result, errors = validator_class( + schema, + serialize=True + ).serialize( + { + 'name': 'abc' + } + ) + assert result == {'name': 'abc'} + assert not errors + result, errors = validator_class( + schema, + serialize=True + ).serialize( + { + 'name': 1 + } + ) + assert result == { + 'name': 1 + } + assert isinstance(errors[0], ValidationError) + assert errors[0].message == "1 is not of type 'string'" + result, errors = validator_class( + schema, + serialize=True + ).serialize( + {} + ) + assert result == {} + assert isinstance(errors[0], ValidationError) + assert errors[0].message == "'name' is a required property" From 8a4e08a05d0d8842e523f66ff10dd517db220a8c Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Tue, 1 Feb 2022 15:26:28 -0800 Subject: [PATCH 03/29] Tests passing --- setup.py | 1 + src/snovault/schema_validation.py | 41 +++++++++++++++- src/snovault/tests/test_validator.py | 72 ++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index dd2d2d526..4f949606f 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ "elasticsearch==5.4.0", "future==0.18.2", "jsonschema_serialize_fork==2.1.1", + "jsonschema==4.4.0", "lucenequery==0.1", "passlib==1.7.2", "psutil==5.6.7", diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 6d56d3005..bd3293833 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -1 +1,40 @@ -NO_DEFAULT = object() +# Draft202012Validator +# "$schema": "https://json-schema.org/draft/2020-12/schema" +from jsonschema import Draft202012Validator, validators +from copy import deepcopy + + +def extend_with_default(validator_class): + validate_properties = validator_class.VALIDATORS['properties'] + + def set_defaults(validator, properties, instance, schema): + for property, subschema in properties.items(): + if 'default' in subschema: + instance.setdefault(property, subschema['default']) + + for error in validate_properties( + validator, properties, instance, schema, + ): + yield error + + return validators.extend( + validator_class, {'properties': set_defaults}, + ) + + +DefaultingDraft202012Validator = extend_with_default(Draft202012Validator) + + +class SchemaValidator(DefaultingDraft202012Validator): + + def serialize(self, instance): + self._original_instance = instance + self._mutated_instance = deepcopy( + self._original_instance + ) + errors = list( + self.iter_errors( + self._mutated_instance + ) + ) + return self._mutated_instance, errors diff --git a/src/snovault/tests/test_validator.py b/src/snovault/tests/test_validator.py index 37a564354..bca86c2a5 100644 --- a/src/snovault/tests/test_validator.py +++ b/src/snovault/tests/test_validator.py @@ -10,6 +10,28 @@ validator_class = Draft4Validator +fake_schema = { + 'title': 'Fake', + 'description': 'Schema', + '$schema': 'http://json-schema.org/draft-04/schema#', + 'type': 'object', + 'required': ['award', 'lab'], + 'identifyingProperties': ['uuid'], + 'additionalProperties': False, + 'properties': { + 'uuid': { + 'type': 'string' + }, + 'award': { + 'type': 'string' + }, + 'lab': { + 'type': 'string' + } + } +} + + def make_default(instance, subschema): if instance.get('skip'): return NO_DEFAULT @@ -223,3 +245,53 @@ def test_validator_returns_error(): assert result == {} assert isinstance(errors[0], ValidationError) assert errors[0].message == "'name' is a required property" + + +def test_validator_check_schema(): + from jsonschema import Draft202012Validator + validator_class.check_schema(fake_schema) + Draft202012Validator.check_schema(fake_schema) + + +def test_validator_extend_with_default(): + from copy import deepcopy + from snovault.schema_validation import SchemaValidator + original_instance = { + 'x': 'y' + } + mutated_instance = deepcopy(original_instance) + assert original_instance == mutated_instance + schema = {'properties': {'foo': {'default': 'bar'}}} + SchemaValidator(schema).validate(mutated_instance) + assert original_instance == {'x': 'y'} + assert mutated_instance == {'x': 'y', 'foo': 'bar'} + + +def test_validator_extend_with_default_and_serialize(): + instance = { + 'x': 'y' + } + from snovault.schema_validation import SchemaValidator + schema = {'properties': {'foo': {'default': 'bar'}}} + result, errors = SchemaValidator(schema).serialize(instance) + assert instance == {'x': 'y'} + assert result == {'x': 'y', 'foo': 'bar'} + assert errors == [] + schema = { + 'properties': { + 'foo': { + 'default': 'bar' + }, + 'name': { + 'type': 'string' + } + }, + 'required': ['name'] + } + result, errors = SchemaValidator(schema).serialize( + { + 'foo': 'thing', + } + ) + assert result == {'foo': 'thing'} + assert errors[0].message == "'name' is a required property" From c686df4b70aafa6fba15aa9cba481dac361d7ca4 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Tue, 1 Feb 2022 15:52:31 -0800 Subject: [PATCH 04/29] Add default and serverDefault functionality --- src/snovault/schema_validation.py | 26 ++++++++++++-- src/snovault/tests/test_validator.py | 53 +++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index bd3293833..8764b4c17 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -4,13 +4,28 @@ from copy import deepcopy +NO_DEFAULT = object() + + def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS['properties'] def set_defaults(validator, properties, instance, schema): for property, subschema in properties.items(): if 'default' in subschema: - instance.setdefault(property, subschema['default']) + instance.setdefault( + property, + deepcopy(subschema['default']) + ) + if 'serverDefault' in subschema: + server_default = validator.server_default( + instance, subschema + ) + if server_default is not NO_DEFAULT: + instance.setdefault( + property, + server_default + ) for error in validate_properties( validator, properties, instance, schema, @@ -25,7 +40,9 @@ def set_defaults(validator, properties, instance, schema): DefaultingDraft202012Validator = extend_with_default(Draft202012Validator) -class SchemaValidator(DefaultingDraft202012Validator): +class SerializingSchemaValidator(DefaultingDraft202012Validator): + + SERVER_DEFAULTS = {} def serialize(self, instance): self._original_instance = instance @@ -38,3 +55,8 @@ def serialize(self, instance): ) ) return self._mutated_instance, errors + + def server_default(self, instance, subschema): + factory_name = subschema['serverDefault'] + factory = self.SERVER_DEFAULTS[factory_name] + return factory(instance, subschema) diff --git a/src/snovault/tests/test_validator.py b/src/snovault/tests/test_validator.py index bca86c2a5..364203d0c 100644 --- a/src/snovault/tests/test_validator.py +++ b/src/snovault/tests/test_validator.py @@ -255,14 +255,14 @@ def test_validator_check_schema(): def test_validator_extend_with_default(): from copy import deepcopy - from snovault.schema_validation import SchemaValidator + from snovault.schema_validation import SerializingSchemaValidator original_instance = { 'x': 'y' } mutated_instance = deepcopy(original_instance) assert original_instance == mutated_instance schema = {'properties': {'foo': {'default': 'bar'}}} - SchemaValidator(schema).validate(mutated_instance) + SerializingSchemaValidator(schema).validate(mutated_instance) assert original_instance == {'x': 'y'} assert mutated_instance == {'x': 'y', 'foo': 'bar'} @@ -271,9 +271,9 @@ def test_validator_extend_with_default_and_serialize(): instance = { 'x': 'y' } - from snovault.schema_validation import SchemaValidator + from snovault.schema_validation import SerializingSchemaValidator schema = {'properties': {'foo': {'default': 'bar'}}} - result, errors = SchemaValidator(schema).serialize(instance) + result, errors = SerializingSchemaValidator(schema).serialize(instance) assert instance == {'x': 'y'} assert result == {'x': 'y', 'foo': 'bar'} assert errors == [] @@ -288,10 +288,53 @@ def test_validator_extend_with_default_and_serialize(): }, 'required': ['name'] } - result, errors = SchemaValidator(schema).serialize( + result, errors = SerializingSchemaValidator(schema).serialize( { 'foo': 'thing', } ) assert result == {'foo': 'thing'} assert errors[0].message == "'name' is a required property" + + +def test_validator_extend_with_server_default_and_serialize(): + instance = { + 'x': 'y' + } + from snovault.schema_validation import SerializingSchemaValidator + SerializingSchemaValidator.SERVER_DEFAULTS = { + 'test': make_default + } + schema = {'properties': {'foo': {'serverDefault': 'test'}}} + result, errors = SerializingSchemaValidator(schema).serialize(instance) + assert instance == {'x': 'y'} + assert result == {'x': 'y', 'foo': 'bar'} + assert errors == [] + schema = { + 'properties': { + 'foo': { + 'serverDefault': 'test' + }, + 'name': { + 'type': 'string' + } + }, + 'required': ['name'] + } + result, errors = SerializingSchemaValidator(schema).serialize( + { + 'foo': 'thing', + } + ) + assert result == {'foo': 'thing'} + assert errors[0].message == "'name' is a required property" + result, errors = SerializingSchemaValidator(schema).serialize( + { + 'name': 'other thing', + } + ) + assert result == { + 'foo': 'bar', + 'name': 'other thing', + } + assert not errors From e57c4f50874ba44cbbd6ec0cda2e42da819e47a1 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Tue, 1 Feb 2022 19:16:04 -0800 Subject: [PATCH 05/29] Hook up new validator --- src/snovault/schema_utils.py | 53 +++++--------- src/snovault/schema_validation.py | 27 +++++--- src/snovault/tests/test_validator.py | 100 +++++++-------------------- 3 files changed, 60 insertions(+), 120 deletions(-) diff --git a/src/snovault/schema_utils.py b/src/snovault/schema_utils.py index cbf41b58b..ec1beb8d2 100644 --- a/src/snovault/schema_utils.py +++ b/src/snovault/schema_utils.py @@ -8,12 +8,10 @@ import codecs import collections import copy -from jsonschema_serialize_fork import ( - Draft4Validator, - FormatChecker, - RefResolver, -) -from jsonschema_serialize_fork.exceptions import ValidationError +from snovault.schema_validation import SerializingSchemaValidator +from jsonschema import FormatChecker +from jsonschema import RefResolver +from jsonschema.exceptions import ValidationError from uuid import UUID from .util import ensurelist @@ -124,8 +122,7 @@ def linkTo(validator, linkTo, instance, schema): return # And normalize the value to a uuid - if validator._serialize: - validator._validated[-1] = str(item.uuid) + instance = str(item.uuid) def linkFrom(validator, linkFrom, instance, schema): @@ -154,9 +151,6 @@ def linkFrom(validator, linkFrom, instance, schema): yield ValidationError(error) return else: - if validator._serialize: - lv = len(validator._validated) - # Look for an existing item; # if found use the schema for its type, # which may be a subtype of an abstract linkType @@ -210,16 +204,13 @@ def linkFrom(validator, linkFrom, instance, schema): for error in validator.descend(instance, subschema): yield error - if validator._serialize: - validated_instance = validator._validated[lv] - del validator._validated[lv:] - if uuid is not None: - validated_instance['uuid'] = uuid - elif 'uuid' in validated_instance: # where does this come from? - del validated_instance['uuid'] - if new_type is not None: - validated_instance['@type'] = [new_type] - validator._validated[-1] = validated_instance + validated_instance = instance + if uuid is not None: + validated_instance['uuid'] = uuid + elif 'uuid' in validated_instance: # where does this come from? + del validated_instance['uuid'] + if new_type is not None: + validated_instance['@type'] = [new_type] class IgnoreUnchanged(ValidationError): @@ -250,17 +241,6 @@ def permission(validator, permission, instance, schema): yield IgnoreUnchanged(error) -orig_uniqueItems = Draft4Validator.VALIDATORS['uniqueItems'] - - -def uniqueItems(validator, uI, instance, schema): - # Use serialized items if available - # (this gives the linkTo validator a chance to normalize paths into uuids) - if validator._serialize and validator._validated[-1]: - instance = validator._validated[-1] - yield from orig_uniqueItems(validator, uI, instance, schema) - - VALIDATOR_REGISTRY = {} @@ -281,8 +261,8 @@ def notSubmittable(validator, linkTo, instance, schema): yield ValidationError('submission disallowed') -class SchemaValidator(Draft4Validator): - VALIDATORS = Draft4Validator.VALIDATORS.copy() +class SchemaValidator(SerializingSchemaValidator): + VALIDATORS = SerializingSchemaValidator.VALIDATORS.copy() VALIDATORS['notSubmittable'] = notSubmittable # for backwards-compatibility VALIDATORS['calculatedProperty'] = notSubmittable @@ -290,7 +270,6 @@ class SchemaValidator(Draft4Validator): VALIDATORS['linkFrom'] = linkFrom VALIDATORS['permission'] = permission VALIDATORS['requestMethod'] = requestMethod - VALIDATORS['uniqueItems'] = uniqueItems VALIDATORS['validators'] = validators SERVER_DEFAULTS = SERVER_DEFAULTS @@ -311,13 +290,13 @@ def load_schema(filename): schema = mixinProperties(schema, resolver) # SchemaValidator is not thread safe for now - SchemaValidator(schema, resolver=resolver, serialize=True) + SchemaValidator(schema, resolver=resolver) return schema def validate(schema, data, current=None): resolver = NoRemoteResolver.from_schema(schema) - sv = SchemaValidator(schema, resolver=resolver, serialize=True, format_checker=format_checker) + sv = SchemaValidator(schema, resolver=resolver, format_checker=format_checker) validated, errors = sv.serialize(data) filtered_errors = [] diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 8764b4c17..7b777761c 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -1,6 +1,7 @@ # Draft202012Validator # "$schema": "https://json-schema.org/draft/2020-12/schema" -from jsonschema import Draft202012Validator, validators +from jsonschema import Draft202012Validator +from jsonschema import validators from copy import deepcopy @@ -10,8 +11,15 @@ def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS['properties'] + def should_set_defaults(instance): + if isinstance(instance, dict): + return True + return False + def set_defaults(validator, properties, instance, schema): for property, subschema in properties.items(): + if not isinstance(subschema, dict): + continue if 'default' in subschema: instance.setdefault( property, @@ -19,7 +27,8 @@ def set_defaults(validator, properties, instance, schema): ) if 'serverDefault' in subschema: server_default = validator.server_default( - instance, subschema + instance, + subschema ) if server_default is not NO_DEFAULT: instance.setdefault( @@ -27,20 +36,20 @@ def set_defaults(validator, properties, instance, schema): server_default ) - for error in validate_properties( - validator, properties, instance, schema, - ): - yield error + def properties_with_defaults(validator, properties, instance, schema): + if should_set_defaults(instance): + set_defaults(validator, properties, instance, schema) + yield from validate_properties(validator, properties, instance, schema) return validators.extend( - validator_class, {'properties': set_defaults}, + validator_class, {'properties': properties_with_defaults}, ) -DefaultingDraft202012Validator = extend_with_default(Draft202012Validator) +ExtendedValidator = extend_with_default(Draft202012Validator) -class SerializingSchemaValidator(DefaultingDraft202012Validator): +class SerializingSchemaValidator(ExtendedValidator): SERVER_DEFAULTS = {} diff --git a/src/snovault/tests/test_validator.py b/src/snovault/tests/test_validator.py index 364203d0c..92e83908b 100644 --- a/src/snovault/tests/test_validator.py +++ b/src/snovault/tests/test_validator.py @@ -2,18 +2,19 @@ from collections import OrderedDict -from jsonschema_serialize_fork.validators import Draft4Validator -from jsonschema_serialize_fork import NO_DEFAULT -from jsonschema_serialize_fork.exceptions import ValidationError +from snovault.schema_validation import NO_DEFAULT +from snovault.schema_utils import SchemaValidator +from jsonschema.exceptions import ValidationError -validator_class = Draft4Validator +validator_class = SchemaValidator fake_schema = { + 'id': 'abc', 'title': 'Fake', 'description': 'Schema', - '$schema': 'http://json-schema.org/draft-04/schema#', + '$schema': 'https://json-schema.org/draft/2020-12/schema', 'type': 'object', 'required': ['award', 'lab'], 'identifyingProperties': ['uuid'], @@ -39,6 +40,11 @@ def make_default(instance, subschema): return 'bar' +validator_class.SERVER_DEFAULTS = { + 'test': make_default +} + + def test_validator_serializes_default_properties(): schema = { 'properties': { @@ -48,8 +54,7 @@ def test_validator_serializes_default_properties(): } } result, errors = validator_class( - schema, - serialize=True + schema ).serialize( {} ) @@ -68,8 +73,7 @@ def test_validator_serializes_default_properties_in_items(): } } result, errors = validator_class( - schema, - serialize=True + schema ).serialize( [ {} @@ -88,11 +92,7 @@ def test_validator_serializes_server_default_properties(): } } result, errors = validator_class( - schema, - serialize=True, - server_defaults={ - 'test': make_default - }, + schema ).serialize( {} ) @@ -110,11 +110,7 @@ def test_validator_ignores_server_default_returning_no_default(): } } result, errors = validator_class( - schema, - serialize=True, - server_defaults={ - 'test': make_default - }, + schema ).serialize( { 'skip': True @@ -135,11 +131,7 @@ def test_validator_serializes_server_default_properties_in_items(): } } result, errors = validator_class( - schema, - serialize=True, - server_defaults={ - 'test': make_default - }, + schema ).serialize( [ {} @@ -149,57 +141,22 @@ def test_validator_serializes_server_default_properties_in_items(): assert errors == [] -def test_validator_serializes_properties_in_order(): +def test_validator_serializes_properties_in_order_of_input(): schema = { - 'properties': OrderedDict( - [ - ('foo', {}), - ('bar', {}) - ] - ) + 'properties': { + 'foo': {}, + 'bar': {}, + } } validator = validator_class( - schema, - types={'object': OrderedDict}, - serialize=True, + schema ) - value = OrderedDict( - [ - ('bar', 1), - ('foo', 2) - ] - ) - result, errors = validator.serialize(value) - assert isinstance(result, OrderedDict) - assert list(result) == ['foo', 'bar'] - assert errors == [] - - -def test_validator_serializes_properties_in_order_with_dict(): - schema = { - 'properties': OrderedDict( - [ - ('foo', {}), - ('bar', {}) - ] - ) + value = { + 'bar': 1, + 'foo': 2 } - validator = validator_class( - schema, - types={ - 'object': (OrderedDict, dict) - }, - serialize=True, - ) - value = dict( - [ - ('bar', 1), - ('foo', 2) - ] - ) result, errors = validator.serialize(value) - assert isinstance(result, OrderedDict) - assert list(result) == ['foo', 'bar'] + assert list(result) == ['bar', 'foo'] assert errors == [] @@ -215,7 +172,6 @@ def test_validator_returns_error(): } result, errors = validator_class( schema, - serialize=True ).serialize( { 'name': 'abc' @@ -225,7 +181,6 @@ def test_validator_returns_error(): assert not errors result, errors = validator_class( schema, - serialize=True ).serialize( { 'name': 1 @@ -238,7 +193,6 @@ def test_validator_returns_error(): assert errors[0].message == "1 is not of type 'string'" result, errors = validator_class( schema, - serialize=True ).serialize( {} ) @@ -248,9 +202,7 @@ def test_validator_returns_error(): def test_validator_check_schema(): - from jsonschema import Draft202012Validator validator_class.check_schema(fake_schema) - Draft202012Validator.check_schema(fake_schema) def test_validator_extend_with_default(): From ededd0cc1e8c90b1903578edce448a69183f5307 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Tue, 1 Feb 2022 20:09:23 -0800 Subject: [PATCH 06/29] Update test --- setup.py | 1 - src/snovault/schema_validation.py | 4 ++ src/snovault/tests/test_validator.py | 56 +++++++++++++++++++--------- src/snowflakes/schema_formats.py | 3 +- src/snowflakes/server_defaults.py | 3 +- src/snowflakes/tests/test_views.py | 4 +- 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/setup.py b/setup.py index 4f949606f..d27a51731 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,6 @@ "elasticsearch-dsl==5.4.0", "elasticsearch==5.4.0", "future==0.18.2", - "jsonschema_serialize_fork==2.1.1", "jsonschema==4.4.0", "lucenequery==0.1", "passlib==1.7.2", diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 7b777761c..f113333da 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -53,6 +53,10 @@ class SerializingSchemaValidator(ExtendedValidator): SERVER_DEFAULTS = {} + def add_server_defaults(self, server_defaults): + self.SERVER_DEFAULTS.update(server_defaults) + return self + def serialize(self, instance): self._original_instance = instance self._mutated_instance = deepcopy( diff --git a/src/snovault/tests/test_validator.py b/src/snovault/tests/test_validator.py index 92e83908b..29223adb1 100644 --- a/src/snovault/tests/test_validator.py +++ b/src/snovault/tests/test_validator.py @@ -40,11 +40,6 @@ def make_default(instance, subschema): return 'bar' -validator_class.SERVER_DEFAULTS = { - 'test': make_default -} - - def test_validator_serializes_default_properties(): schema = { 'properties': { @@ -73,7 +68,7 @@ def test_validator_serializes_default_properties_in_items(): } } result, errors = validator_class( - schema + schema, ).serialize( [ {} @@ -92,7 +87,11 @@ def test_validator_serializes_server_default_properties(): } } result, errors = validator_class( - schema + schema, + ).add_server_defaults( + { + 'test': make_default + } ).serialize( {} ) @@ -110,7 +109,11 @@ def test_validator_ignores_server_default_returning_no_default(): } } result, errors = validator_class( - schema + schema, + ).add_server_defaults( + { + 'test': make_default + } ).serialize( { 'skip': True @@ -130,12 +133,14 @@ def test_validator_serializes_server_default_properties_in_items(): } } } - result, errors = validator_class( + result, errors = SchemaValidator( schema + ).add_server_defaults( + { + 'test': make_default + } ).serialize( - [ - {} - ] + [{}] ) assert result == [{'foo': 'bar'}] assert errors == [] @@ -254,11 +259,14 @@ def test_validator_extend_with_server_default_and_serialize(): 'x': 'y' } from snovault.schema_validation import SerializingSchemaValidator - SerializingSchemaValidator.SERVER_DEFAULTS = { - 'test': make_default - } schema = {'properties': {'foo': {'serverDefault': 'test'}}} - result, errors = SerializingSchemaValidator(schema).serialize(instance) + result, errors = SerializingSchemaValidator( + schema, + ).add_server_defaults( + { + 'test': make_default + } + ).serialize(instance) assert instance == {'x': 'y'} assert result == {'x': 'y', 'foo': 'bar'} assert errors == [] @@ -273,14 +281,26 @@ def test_validator_extend_with_server_default_and_serialize(): }, 'required': ['name'] } - result, errors = SerializingSchemaValidator(schema).serialize( + result, errors = SerializingSchemaValidator( + schema + ).add_server_defaults( + { + 'test': make_default + } + ).serialize( { 'foo': 'thing', } ) assert result == {'foo': 'thing'} assert errors[0].message == "'name' is a required property" - result, errors = SerializingSchemaValidator(schema).serialize( + result, errors = SerializingSchemaValidator( + schema, + ).add_server_defaults( + { + 'test': make_default + } + ).serialize( { 'name': 'other thing', } diff --git a/src/snowflakes/schema_formats.py b/src/snowflakes/schema_formats.py index 39e376451..a2b5cf9e7 100644 --- a/src/snowflakes/schema_formats.py +++ b/src/snowflakes/schema_formats.py @@ -1,9 +1,10 @@ import re import rfc3987 -from jsonschema_serialize_fork import FormatChecker +from jsonschema import FormatChecker from pyramid.threadlocal import get_current_request from uuid import UUID + accession_re = re.compile(r'^SNO(SS|FL)[0-9][0-9][0-9][A-Z][A-Z][A-Z]$') test_accession_re = re.compile(r'^TST(SS|FL)[0-9][0-9][0-9]([0-9][0-9][0-9]|[A-Z][A-Z][A-Z])$') uuid_re = re.compile(r'(?i)\{?(?:[0-9a-f]{4}-?){8}\}?') diff --git a/src/snowflakes/server_defaults.py b/src/snowflakes/server_defaults.py index 1253ceb5e..b5cc7c067 100644 --- a/src/snowflakes/server_defaults.py +++ b/src/snowflakes/server_defaults.py @@ -1,5 +1,5 @@ from datetime import datetime -from jsonschema_serialize_fork import NO_DEFAULT +from snovault.schema_validation import NO_DEFAULT from pyramid.threadlocal import get_current_request from string import ( digits, @@ -36,7 +36,6 @@ def userid(instance, subschema): @server_default def now(instance, subschema): - # from jsonschema_serialize_fork date-time format requires a timezone return datetime.utcnow().isoformat() + '+00:00' diff --git a/src/snowflakes/tests/test_views.py b/src/snowflakes/tests/test_views.py index 0e5e96c8f..5e2bbe2b3 100644 --- a/src/snowflakes/tests/test_views.py +++ b/src/snowflakes/tests/test_views.py @@ -337,9 +337,9 @@ def test_index_data_workbook(workbook, testapp, indexer_testapp, item_type): @pytest.mark.parametrize('item_type', TYPE_LENGTH) def test_profiles(testapp, item_type): - from jsonschema_serialize_fork import Draft4Validator + from jsonschema import Draft202012Validator res = testapp.get('/profiles/%s.json' % item_type).maybe_follow(status=200) - errors = Draft4Validator.check_schema(res.json) + errors = Draft202012Validator.check_schema(res.json) assert not errors From c49dac50b29e8aa60b300882d311cbf3024382c6 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 10:26:30 -0800 Subject: [PATCH 07/29] Update for tests --- src/snovault/tests/testing_views.py | 2 +- src/snowflakes/schemas/mixins.json | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/snovault/tests/testing_views.py b/src/snovault/tests/testing_views.py index 90c55dbd1..e69ede788 100644 --- a/src/snovault/tests/testing_views.py +++ b/src/snovault/tests/testing_views.py @@ -418,7 +418,7 @@ class TestingDependencies(Item): item_type = 'testing_dependencies' schema = { 'type': 'object', - 'dependencies': { + 'dependentRequired': { 'dep1': ['dep2'], 'dep2': ['dep1'], }, diff --git a/src/snowflakes/schemas/mixins.json b/src/snowflakes/schemas/mixins.json index efc6c3494..edc936661 100644 --- a/src/snowflakes/schemas/mixins.json +++ b/src/snowflakes/schemas/mixins.json @@ -1,12 +1,12 @@ { "title": "Mixin properties", "schema_version": { - "schema_version": { - "title": "Schema Version", - "comment": "Do not submit, value is assigned by the server. The version of the JSON schema that the server uses to validate the object. Schema version indicates generation of schema used to save version to to enable upgrade steps to work. Individual schemas should set the default.", - "type": "string", - "pattern": "^\\d+(\\.\\d+)*$", - "requestMethod": [] + "schema_version": { + "title": "Schema Version", + "comment": "Do not submit, value is assigned by the server. The version of the JSON schema that the server uses to validate the object. Schema version indicates generation of schema used to save version to enable upgrade steps to work. Individual schemas should set the default.", + "type": "string", + "pattern": "^\\d+(\\.\\d+)*$", + "permission": "import_items" } }, "uuid": { @@ -16,7 +16,7 @@ "format": "uuid", "serverDefault": "uuid4", "permission": "import_items", - "requestMethod": "POST" + "requestMethod": ["POST", "PATCH"] } }, "standard_status": { From e317714687864bc8bec4b25adb51f30feebdcebf Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 10:31:05 -0800 Subject: [PATCH 08/29] Use validator is_type --- src/snovault/schema_validation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index f113333da..c75f7433d 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -11,14 +11,14 @@ def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS['properties'] - def should_set_defaults(instance): - if isinstance(instance, dict): + def should_set_defaults(validator, instance): + if validator.is_type(instance, 'object'): return True return False def set_defaults(validator, properties, instance, schema): for property, subschema in properties.items(): - if not isinstance(subschema, dict): + if not validator.is_type(subschema, 'object'): continue if 'default' in subschema: instance.setdefault( @@ -37,7 +37,7 @@ def set_defaults(validator, properties, instance, schema): ) def properties_with_defaults(validator, properties, instance, schema): - if should_set_defaults(instance): + if should_set_defaults(validator, instance): set_defaults(validator, properties, instance, schema) yield from validate_properties(validator, properties, instance, schema) From 6074c36174221071f81e23f29cecbbe1f47bc5f5 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 14:47:18 -0800 Subject: [PATCH 09/29] Normalize linkTos in properties --- src/snovault/schema_utils.py | 3 -- src/snovault/schema_validation.py | 44 +++++++++++++++++++++++-- src/snovault/tests/test_schema_utils.py | 12 ++++--- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/snovault/schema_utils.py b/src/snovault/schema_utils.py index ec1beb8d2..9913ba215 100644 --- a/src/snovault/schema_utils.py +++ b/src/snovault/schema_utils.py @@ -121,9 +121,6 @@ def linkTo(validator, linkTo, instance, schema): yield ValidationError(error) return - # And normalize the value to a uuid - instance = str(item.uuid) - def linkFrom(validator, linkFrom, instance, schema): # avoid circular import diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index c75f7433d..3af3cb9c1 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -1,21 +1,60 @@ -# Draft202012Validator # "$schema": "https://json-schema.org/draft/2020-12/schema" +from copy import deepcopy from jsonschema import Draft202012Validator from jsonschema import validators -from copy import deepcopy +from jsonschema.exceptions import ValidationError +from pyramid.threadlocal import get_current_request +from pyramid.traversal import find_resource NO_DEFAULT = object() +def normalize_links(links): + request = get_current_request() + normalized_links = [] + errors = [] + for link in links: + try: + normalized_links.append( + str(find_resource(request.root, link).uuid) + ) + except KeyError: + errors.append( + ValidationError(f'Unable to resolve link: {link}') + ) + return normalized_links, errors + + +def maybe_normalize_links_to_uuids(validator, property, subschema, instance): + errors = [] + if 'linkTo' in subschema: + link = instance.get(property) + if link: + normalized_links, errors = normalize_links([link]) + instance[property] = normalized_links[0] + if 'linkTo' in subschema.get('items', {}): + links = instance.get(property, []) + if links: + normalized_links, errors = normalize_links(links) + instance[property] = normalized_links + for error in errors: + yield error + + def extend_with_default(validator_class): validate_properties = validator_class.VALIDATORS['properties'] def should_set_defaults(validator, instance): + return True if validator.is_type(instance, 'object'): return True return False + def maybe_normalize(validator, properties, instance, schema): + for property, subschema in properties.items(): + yield from maybe_normalize_links_to_uuids(validator, property, subschema, instance) + def set_defaults(validator, properties, instance, schema): for property, subschema in properties.items(): if not validator.is_type(subschema, 'object'): @@ -37,6 +76,7 @@ def set_defaults(validator, properties, instance, schema): ) def properties_with_defaults(validator, properties, instance, schema): + yield from maybe_normalize(validator, properties, instance, schema) if should_set_defaults(validator, instance): set_defaults(validator, properties, instance, schema) yield from validate_properties(validator, properties, instance, schema) diff --git a/src/snovault/tests/test_schema_utils.py b/src/snovault/tests/test_schema_utils.py index 28ac57c1c..334ac4626 100644 --- a/src/snovault/tests/test_schema_utils.py +++ b/src/snovault/tests/test_schema_utils.py @@ -16,9 +16,13 @@ def content(testapp): def test_uniqueItems_validates_normalized_links(content, threadlocals): schema = { - 'uniqueItems': True, - 'items': { - 'linkTo': 'TestingLinkTarget', + 'properties': { + 'some_links': { + 'uniqueItems': True, + 'items': { + 'linkTo': 'TestingLinkTarget', + } + } } } uuid = targets[0]['uuid'] @@ -26,7 +30,7 @@ def test_uniqueItems_validates_normalized_links(content, threadlocals): uuid, '/testing-link-targets/{}'.format(uuid), ] - validated, errors = validate(schema, data) + validated, errors = validate(schema, {'some_links': data}) assert len(errors) == 1 assert ( errors[0].message == "['{}', '{}'] has non-unique elements".format( From 013399603cbe5404be17db4d26d8bd9d1a9e36b3 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 15:17:38 -0800 Subject: [PATCH 10/29] Normalize in properties --- src/snovault/schema_validation.py | 73 +++++++++++++++++-------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 3af3cb9c1..e3a27d373 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -26,6 +26,19 @@ def normalize_links(links): return normalized_links, errors +def should_mutate_properties(validator, instance): + if validator.is_type(instance, 'object'): + return True + return False + + +def get_items_or_empty_object(subschema): + items = subschema.get('items', {}) + if isinstance(items, dict): + return items + return {} + + def maybe_normalize_links_to_uuids(validator, property, subschema, instance): errors = [] if 'linkTo' in subschema: @@ -33,7 +46,7 @@ def maybe_normalize_links_to_uuids(validator, property, subschema, instance): if link: normalized_links, errors = normalize_links([link]) instance[property] = normalized_links[0] - if 'linkTo' in subschema.get('items', {}): + if 'linkTo' in get_items_or_empty_object(subschema): links = instance.get(property, []) if links: normalized_links, errors = normalize_links(links) @@ -42,47 +55,41 @@ def maybe_normalize_links_to_uuids(validator, property, subschema, instance): yield error -def extend_with_default(validator_class): - validate_properties = validator_class.VALIDATORS['properties'] +def set_defaults(validator, property, subschema, instance): + if 'default' in subschema: + instance.setdefault( + property, + deepcopy(subschema['default']) + ) + if 'serverDefault' in subschema: + server_default = validator.server_default( + instance, + subschema + ) + if server_default is not NO_DEFAULT: + instance.setdefault( + property, + server_default + ) - def should_set_defaults(validator, instance): - return True - if validator.is_type(instance, 'object'): - return True - return False - def maybe_normalize(validator, properties, instance, schema): - for property, subschema in properties.items(): - yield from maybe_normalize_links_to_uuids(validator, property, subschema, instance) +def extend_with_default(validator_class): + validate_properties = validator_class.VALIDATORS['properties'] - def set_defaults(validator, properties, instance, schema): + def mutate_properties(validator, properties, instance, schema): for property, subschema in properties.items(): if not validator.is_type(subschema, 'object'): continue - if 'default' in subschema: - instance.setdefault( - property, - deepcopy(subschema['default']) - ) - if 'serverDefault' in subschema: - server_default = validator.server_default( - instance, - subschema - ) - if server_default is not NO_DEFAULT: - instance.setdefault( - property, - server_default - ) - - def properties_with_defaults(validator, properties, instance, schema): - yield from maybe_normalize(validator, properties, instance, schema) - if should_set_defaults(validator, instance): - set_defaults(validator, properties, instance, schema) + yield from maybe_normalize_links_to_uuids(validator, property, subschema, instance) + set_defaults(validator, property, subschema, instance) + + def before_properties_validation_hook(validator, properties, instance, schema): + if should_mutate_properties(validator, instance): + yield from mutate_properties(validator, properties, instance, schema) yield from validate_properties(validator, properties, instance, schema) return validators.extend( - validator_class, {'properties': properties_with_defaults}, + validator_class, {'properties': before_properties_validation_hook}, ) From 91dc6bc0e4967fb644c2a7709c59848953e782be Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 15:22:53 -0800 Subject: [PATCH 11/29] Use validator is_type --- src/snovault/schema_validation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index e3a27d373..40e75c8a7 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -32,9 +32,9 @@ def should_mutate_properties(validator, instance): return False -def get_items_or_empty_object(subschema): +def get_items_or_empty_object(validator, subschema): items = subschema.get('items', {}) - if isinstance(items, dict): + if validator.is_type(items, 'object'): return items return {} @@ -46,7 +46,7 @@ def maybe_normalize_links_to_uuids(validator, property, subschema, instance): if link: normalized_links, errors = normalize_links([link]) instance[property] = normalized_links[0] - if 'linkTo' in get_items_or_empty_object(subschema): + if 'linkTo' in get_items_or_empty_object(validator, subschema): links = instance.get(property, []) if links: normalized_links, errors = normalize_links(links) From cb1d5211cce15cd4dd7af9a225f9e40e61649450 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 15:27:14 -0800 Subject: [PATCH 12/29] Pass link back --- src/snovault/schema_validation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 40e75c8a7..2ef7f8f7c 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -23,6 +23,9 @@ def normalize_links(links): errors.append( ValidationError(f'Unable to resolve link: {link}') ) + normalized_links.append( + link + ) return normalized_links, errors From c3fbc30b3bf8d4e002de2014f02d27a3f9ec9e27 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 15:59:26 -0800 Subject: [PATCH 13/29] Update --- src/snovault/schema_utils.py | 1 - src/snovault/schema_validation.py | 27 +++++++++++++++++++++++---- src/snowflakes/schemas/mixins.json | 2 +- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/snovault/schema_utils.py b/src/snovault/schema_utils.py index 9913ba215..58566117e 100644 --- a/src/snovault/schema_utils.py +++ b/src/snovault/schema_utils.py @@ -93,7 +93,6 @@ def linkTo(validator, linkTo, instance, schema): error = "%r is not of type %s" % (instance, ", ".join(reprs)) yield ValidationError(error) return - linkEnum = schema.get('linkEnum') if linkEnum is not None: if not validator.is_type(linkEnum, "array"): diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 2ef7f8f7c..51788e826 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -10,14 +10,25 @@ NO_DEFAULT = object() -def normalize_links(links): +def get_resource_base(validator, linkTo): + from snovault import COLLECTIONS request = get_current_request() + collections = request.registry[COLLECTIONS] + if validator.is_type(linkTo, 'string'): + resource_base = collections.get(linkTo, request.root) + else: + resource_base = request.root + return resource_base + + +def normalize_links(validator, links, linkTo): + resource_base = get_resource_base(validator, linkTo) normalized_links = [] errors = [] for link in links: try: normalized_links.append( - str(find_resource(request.root, link).uuid) + str(find_resource(resource_base, link).uuid) ) except KeyError: errors.append( @@ -47,12 +58,20 @@ def maybe_normalize_links_to_uuids(validator, property, subschema, instance): if 'linkTo' in subschema: link = instance.get(property) if link: - normalized_links, errors = normalize_links([link]) + normalized_links, errors = normalize_links( + validator, + [link], + subschema.get('linkTo'), + ) instance[property] = normalized_links[0] if 'linkTo' in get_items_or_empty_object(validator, subschema): links = instance.get(property, []) if links: - normalized_links, errors = normalize_links(links) + normalized_links, errors = normalize_links( + validator, + links, + subschema.get('items').get('linkTo'), + ) instance[property] = normalized_links for error in errors: yield error diff --git a/src/snowflakes/schemas/mixins.json b/src/snowflakes/schemas/mixins.json index edc936661..f43f01cf1 100644 --- a/src/snowflakes/schemas/mixins.json +++ b/src/snowflakes/schemas/mixins.json @@ -16,7 +16,7 @@ "format": "uuid", "serverDefault": "uuid4", "permission": "import_items", - "requestMethod": ["POST", "PATCH"] + "requestMethod": ["POST", "PATCH", "PUT"] } }, "standard_status": { From 916fc36bcea2b68059701495d49ef8c8f8d11f95 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 16:05:38 -0800 Subject: [PATCH 14/29] Update tests --- src/snovault/tests/test_post_put_patch.py | 5 ++++- src/snovault/tests/testing_views.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/snovault/tests/test_post_put_patch.py b/src/snovault/tests/test_post_put_patch.py index 30a930509..19101f3d2 100644 --- a/src/snovault/tests/test_post_put_patch.py +++ b/src/snovault/tests/test_post_put_patch.py @@ -233,7 +233,10 @@ def test_put_object_child_validation(content_with_child, testapp): }] } res = testapp.put_json(content_with_child['@id'], edit, status=422) - assert res.json['errors'][0]['name'] == [u'reverse', 0, u'target'] + assert ['reverse', 0, 'target'] in [ + x['name'] + for x in res.json['errors'] + ] def test_put_object_validates_child_references(content_with_child, testapp): diff --git a/src/snovault/tests/testing_views.py b/src/snovault/tests/testing_views.py index e69ede788..ca508bd37 100644 --- a/src/snovault/tests/testing_views.py +++ b/src/snovault/tests/testing_views.py @@ -286,7 +286,7 @@ class TestingPostPutPatch(Item): "schema_version": { "type": "string", "pattern": "^\\d+(\\.\\d+)*$", - "requestMethod": [], + "requestMethod": ['POST'], "default": "1", }, "uuid": { @@ -341,7 +341,7 @@ class TestingServerDefault(Item): "schema_version": { "type": "string", "pattern": "^\\d+(\\.\\d+)*$", - "requestMethod": [], + "requestMethod": ['POST'], }, "uuid": { "title": "UUID", From 547dd072342adb907fca75cf49f662d33e2b08ac Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 16:12:14 -0800 Subject: [PATCH 15/29] Remove --- src/snowflakes/schemas/mixins.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/snowflakes/schemas/mixins.json b/src/snowflakes/schemas/mixins.json index f43f01cf1..dde6ea7bc 100644 --- a/src/snowflakes/schemas/mixins.json +++ b/src/snowflakes/schemas/mixins.json @@ -15,7 +15,6 @@ "type": "string", "format": "uuid", "serverDefault": "uuid4", - "permission": "import_items", "requestMethod": ["POST", "PATCH", "PUT"] } }, From b1c33bcb7559908dea887b560d537204d2f058f7 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 17:09:45 -0800 Subject: [PATCH 16/29] Fix test --- src/snowflakes/tests/test_permissions.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/snowflakes/tests/test_permissions.py b/src/snowflakes/tests/test_permissions.py index a0f33261a..47e8ff5f3 100644 --- a/src/snowflakes/tests/test_permissions.py +++ b/src/snowflakes/tests/test_permissions.py @@ -215,7 +215,16 @@ def test_snowflake_accession_put_admin(testapp, snowflake): assert res.json['errors'][0]['description'] == 'uuid may not be changed' -def test_snowflake_accession_put_submitter(submitter_testapp, snowflake): +def test_snowflake_accession_put_submitter(testapp, submitter, submitter_testapp, snowflake): + # Manually add submitter. + testapp.patch_json( + snowflake['@id'], + { + 'submitted_by': submitter['@id'] + }, + status=200 + ) + testapp.get(snowflake['@id']).json snowflake_id = snowflake['@id'] old_accession = snowflake['accession'] # Can't resubmit @type/@id. From 41a3dacce7e59b8430d9145a330cbd0c03635c36 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Wed, 2 Feb 2022 17:11:41 -0800 Subject: [PATCH 17/29] Update --- src/snowflakes/schemas/mixins.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snowflakes/schemas/mixins.json b/src/snowflakes/schemas/mixins.json index dde6ea7bc..cdb0b775d 100644 --- a/src/snowflakes/schemas/mixins.json +++ b/src/snowflakes/schemas/mixins.json @@ -6,7 +6,7 @@ "comment": "Do not submit, value is assigned by the server. The version of the JSON schema that the server uses to validate the object. Schema version indicates generation of schema used to save version to enable upgrade steps to work. Individual schemas should set the default.", "type": "string", "pattern": "^\\d+(\\.\\d+)*$", - "permission": "import_items" + "requestMethod": ["POST", "PATCH", "PUT"] } }, "uuid": { From a81844b566c0c9e73460b86befc5212e47e0b721 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Thu, 3 Feb 2022 20:44:43 -0800 Subject: [PATCH 18/29] Filter out requestMethod and permission violations from server defaults --- src/snovault/schema_utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/snovault/schema_utils.py b/src/snovault/schema_utils.py index 58566117e..eaeae05ec 100644 --- a/src/snovault/schema_utils.py +++ b/src/snovault/schema_utils.py @@ -311,6 +311,14 @@ def validate(schema, data, current=None): validated_value = validated_value[key] if validated_value == current_value: continue + # Also ignore requestMethod and permission errors from defaults. + if isinstance(error, IgnoreUnchanged): + current_value = data + try: + for key in error.path: + current_value = current_value[key] + except KeyError: + continue filtered_errors.append(error) return validated, filtered_errors From 8bf10ac3f151917d1d591b6ffa691c67fe1b529f Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Thu, 3 Feb 2022 20:47:28 -0800 Subject: [PATCH 19/29] Change back schema --- src/snovault/tests/testing_views.py | 4 ++-- src/snowflakes/schemas/mixins.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/snovault/tests/testing_views.py b/src/snovault/tests/testing_views.py index ca508bd37..e69ede788 100644 --- a/src/snovault/tests/testing_views.py +++ b/src/snovault/tests/testing_views.py @@ -286,7 +286,7 @@ class TestingPostPutPatch(Item): "schema_version": { "type": "string", "pattern": "^\\d+(\\.\\d+)*$", - "requestMethod": ['POST'], + "requestMethod": [], "default": "1", }, "uuid": { @@ -341,7 +341,7 @@ class TestingServerDefault(Item): "schema_version": { "type": "string", "pattern": "^\\d+(\\.\\d+)*$", - "requestMethod": ['POST'], + "requestMethod": [], }, "uuid": { "title": "UUID", diff --git a/src/snowflakes/schemas/mixins.json b/src/snowflakes/schemas/mixins.json index cdb0b775d..a90ca5e30 100644 --- a/src/snowflakes/schemas/mixins.json +++ b/src/snowflakes/schemas/mixins.json @@ -6,7 +6,7 @@ "comment": "Do not submit, value is assigned by the server. The version of the JSON schema that the server uses to validate the object. Schema version indicates generation of schema used to save version to enable upgrade steps to work. Individual schemas should set the default.", "type": "string", "pattern": "^\\d+(\\.\\d+)*$", - "requestMethod": ["POST", "PATCH", "PUT"] + "requestMethod": [] } }, "uuid": { @@ -15,7 +15,7 @@ "type": "string", "format": "uuid", "serverDefault": "uuid4", - "requestMethod": ["POST", "PATCH", "PUT"] + "requestMethod": ["POST"] } }, "standard_status": { From 5fddd4a27a29d3d1e2fc4afd7d5c0c355fecc585 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Thu, 3 Feb 2022 21:06:19 -0800 Subject: [PATCH 20/29] Update comment --- src/snovault/schema_utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/snovault/schema_utils.py b/src/snovault/schema_utils.py index eaeae05ec..10cb52db1 100644 --- a/src/snovault/schema_utils.py +++ b/src/snovault/schema_utils.py @@ -316,8 +316,12 @@ def validate(schema, data, current=None): current_value = data try: for key in error.path: + # If it's in original data then either user passed it in + # or it's from PATCH object with unchanged data. If it's + # unchanged then it's already been skipped above. current_value = current_value[key] except KeyError: + # If it's not in original data then it's filled in by defaults. continue filtered_errors.append(error) From f0f32278c1a92cfc5767a8ebac77049e4d5803ac Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 12:34:22 -0800 Subject: [PATCH 21/29] Update id attribute --- src/snowflakes/schemas/access_key.json | 2 +- src/snowflakes/schemas/award.json | 2 +- src/snowflakes/schemas/image.json | 2 +- src/snowflakes/schemas/lab.json | 2 +- src/snowflakes/schemas/page.json | 2 +- src/snowflakes/schemas/snowball.json | 2 +- src/snowflakes/schemas/snowflake.json | 2 +- src/snowflakes/schemas/snowfort.json | 2 +- src/snowflakes/schemas/snowset.json | 2 +- src/snowflakes/schemas/user.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/snowflakes/schemas/access_key.json b/src/snowflakes/schemas/access_key.json index d02a84983..cc1d4b324 100644 --- a/src/snowflakes/schemas/access_key.json +++ b/src/snowflakes/schemas/access_key.json @@ -1,6 +1,6 @@ { "title": "Admin access key", - "id": "/profiles/access_key_admin.json", + "$id": "/profiles/access_key_admin.json", "$schema": "http://json-schema.org/draft-04/schema#", "required": [], "additionalProperties": false, diff --git a/src/snowflakes/schemas/award.json b/src/snowflakes/schemas/award.json index 3c1426183..ae5b40970 100644 --- a/src/snowflakes/schemas/award.json +++ b/src/snowflakes/schemas/award.json @@ -1,6 +1,6 @@ { "title": "Grant", - "id": "/profiles/award.json", + "$id": "/profiles/award.json", "$schema": "http://json-schema.org/draft-04/schema#", "required": [ "name", "project", "rfa"], "identifyingProperties": ["uuid", "name", "title"], diff --git a/src/snowflakes/schemas/image.json b/src/snowflakes/schemas/image.json index 34945353e..8be79d1f0 100644 --- a/src/snowflakes/schemas/image.json +++ b/src/snowflakes/schemas/image.json @@ -1,7 +1,7 @@ { "title": "Image", "description": "Schema for images embedded in page objects", - "id": "/profiles/image.json", + "$id": "/profiles/image.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": [ "attachment" ], diff --git a/src/snowflakes/schemas/lab.json b/src/snowflakes/schemas/lab.json index 0ddfab0b1..ca0a73521 100644 --- a/src/snowflakes/schemas/lab.json +++ b/src/snowflakes/schemas/lab.json @@ -1,6 +1,6 @@ { "title": "Lab", - "id": "/profiles/lab.json", + "$id": "/profiles/lab.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": ["name", "title"], diff --git a/src/snowflakes/schemas/page.json b/src/snowflakes/schemas/page.json index cc9a20a99..27abfb96a 100644 --- a/src/snowflakes/schemas/page.json +++ b/src/snowflakes/schemas/page.json @@ -1,7 +1,7 @@ { "title": "Page", "description": "Schema for a portal page.", - "id": "/profiles/page.json", + "$id": "/profiles/page.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": [ "name", "title" ], diff --git a/src/snowflakes/schemas/snowball.json b/src/snowflakes/schemas/snowball.json index 0ef06e6d9..3db6238a1 100644 --- a/src/snowflakes/schemas/snowball.json +++ b/src/snowflakes/schemas/snowball.json @@ -2,7 +2,7 @@ "title": "Snowball", "description": "Schema for submitting metadata for a Snowball with 1 or more snowflakes", "comment": "An snowball is a special case of snowset.", - "id": "/profiles/snowball.json", + "$id": "/profiles/snowball.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": ["award", "lab"], diff --git a/src/snowflakes/schemas/snowflake.json b/src/snowflakes/schemas/snowflake.json index aec07f048..97d3a3b5b 100644 --- a/src/snowflakes/schemas/snowflake.json +++ b/src/snowflakes/schemas/snowflake.json @@ -1,7 +1,7 @@ { "title": "Snowflake ", "description": "Schema for submitting metadata for a snowflake", - "id": "/profiles/snowflake.json", + "$id": "/profiles/snowflake.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": ["snowset", "type","award", "lab"], diff --git a/src/snowflakes/schemas/snowfort.json b/src/snowflakes/schemas/snowfort.json index 088541f2f..a036a7df9 100644 --- a/src/snowflakes/schemas/snowfort.json +++ b/src/snowflakes/schemas/snowfort.json @@ -2,7 +2,7 @@ "title": "Snowfort", "description": "Schema for submitting metadata for a Snowfort with 1 or more snowflakes", "comment": "An snowfort is a special case of snowset.", - "id": "/profiles/snowfort.json", + "$id": "/profiles/snowfort.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": ["award", "lab"], diff --git a/src/snowflakes/schemas/snowset.json b/src/snowflakes/schemas/snowset.json index c393fff4c..41cbc20c9 100644 --- a/src/snowflakes/schemas/snowset.json +++ b/src/snowflakes/schemas/snowset.json @@ -1,7 +1,7 @@ { "title": "Snowset", "description": "Abstract schema class for collections of snowflakes", - "id": "/profiles/snowset.json", + "$id": "/profiles/snowset.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": [ "award", "lab"], diff --git a/src/snowflakes/schemas/user.json b/src/snowflakes/schemas/user.json index 81a2c77c5..b7ae1be8b 100644 --- a/src/snowflakes/schemas/user.json +++ b/src/snowflakes/schemas/user.json @@ -1,6 +1,6 @@ { "title": "User", - "id": "/profiles/user.json", + "$id": "/profiles/user.json", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": [ "email", "first_name", "last_name"], From 65934c423cccdb16eb039bd33fe7cc9eb08f74df Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 12:40:15 -0800 Subject: [PATCH 22/29] Update $schema --- src/snowflakes/schemas/access_key.json | 2 +- src/snowflakes/schemas/award.json | 2 +- src/snowflakes/schemas/image.json | 2 +- src/snowflakes/schemas/lab.json | 2 +- src/snowflakes/schemas/page.json | 2 +- src/snowflakes/schemas/snowball.json | 2 +- src/snowflakes/schemas/snowflake.json | 2 +- src/snowflakes/schemas/snowfort.json | 2 +- src/snowflakes/schemas/snowset.json | 2 +- src/snowflakes/schemas/user.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/snowflakes/schemas/access_key.json b/src/snowflakes/schemas/access_key.json index cc1d4b324..4d05b8706 100644 --- a/src/snowflakes/schemas/access_key.json +++ b/src/snowflakes/schemas/access_key.json @@ -1,7 +1,7 @@ { "title": "Admin access key", "$id": "/profiles/access_key_admin.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "required": [], "additionalProperties": false, "mixinProperties": [ diff --git a/src/snowflakes/schemas/award.json b/src/snowflakes/schemas/award.json index ae5b40970..38cde1e12 100644 --- a/src/snowflakes/schemas/award.json +++ b/src/snowflakes/schemas/award.json @@ -1,7 +1,7 @@ { "title": "Grant", "$id": "/profiles/award.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "required": [ "name", "project", "rfa"], "identifyingProperties": ["uuid", "name", "title"], "additionalProperties": false, diff --git a/src/snowflakes/schemas/image.json b/src/snowflakes/schemas/image.json index 8be79d1f0..eeeb8c80b 100644 --- a/src/snowflakes/schemas/image.json +++ b/src/snowflakes/schemas/image.json @@ -2,7 +2,7 @@ "title": "Image", "description": "Schema for images embedded in page objects", "$id": "/profiles/image.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": [ "attachment" ], "identifyingProperties": ["uuid"], diff --git a/src/snowflakes/schemas/lab.json b/src/snowflakes/schemas/lab.json index ca0a73521..e619be345 100644 --- a/src/snowflakes/schemas/lab.json +++ b/src/snowflakes/schemas/lab.json @@ -1,7 +1,7 @@ { "title": "Lab", "$id": "/profiles/lab.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["name", "title"], "identifyingProperties": ["uuid", "title", "name"], diff --git a/src/snowflakes/schemas/page.json b/src/snowflakes/schemas/page.json index 27abfb96a..66107d18b 100644 --- a/src/snowflakes/schemas/page.json +++ b/src/snowflakes/schemas/page.json @@ -2,7 +2,7 @@ "title": "Page", "description": "Schema for a portal page.", "$id": "/profiles/page.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": [ "name", "title" ], "identifyingProperties": ["uuid", "name" ], diff --git a/src/snowflakes/schemas/snowball.json b/src/snowflakes/schemas/snowball.json index 3db6238a1..9309a2eae 100644 --- a/src/snowflakes/schemas/snowball.json +++ b/src/snowflakes/schemas/snowball.json @@ -3,7 +3,7 @@ "description": "Schema for submitting metadata for a Snowball with 1 or more snowflakes", "comment": "An snowball is a special case of snowset.", "$id": "/profiles/snowball.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["award", "lab"], "identifyingProperties": ["uuid", "accession"], diff --git a/src/snowflakes/schemas/snowflake.json b/src/snowflakes/schemas/snowflake.json index 97d3a3b5b..255847d12 100644 --- a/src/snowflakes/schemas/snowflake.json +++ b/src/snowflakes/schemas/snowflake.json @@ -2,7 +2,7 @@ "title": "Snowflake ", "description": "Schema for submitting metadata for a snowflake", "$id": "/profiles/snowflake.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["snowset", "type","award", "lab"], "identifyingProperties": ["uuid", "accession"], diff --git a/src/snowflakes/schemas/snowfort.json b/src/snowflakes/schemas/snowfort.json index a036a7df9..ecc54bf2b 100644 --- a/src/snowflakes/schemas/snowfort.json +++ b/src/snowflakes/schemas/snowfort.json @@ -3,7 +3,7 @@ "description": "Schema for submitting metadata for a Snowfort with 1 or more snowflakes", "comment": "An snowfort is a special case of snowset.", "$id": "/profiles/snowfort.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["award", "lab"], "identifyingProperties": ["uuid", "accession"], diff --git a/src/snowflakes/schemas/snowset.json b/src/snowflakes/schemas/snowset.json index 41cbc20c9..6a56331c9 100644 --- a/src/snowflakes/schemas/snowset.json +++ b/src/snowflakes/schemas/snowset.json @@ -2,7 +2,7 @@ "title": "Snowset", "description": "Abstract schema class for collections of snowflakes", "$id": "/profiles/snowset.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": [ "award", "lab"], "identifyingProperties": ["uuid", "accession"], diff --git a/src/snowflakes/schemas/user.json b/src/snowflakes/schemas/user.json index b7ae1be8b..e7b48414a 100644 --- a/src/snowflakes/schemas/user.json +++ b/src/snowflakes/schemas/user.json @@ -1,7 +1,7 @@ { "title": "User", "$id": "/profiles/user.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": [ "email", "first_name", "last_name"], "identifyingProperties": ["uuid", "email"], From 867d5dfc26e4107a293e81e999da9f14e9d3b6b9 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 12:41:23 -0800 Subject: [PATCH 23/29] Update --- src/snovault/tests/test_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snovault/tests/test_validator.py b/src/snovault/tests/test_validator.py index 29223adb1..30a5d713d 100644 --- a/src/snovault/tests/test_validator.py +++ b/src/snovault/tests/test_validator.py @@ -11,7 +11,7 @@ fake_schema = { - 'id': 'abc', + '$id': 'abc', 'title': 'Fake', 'description': 'Schema', '$schema': 'https://json-schema.org/draft/2020-12/schema', From acccb3eca9e296e6f2145221677e330171de4915 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 13:45:27 -0800 Subject: [PATCH 24/29] Update dependencies keyword --- src/snowflakes/schemas/snowball.json | 2 +- src/snowflakes/schemas/snowflake.json | 2 +- src/snowflakes/schemas/snowfort.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/snowflakes/schemas/snowball.json b/src/snowflakes/schemas/snowball.json index 9309a2eae..60af6e572 100644 --- a/src/snowflakes/schemas/snowball.json +++ b/src/snowflakes/schemas/snowball.json @@ -16,7 +16,7 @@ { "$ref": "mixins.json#/submitted" }, { "$ref": "snowset.json#/properties" } ], - "dependencies": { + "dependentSchemas": { "status": { "oneOf": [ { diff --git a/src/snowflakes/schemas/snowflake.json b/src/snowflakes/schemas/snowflake.json index 255847d12..ba7d24a11 100644 --- a/src/snowflakes/schemas/snowflake.json +++ b/src/snowflakes/schemas/snowflake.json @@ -26,7 +26,7 @@ "title": "Lab" } }, - "dependencies": { + "dependentSchemas": { "external_accession": { "not": { "required": ["accession"] diff --git a/src/snowflakes/schemas/snowfort.json b/src/snowflakes/schemas/snowfort.json index ecc54bf2b..0eed546bf 100644 --- a/src/snowflakes/schemas/snowfort.json +++ b/src/snowflakes/schemas/snowfort.json @@ -16,7 +16,7 @@ { "$ref": "mixins.json#/submitted" }, { "$ref": "snowset.json#/properties" } ], - "dependencies": { + "dependentSchemas": { "status": { "oneOf": [ { From ad3329168ec5d670d85887d053e65d346342ec66 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 14:14:27 -0800 Subject: [PATCH 25/29] Remove comment --- src/snovault/schema_validation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 51788e826..7981be2f1 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -1,4 +1,3 @@ -# "$schema": "https://json-schema.org/draft/2020-12/schema" from copy import deepcopy from jsonschema import Draft202012Validator from jsonschema import validators From bffb292477851237893099731783e156b3f74ad3 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 14:47:38 -0800 Subject: [PATCH 26/29] Also install format checkers --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d27a51731..02590d96b 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ "elasticsearch-dsl==5.4.0", "elasticsearch==5.4.0", "future==0.18.2", - "jsonschema==4.4.0", + "jsonschema[format]==4.4.0", "lucenequery==0.1", "passlib==1.7.2", "psutil==5.6.7", From 59839dc289b03ed49b9f4717048b4ed0098cb653 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 14:58:42 -0800 Subject: [PATCH 27/29] Encode links like in utils --- src/snovault/schema_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snovault/schema_validation.py b/src/snovault/schema_validation.py index 7981be2f1..6012f97f2 100644 --- a/src/snovault/schema_validation.py +++ b/src/snovault/schema_validation.py @@ -27,7 +27,7 @@ def normalize_links(validator, links, linkTo): for link in links: try: normalized_links.append( - str(find_resource(resource_base, link).uuid) + str(find_resource(resource_base, link.replace(':', '%3A')).uuid) ) except KeyError: errors.append( From e8156d48696a3e6f381999a1e10b7fabdb9c67a8 Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Fri, 4 Feb 2022 19:04:04 -0800 Subject: [PATCH 28/29] Update requests version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 02590d96b..3256c994d 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ "rdflib-jsonld==0.6.0", "rdflib==4.2.2", "redis==3.5.3", - "requests==2.22.0", + "requests==2.27.1", "simplejson==3.17.0", "snovault-search==1.0.4", "transaction==3.0.0", From 70d383b95ea194d5d37c6dbe4502dd5b1b43c0ec Mon Sep 17 00:00:00 2001 From: Keenan Graham Date: Tue, 8 Feb 2022 11:43:08 -0800 Subject: [PATCH 29/29] Use $id --- src/snovault/schema_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/snovault/schema_views.py b/src/snovault/schema_views.py index 1a6428bba..6fdca3169 100644 --- a/src/snovault/schema_views.py +++ b/src/snovault/schema_views.py @@ -78,8 +78,8 @@ def schemas_map(context, request): types = request.registry[TYPES] profiles_map = {} for type_info in types.by_item_type.values(): - if 'id' in type_info.schema: - profiles_map[type_info.name] = type_info.schema['id'] + if '$id' in type_info.schema: + profiles_map[type_info.name] = type_info.schema['$id'] profiles_map['@type'] = ['JSONSchemas'] return profiles_map