def required_draft4(validator, required, instance, schema): if not validator.is_type(instance, "object"): return for property in required: if property not in instance: error = ValidationError("%r is a required property" % property) error._set(validator="required", validator_value=required, instance=instance, schema=schema) error.path.appendleft(property) error.schema_path.extend([property, "required"]) yield error
def properties_draft3(validator, properties, instance, schema): if not validator.is_type(instance, "object"): return for property, subschema in iteritems(properties): if property in instance: for error in validator.descend(instance[property], subschema, path=property, schema_path=property): yield error elif subschema.get("required", False): error = ValidationError("%r is a required property" % property) error._set(validator="required", validator_value=subschema["required"], instance=instance, schema=schema) error.path.appendleft(property) error.schema_path.extend([property, "required"]) yield error
def type(validator, data_type, instance, schema): if instance is None: return if not validator.is_type(instance, data_type): yield ValidationError("%r is not of type %s" % (instance, data_type))
def is_date(validator, value, instance, schema_profile_data): try: datetime.datetime.strptime(instance, '%Y-%m-%d') except ValueError: yield ValidationError("%r incorrect birth date format" % (instance))
def not_draft4(validator, not_schema, instance, schema): if validator.is_valid(instance, not_schema): yield ValidationError("%r is not allowed for %r" % (not_schema, instance))
def minProperties_draft4(validator, mP, instance, schema): if validator.is_type(instance, "object") and len(instance) < mP: yield ValidationError("%r does not have enough properties" % (instance, ))
def type_draft4(validator, types, instance, schema): types = _utils.ensure_list(types) if not any(validator.is_type(instance, type) for type in types): yield ValidationError(_utils.types_msg(instance, types))
def enum(validator, enums, instance, schema): if instance not in enums: yield ValidationError("%r is not one of %r" % (instance, enums))
def minLength(validator, mL, instance, schema): if validator.is_type(instance, "string") and len(instance) < mL: yield ValidationError("%r is too short" % (instance, ))
def getValidatedValue(token, value, **kwargs): ''' Returns the provided value if it passed a validation laboratory against the token's model schema or ``None`` if the laboratory failed. Please refer to http://json-schema.org/documentation.html or https://spacetelescope.github.io/understanding-json-schema for more insights on JSON schema. :param str token: The token :param value: The value :returns value: The provided value if valid or ``None`` if validation failed ''' # Only for private functions that require an exception instead of ``None`` as return value exception = bool(kwargs.get('exception')) or False try: # This will be a temporarily copy of the value to validate (in a prepared form) dataobject = None # Get the domain and key from the provided token try: domain, key = str(token).split('.', 1) except ValueError: domain = str(token).split('.', 1)[0] key = False # Get the default class cls = locate(f'{__package__}.{domain}') # Validate a value against a sub-schema (of a schema module in freedm.models) if cls and key: tokens = key.split('.') # In case of the following tokens: "model.+" if len(tokens) >= 1 and tokens[0] == '+': # Recursively check the values with a token where '+' is substituted checks = [] for v in value: k = next(iter(v)) checks.append( getValidatedValue(f'{domain}.{k}', v[k], exception=True)) # Check for an exception if any(isinstance(e, Exception) for e in checks): value = ([e for e in checks if isinstance(e, Exception)] if exception else None) # In case of the following tokens: "model.property" elif len(tokens) >= 1 and tokens[0] in ( (cls.get('properties') or cls) if isinstance(cls, dict) else cls.__dict__): # Get data schema schema = (cls.get('properties') or cls)[tokens.pop(0)] if isinstance( cls, dict) else getattr(cls, tokens.pop(0)) # Traverse through subschemas for each token for i, t in enumerate(tokens): # Get index position last = (len(tokens) - i) == 1 # Try to find a sub-schema definition for current key token try: # 1st possibility: Value is member of a collection if (t.isdigit() or t == '[]') and schema.get('type') == 'array': # We use the previous schema again if last and schema.get('type') == 'array': obj = schema # Try to find the correct items or properties sub-schema else: obj = schema.get('items') # 2nd possibility: Value is a wildcard collection. Iteratively check each item elif t == '+': # Recursively check the values with a token where '+' is substituted checks = [] token_prev_part = f'{domain}.{".".join(key.split(".")[:i+1])}' token_next_part = '.'.join( key.split('.')[i + 2:] if not last else '') token_placeholder = '.'.join([ p for p in [token_prev_part, '{}', token_next_part] if p != '' ]) if hasattr(value, '__iter__'): for v in value: # If the values are dictionaries if isinstance(v, dict): # If the "+" was the last key token and the dictionary has more than one key like {a:1, b:2,...}, # then we should not substitute the "+" but rather remove and check the whole structure if last and len(v.keys()) > 1: checks.append( getValidatedValue( token_placeholder.replace( '.{}', ''), v, exception=True)) # Do a normal check by substituting the "+ for instance by "[]" or the one and only key in the dictionary else: # Get the key of the value value_key = next(iter(v)) # 1st check: Assume we can replace "+" by the value key check_token = token_placeholder.format( value_key) check_result = getValidatedValue( check_token, v[value_key], exception=True) # 2nd check: Assume we must replace "+" by a collection "[]" if (isinstance(check_result, list) and any( isinstance( r, Exception) for r in check_result) ) or isinstance( check_result, Exception): check_token = token_placeholder.format( '[]') # Save 2nd check result checks.append( getValidatedValue( check_token, [v], exception=True)) # 1st check has succeeded else: checks.append(check_result) # If the values are lists elif isinstance(v, list): check_token = token_placeholder.format( '[]') checks.append( getValidatedValue(check_token, v, exception=True)) # If the values are for instance strings else: check_token = token_placeholder.format( '[]') check_result = getValidatedValue( check_token, v, exception=True) checks.append(check_result) if isinstance(check_result, Exception): break # Do not check more than one single character of a string when the token already fails else: check_token = token_placeholder.format('[]') checks.append( getValidatedValue(check_token, value, exception=True)) # Check for an exception if any(isinstance(e, Exception) for e in checks): value = ([ e for e in checks if isinstance(e, Exception) ] if exception else None) # We stop at this moment as we just validated the wildcard results separately in recursive steps return # 3rd possibility: Value refers to a single property, and is not member of a collection else: # Try to get sub-schema obj = schema.get(t) # Did not work? Try sub-schema definitions (properties/items) if obj is None: p = schema.get('properties') i = schema.get('items') if p: obj = p.get(t) elif i: if isinstance(i, dict): obj = i.get('properties').get(t) # This is a last check for special situation where we refer to one item in a previous collection "[]" # The key token also must be a digit number and no schema found yet. if obj is None and '[]' in token and t.isdigit(): obj = schema # Set found schema or raise an exception if obj is not None: schema = obj else: raise Exception # Or try using the last schema we found for the previous token except: if schema.__contains__( 'additionalProperties' ) and schema.get('additionalProperties') is False: # We reset the value to None because new additional properties are not allowed if exception: value = ValidationError( 'No validation schema found for token "{}" and new schema properties not allowed by model' ) else: value = None raise UserWarning( f'No validation schema found for token "{token}" and model "{domain}" does not allow new schema properties' ) else: if schema.get('type') == 'object': # We warn the user but as we have additionalProperties allowed (object's default), we continue using the value with the new token raise UserWarning( f'No validation schema found for token "{token}". Define model schema in "freedm.models.{domain}.py"' ) else: # Validate against the last schema we found raise ValidationError( f'Token "{t}" refers to invalid sub-element to property of type "{schema.get("type")}"' ) # Prepare the data (To get rid of dictionaries with numeric keys) dataobject = __prepareCollectionObject(value) # Make sure that single members of a collection (=Token must contain a number) are properly packed for validation if schema.get('type') == 'array' and any( d in token for d in '0123456789') and not isinstance( dataobject, list): # Value is a single collection element, we need to pack it in a list validate([dataobject], schema) # Make sure that all single items in a collection are each individually checked against the schema elif schema.get( 'type') != 'array' and '[]' in token and isinstance( dataobject, list): # Validate each element individually for v in dataobject: validate(v, schema) # The normal case where a value is checked against its schema else: validate(dataobject, schema) else: raise UserWarning( f'No validation schema found for token "{domain}.{tokens[0]}.*". Define schema in "freedm.models.{domain}.py"' ) # Validate against a root schema, for instance just "model", without any further key like "model.?" (schema module in freedm.models) elif cls: # The dictionary is the schema if isinstance(cls, dict): validate(value, cls) # Build a schema for each property we find in the class else: # Build one JSON schema of all schema properties schema = {} for key in [k for k in cls.__dict__.keys() if k[:1] != '_']: schema.update({key: getattr(cls, key)}) # We validate a data structure (object) if isinstance(value, dict): # Try validating the domain data with the found schemas s_keys = schema.keys() v_keys = value.keys() s_diff = list(set(s_keys) - set(v_keys)) v_diff = list(set(v_keys) - set(s_keys)) if len(s_diff) > 0: raise UserWarning( f'Domain data does not define values for: {", ".join(s_diff)}' ) elif len(v_diff) > 0: raise ValidationError( f'Domain data should not contain these additional values ({", ".join(v_diff)})' ) else: for v in v_keys: # Prepare the data (To get rid of dictionaries with numeric keys) dataobject = __prepareCollectionObject(value[v]) # Validate validate(dataobject, schema[v]) # We validate just one single value else: validate(value, schema) # If no class is found at all, make sure we raise an exception and return "None" else: raise UserWarning( f'No validation schema found for token "{domain}.*". Define schema in "freedm.models.{domain}.py"' ) except UserWarning as uw: logger = logging.getLogger(str(os.getpid())) logger.debug(uw) except SchemaError as se: logger = logging.getLogger(str(os.getpid())) logger.warn( f'Validation schema for token "{token}" is malformed ({se.message})' ) except ValidationError as ve: try: if not exception: logger = logging.getLogger(str(os.getpid())) logger.warn( f'{"Schema" if token.isalpha() else "Sub-Schema"} "{token}" validation with value "{ellipsis(str(value), 40)}" failed ({ve.message})' ) finally: # Make sure a "None" will be returned on Validation errors in any case value = None if not exception else ve except Exception as e: pass finally: # Delete the temporary data object, created for validation del dataobject # Return the value in any case (None if a ValidationError occured) return value
def const(validator, const, instance, schema): if not equal(instance, const): yield ValidationError("%r was expected" % (const,))
def wrapper(self, *args, **kwargs): for field, error in fields.items(): if field not in self: raise ValidationError(error) return f(self, *args, **kwargs)
def discriminator_validator(swagger_spec, validator, discriminator_attribute, instance, schema): """ Validates instance against the schema defined by the discriminator attribute. [Swagger 2.0 Schema Object](http://swagger.io/specification/#schemaObject) allows discriminator field to be defined. discriminator field defines the attribute that will be used to discriminate the object type. NOTE: discriminator_validator assumes that discriminator_attribute is not None or empty :param swagger_spec: needed for access to deref() :type swagger_spec: :class:`bravado_core.spec.Spec` :param validator: Validator class used to validate the object :type validator: :class: `Swagger20Validator` or :class: `jsonschema.validators.Draft4Validator` :param discriminator_attribute: name of the discriminator attribute :type discriminator_attribute: str :param instance: object instance value :type instance: dict :param schema: swagger spec for the object :type schema: dict """ try: discriminator_value = instance[discriminator_attribute] except KeyError: raise ValidationError( "'{}' is a required property".format(discriminator_attribute)) if discriminator_value not in swagger_spec.definitions: raise ValidationError( message='\'{}\' is not a recognized schema'.format( discriminator_value), ) if discriminator_value == schema[MODEL_MARKER]: return discriminated_schema = swagger_spec.definitions[ discriminator_value]._model_spec if 'allOf' not in discriminated_schema: raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format( discriminator_value, schema[MODEL_MARKER], ), ) schemas_to_remove = [ s for s in discriminated_schema['allOf'] if swagger_spec.deref(s) == schema ] if not schemas_to_remove: # Not checking against len(schemas_to_remove) > 1 because it should be prevented by swagger spec validation raise ValidationError( message='discriminated schema \'{}\' must inherit from \'{}\''. format( discriminator_value, schema[MODEL_MARKER], ), ) # Remove the current schema from the allOf list in order to avoid unbounded recursion # (the current object is already validated against schema) # WARNING: This is especially important if internally_dereference_refs is set to true # as we're modifying new_schema and new_schema is a dict (so mutable) we need to copy # it in order to have a brand new dictionary that we can modify new_schema = discriminated_schema.copy() new_schema['allOf'] = [ all_of_schema if all_of_schema not in schemas_to_remove else {} for all_of_schema in new_schema['allOf'] ] from bravado_core.validate import validate_object # Local import due to circular dependency validate_object(swagger_spec=swagger_spec, object_spec=new_schema, value=instance)
def post(self): """ This is the POST method used to insert new book Request: URL : POST http://localhost:8080/api/v1/books Param Data : "book": { "name": "My First Book", "isbn": "123-3213243567", "authors": [ "John Doe" ], "number_of_pages": 350, "publisher": "Acme Books", "country": "United States", "release_date": "2019-08-01", } Response: [ "status_code": 201, "status": "success", "data": [ "book": { "name": "My First Book", "isbn": "123-3213243567", "authors": [ "John Doe" ], "number_of_pages": 350, "publisher": "Acme Books", "country": "United States", "release_date": "2019-08-01", }, ] ] """ try: logger.info("Entering InternalBooksController POST method") if not request.content_type == 'application/json': raise ValidationError("Given content type : {} is not acceptable." " Accepted content type is application/json ".format(request.content_type)) request_data = request.get_json() validate(request_data, post_schema, format_checker=jsonschema.FormatChecker()) int_bk_obj = InternalBooks() result = int_bk_obj.insert_book(request_data) return result logger.info("Existing InternalBooksController POST method") except ValueError as err: logger.exception("Value Error :: {}".format(err)) abort(400, str(err)) except ValidationError as err: logger.exception("Validation Error :: {}".format(err)) abort(400, err.message) except Exception as err: logger.exception("Un-handled Exception :: {}".format(err)) abort(500, "Internal Server Error")
def validator_form(validator, form, instance, schema, _from_items=False): """ - Silk form validators - Every "form" property must be either absent or present - If present, the form must have exactly that value - Or, the "form" property must be a list, and have one of the values in the list Or, if numeric, it must have one of the values between any two adjacent ascending list items - The above applies a bit differently for shape, as it is already a list: - Or, the "form" property must be a list of lists. The property must have the same length. For each item in the lists-of-lists, the property must have one of the values, or be between any two adjacent ascending list items. - Validation on "strides" works through exact match of the strides value. Note that "strides" is only present if "contiguous" is absent (and vice versa) """ def _allowed_value(schema_value, instance_value): if isinstance(schema_value, (list, tuple)): if instance_value in schema_value: return True for n in range(len(schema_value)-1): left, right = schema_value[n:n+2] if instance_value > left and instance_value < right: return True return False else: return schema_value == instance_value if isinstance(instance, FormWrapper): instance_storage, instance_form = instance._storage, instance._form else: #TODO:BAD instance_storage, instance_form = get_form(instance) form_str = indent(pprint.pformat(instance_form, width=72)) if instance_form is not None and "storage" in instance_form: storage_form = instance_form["storage"] for error in _validator_storage(storage_form, instance_storage, form_str): yield error if instance_storage is None: instance_storage = storage_form.get("form") if instance_storage is None: return if _from_items: form_str += "\n(on items)" binary_form_props = ("unsigned", "shape", "bytesize", "strides", "ndim") for key, value in sorted(form.items(),key=lambda item:item[0]): if key in binary_form_props and not instance_storage.endswith("binary"): continue missing_key = None if key == "ndim": if "shape" not in instance_form: missing_key = "'shape' (needed by ndim)" else: if key not in instance_form: missing_key = "'" + key + "'" if missing_key: msg = textwrap.dedent(""" No form property '%s' On form: %s """.rstrip() ) % (missing_key, form_str) yield ValidationError(msg) if key != "ndim": instance_value = instance_form[key] ok = True if key == "ndim": instance_value = len(instance_form["shape"]) if instance_value != value: ok = False elif key == "strides": if value != instance_value: ok = False elif key == "shape": assert len(value) == len(instance_value) #TODO: check before for inconsistent shape/ndim requirement for schema_dim, instance_dim in zip(value, instance_value): if schema_dim == -1: continue if not _allowed_value(schema_dim, instance_dim): ok = False elif isinstance(instance_value, _types["number"]): if isinstance(value, _types["number"]): if not _allowed_value(value, instance_value): ok = False else: if value != instance_value: ok = False if not ok: msg = textwrap.dedent(""" Form property '%s' has value %r, not %r On form: %s """.rstrip() ) % (key, instance_value, value, form_str) yield ValidationError(msg) if not _from_items and is_numpy_structure_schema(schema): assert instance_storage is not None, schema if "items" not in instance_form: msg = textwrap.dedent(""" No form property 'items' On form: %s """.rstrip() ) % (form_str,) yield ValidationError(msg) return form_wrapper = FormWrapper(None, instance_form["items"], instance_storage) items_form = schema.get("items", {}).get("form" , {}) for error in validator_form( validator, items_form, form_wrapper, schema, _from_items=True ): yield error
def contains(validator, contains, instance, schema): if not validator.is_type(instance, "array"): return if not any(validator.is_valid(element, contains) for element in instance): yield ValidationError("XXX")
def format(validator, format, instance, schema): if validator.format_checker is not None: try: validator.format_checker.check(instance, format) except FormatError as error: yield ValidationError(error.message, cause=error.cause)
def _config_get_validators_fail_bad_params(temp_dir, key_): # calling str() on ValidationErrors returns more detailed into about the error _config_get_validators_fail('', temp_dir, ValidationError("'' is not of type 'object'")) _config_get_validators_fail( ['foo', 'bar'], temp_dir, ValidationError("['foo', 'bar'] is not of type 'object'")) _config_get_validators_fail( {'key': 'y'}, temp_dir, ValidationError( "Additional properties are not allowed ('key' was unexpected)")) _config_get_validators_fail({key_: 'y'}, temp_dir, ValidationError("'y' is not of type 'object'")) _config_get_validators_fail({key_: { 'y': ['foo'] }}, temp_dir, ValidationError("['foo'] is not of type 'object'")) _config_get_validators_fail({key_: { 'y': { 'key_metadata': { 'a': 'b' } } }}, temp_dir, ValidationError("'validators' is a required property")) _config_get_validators_fail({key_: { 'y': { 'randomkey': { 'a': 'b' } } }}, temp_dir, ValidationError("'validators' is a required property")) _config_get_validators_fail({key_: { 'key': { 'validators': {} } }}, temp_dir, ValidationError("{} is not of type 'array'")) _config_get_validators_fail({key_: { 'key': { 'validators': ['foo'] } }}, temp_dir, ValidationError("'foo' is not of type 'object'")) _config_get_validators_fail( { key_: { 'key': { 'validators': [{ 'module': 'foo', 'callable_builder': 'bar' }], 'key_metadata': [] } } }, temp_dir, ValidationError("[] is not of type 'object'")) _config_get_validators_fail( { key_: { 'key': { 'validators': [{ 'module': 'foo', 'callable_builder': 'bar' }], 'key_metadata': { 'a': {} } } } }, temp_dir, ValidationError( "{} is not of type 'number', 'boolean', 'string', 'null'")) _config_get_validators_fail({key_: { 'key': { 'validators': [{}] } }}, temp_dir, ValidationError("'module' is a required property")) _config_get_validators_fail( {key_: { 'key': { 'validators': [{ 'module': 'foo' }] } }}, temp_dir, ValidationError("'callable_builder' is a required property")) _config_get_validators_fail( { key_: { 'key': { 'validators': [{ 'module': 'foo', 'callable_builder': 'baz', 'callable-builder': 'bar' }] } } }, temp_dir, ValidationError( "{'callable-builder': 'bar', 'callable_builder': 'baz', 'module': 'foo'} is valid " + "under each of {'required': ['callable-builder']}, {'required': ['callable_builder']}" )) _config_get_validators_fail( { key_: { 'key': { 'validators': [{ 'module': 'foo', 'callable_builder': 'bar', 'prefix': 1 }] } } }, temp_dir, ValidationError( "Additional properties are not allowed ('prefix' was unexpected)")) _config_get_validators_fail( { key_: { 'key': { 'validators': [{ 'module': ['foo'], 'callable_builder': 'bar' }] } } }, temp_dir, ValidationError("['foo'] is not of type 'string'")) _config_get_validators_fail( { key_: { 'key': { 'validators': [{ 'module': 'foo', 'callable_builder': ['bar'] }] } } }, temp_dir, ValidationError("['bar'] is not of type 'string'")) _config_get_validators_fail( { key_: { 'key': { 'validators': [{ 'module': 'foo', 'callable_builder': 'bar', 'parameters': 'foo' }] } } }, temp_dir, ValidationError("'foo' is not of type 'object'"))
def maxLength(validator, mL, instance, schema): if validator.is_type(instance, "string") and len(instance) > mL: yield ValidationError("%r is too long" % (instance, ))
def isValid(self,validator,nslist,origValue,schema): found = False checkedPatterns = [] matchType = str(schema.get(self.MatchTypeAttrName,'loose')) if schema is not None else 'canonical' if matchType not in CurieSearch.VALID_MATCHES: raise ValidationError("attribute '{0}' is {1} but it must be one of the next values: {2}".format(self.MatchTypeAttrName,matchType,CurieSearch.VALID_MATCHES.keys())) cache = self.GetCurieCache(cachePath=self.config.get('cacheDir')) parsed = None try: parsed = rfc3987.parse(origValue, rule="URI") except BaseException as be: if matchType != 'loose': raise be # Trying to decide the matching mode prefix = None if parsed: prefix = parsed.get('scheme') if prefix: if len(prefix) > 0: if matchType == 'loose': matchType = 'canonical' # We have to enforce lowercase schemes if prefix.lower() != prefix: raise ValidationError('The namespace of {} must be in lower case'.format(origValue)) if nslist: # The restricted namespaces list could have some of them in capitals l_nslist = list(map(lambda x: None if x is None else x.lower() if isinstance(x,str) else str(x).lower(), nslist)) else: l_nslist = nslist if matchType == 'basic': # Basic mode is like canonical, but without querying identifiers.org cache found = l_nslist and (prefix in l_nslist) elif matchType == 'loose': if l_nslist: valToVal = origValue validatedCURIEs = list(filter(lambda curie: curie is not None,map(lambda namespace: cache.get(namespace),l_nslist))) if not validatedCURIEs: raise ValidationError('No namespace from {} was found in identifiers.org cache'.format(nslist)) # Looking for a match for curie in validatedCURIEs: checkedPatterns.append(curie.pattern) pat = re.compile(curie.pattern) if pat.search(valToVal): found = True break else: raise ValidationError('In "loose" mode, at least one namespace must be declared') elif prefix is None: raise ValidationError('In "canonical" mode, the value must be prefixed by the namespace') else: # Searching in canonical mode. To do that, we have to remove the prefix valToVal = origValue[(origValue.find(':')+1):] # The case where the namespace list is empty if l_nslist and (prefix not in l_nslist): raise ValidationError('The namespace {} is not in the list of the accepted ones: {}'.format(prefix,nslist)) curie = cache.get(prefix) if not curie: raise ValidationError('The namespace {} was not found in identifiers.org cache'.format(prefix)) checkedPatterns.append(curie.pattern) pat = re.compile(curie.pattern) found = pat.search(valToVal) or pat.search(origValue) return found, checkedPatterns
def disallow_draft3(validator, disallow, instance, schema): for disallowed in _utils.ensure_list(disallow): if validator.is_valid(instance, {"type": [disallowed]}): yield ValidationError("%r is disallowed for %r" % (disallowed, instance))
def create(cls, data, id_=None, version_of=None): """Create a deposit with the optional id. :params version_of: PID of an existing record. If set, the new record will be marked as a new version of this referenced record. If no data is provided the new record will be a copy of this record. Note: this PID must reference the current last version of a record. """ # check that the status field is not set if 'publication_state' in data: raise InvalidDepositError( 'Field "publication_state" cannot be set.') data['publication_state'] = PublicationStates.draft.name # Set record's schema if '$schema' in data: raise InvalidDepositError('"$schema" field should not be set.') # Retrieve reserved record PID which should have already been created # by the deposit minter (The record PID value is the same # as the one of the deposit) rec_pid = RecordUUIDProvider.get(data['_deposit']['id']).pid version_master, prev_version = None, None # if this is a new version of an existing record, add the future # record pid in the chain of versions. if version_of: version_master, prev_version = \ find_version_master_and_previous_record(version_of) # The new version must be in the same community if data['community'] != prev_version['community']: raise ValidationError( 'The community field cannot change between versions.') try: version_master.insert_draft_child(rec_pid) except Exception as exc: # Only one draft is allowed per version chain. if 'Draft child already exists for this relation' in \ exc.args[0]: raise DraftExistsVersioningError( version_master.draft_child ) raise exc else: # create parent PID parent_pid = RecordUUIDProvider.create().pid version_master = PIDVersioning(parent=parent_pid) version_master.insert_draft_child(child=rec_pid) # Mint the deposit with the parent PID data['_pid'] = [{ 'value': version_master.parent.pid_value, 'type': RecordUUIDProvider.parent_pid_type, }] if 'community' not in data or not data['community']: raise ValidationError( 'Record\s metadata has no community field.') try: community_id = uuid.UUID(data['community']) except ValueError as e: raise InvalidDepositError( 'Community ID is not a valid UUID.') from e try: schema = CommunitySchema.get_community_schema(community_id) except CommunitySchemaDoesNotExistError as e: raise InvalidDepositError( 'No schema for community {}.'.format(community_id)) from e if version_of: data['$schema'] = Deposit._build_deposit_schema(prev_version) else: from b2share.modules.schemas.serializers import \ community_schema_draft_json_schema_link data['$schema'] = community_schema_draft_json_schema_link( schema, _external=True ) # prepopulate required boolean fields with false if not version_of: from b2share.modules.schemas.api import BlockSchema import json community_schema = json.loads(schema.community_schema) if 'required' in community_schema: bs_id = json.loads(schema.community_schema)['required'][0] bs = BlockSchema.get_block_schema(bs_id) bs_version = bs.versions[len(bs.versions) - 1] schema_dict = json.loads(bs_version.json_schema) required = [] if 'required' in schema_dict: required = schema_dict['required'] properties = {} if 'properties' in schema_dict: properties = schema_dict['properties'] try: community_metadata = data['community_specific'][bs_id] except KeyError: community_metadata = {} for key in required: if properties.get(key).get('type') == 'boolean' and not key in community_metadata: community_metadata[key] = False data['community_specific'] = {bs_id: community_metadata} # create file bucket if prev_version and prev_version.files: # Clone the bucket from the previous version. This doesn't # duplicate files. bucket = prev_version.files.bucket.snapshot(lock=False) bucket.locked = False else: bucket = Bucket.create(storage_class=current_app.config[ 'DEPOSIT_DEFAULT_STORAGE_CLASS' ]) if 'external_pids' in data: create_b2safe_file(data['external_pids'], bucket) del data['external_pids'] deposit = super(Deposit, cls).create(data, id_=id_) db.session.add(bucket) db.session.add(RecordsBuckets( record_id=deposit.id, bucket_id=bucket.id )) return deposit
def required_draft4(validator, required, instance, schema): if not validator.is_type(instance, "object"): return for property in required: if property not in instance: yield ValidationError("%r is a required property" % property)
def const(validator, const, instance, schema): if instance != const: yield ValidationError("%r was expected" % (const, ))
def maxProperties_draft4(validator, mP, instance, schema): if not validator.is_type(instance, "object"): return if validator.is_type(instance, "object") and len(instance) > mP: yield ValidationError("%r has too many properties" % (instance, ))
def minItems(validator, mI, instance, schema): if validator.is_type(instance, "array") and len(instance) < mI: yield ValidationError("%r is too short" % (instance, ))
def oneOf_draft4(validator, oneOf, instance, schema): """ oneOf_draft4 validator from https://github.com/Julian/jsonschema/blob/d16713a4296663f3d62c50b9f9a2893cb380b7af/jsonschema/_validators.py#L337 Modified to: - sort the instance JSON, so we get a reproducible output that we can can test more easily - Yield all the individual errors for linked or embedded releases within a record. - Return more information on the ValidationError object, to allow us to replace the translation with a message in cove-ocds """ subschemas = enumerate(oneOf) all_errors = [] for index, subschema in subschemas: errs = list(validator.descend(instance, subschema, schema_path=index)) if not errs: first_valid = subschema break # We check the title, because we don't have access to the field name, # as it lives in the parent. # It will not match the releases array in a release package, because # there is no oneOf. if (schema.get("title") == "Releases" or schema.get("description") == "An array of linking identifiers or releases"): # If instance is not a list, or is a list of zero length, then # validating against either subschema will work. # Assume instance is an array of Linked releases, if there are no # "id"s in any of the releases. if type(instance) is not list or all("id" not in release for release in instance): if "properties" in subschema.get( "items", {}) and "id" not in subschema["items"]["properties"]: for err in errs: err.assumption = "linked_releases" yield err return # Assume instance is an array of Embedded releases, if there is an # "id" in each of the releases elif all("id" in release for release in instance): if "id" in subschema.get("items", {}).get( "properties", {}) or subschema.get("items", {}).get( "$ref", "").endswith("release-schema.json"): for err in errs: err.assumption = "embedded_releases" yield err return else: err = ValidationError( "This array should contain either entirely embedded releases or " "linked releases. Embedded releases contain an 'id' whereas linked " "releases do not. Your releases contain a mixture.") err.error_id = "releases_both_embedded_and_linked" yield err return all_errors.extend(errs) else: err = ValidationError( f"{json.dumps(instance, sort_keys=True, default=decimal_default)} " "is not valid under any of the given schemas", context=all_errors, ) err.error_id = "oneOf_any" yield err more_valid = [s for i, s in subschemas if validator.is_valid(instance, s)] if more_valid: more_valid.append(first_valid) reprs = ", ".join(repr(schema) for schema in more_valid) err = ValidationError(f"{instance!r} is valid under each of {reprs}") err.error_id = "oneOf_each" err.reprs = reprs yield err
def maxItems(validator, mI, instance, schema): if validator.is_type(instance, "array") and len(instance) > mI: yield ValidationError("%r is too long" % (instance, ))
def nullable(validator, is_nullable, instance, schema): if instance is None and not is_nullable: yield ValidationError("None for not nullable")
def uniqueItems(validator, uI, instance, schema): if (uI and validator.is_type(instance, "array") and not _utils.uniq(instance)): yield ValidationError("%r has non-unique elements" % instance)
def readOnly(validator, ro, instance, schema): if not validator.write or not ro: return yield ValidationError("Tried to write read-only property with %s" % (instance))
def pattern(validator, patrn, instance, schema): if (validator.is_type(instance, "string") and not re.search(patrn, instance)): yield ValidationError("%r does not match %r" % (instance, patrn))