Beispiel #1
0
def get_kwargs(
    *,
    source: typing.Union[
        types.Schema,
        types.ColumnSchema,
        types.ObjectRefSchema,
        types.ArrayRefSchema,
    ],
    reserved: typing.Optional[typing.Set[str]] = None,
    default: typing.Optional[typing.Any] = None,
    pop: bool = False,
    name: str = types.ExtensionProperties.KWARGS,
) -> types.TOptKwargs:
    """
    Read the value of x-kwargs, validate the schema and return it.

    Raise MalformedExtensionPropertyError when the schema of the extension property is
        malformed.
    Raise MalformedExtensionPropertyError when the keys of the kwargs are not a string.
    Raise MalformedExtensionPropertyError if any keys of x-kwargs are in the reserved
        keys.

    Args:
        source: The object to get the extension property from.
        reserved: The reserved keys that should not be in the kwargs.
        default: The default value.
        pop: Whether to remove the value from the dictionary.
        name: The name of the extension property with the kwargs.

    Returns:
        The value of the property or the default value if it does not exist.

    """
    value = get(source=source, name=name, default=default, pop=pop)
    if value is None:
        return None

    # Check for dictionary to make mypy happy, in reality always passes
    if not isinstance(value, dict):  # pragma: no cover
        raise exceptions.MalformedExtensionPropertyError(
            "The value of x-kwargs must be an object."
        )

    # Check keys are strings
    if any(not isinstance(key, str) for key in value.keys()):
        raise exceptions.MalformedExtensionPropertyError(
            "The keys of x-kwargs must be strings."
        )

    # Check whether any reserved keys are in use
    if reserved is not None:
        if not reserved.isdisjoint(value.keys()):
            raise exceptions.MalformedExtensionPropertyError(
                "Some of the keys in x-kwargs refer to arguments that OpenAlchemy "
                "handles. To make use of the arguments, please use the relevant "
                "extension property. The following keys need to be removed: "
                f"{reserved.intersection(value.keys())}."
            )

    return value
Beispiel #2
0
def get(
    *,
    source: typing.Union[
        types.Schema,
        types.ColumnSchema,
        types.ObjectRefSchema,
        types.ArrayRefSchema,
    ],
    name: str,
    default: typing.Optional[typing.Any] = None,
    pop: bool = False,
) -> typing.Optional[typing.Any]:
    """
    Read the value of an extension property, validate the schema and return it.

    Raise MalformedExtensionPropertyError when the schema of the extension property is
    malformed.

    Args:
        source: The object to get the extension property from.
        name: The name of the property.
        default: The default value.
        pop: Whether to remove the value from the dictionary.

    Returns:
        The value of the property or the default value if it does not exist.

    """
    # Check for presence of name
    keys = [name.replace("x-", prefix) for prefix in types.KeyPrefixes]
    key, found = next(
        filter(
            lambda key_value: key_value[1] is True,
            map(lambda key: (key, key in source), keys),
        ),
        ("", False),
    )
    if not found:
        return default

    # Retrieve value
    value = source.get(key)
    if value is None:
        raise exceptions.MalformedExtensionPropertyError(
            f"The value of the {name} extension property cannot be null."
        )

    schema = _SCHEMAS.get(name)
    try:
        facades.jsonschema.validate(instance=value, schema=schema, resolver=_resolver)
    except facades.jsonschema.ValidationError as exc:
        raise exceptions.MalformedExtensionPropertyError(
            f"The value of the {json.dumps(name)} extension property is not "
            "valid. "
            f"The expected schema is {json.dumps(schema)}. "
            f"The given value is {json.dumps(value)}."
        ) from exc
    if pop:
        del source[key]  # type: ignore
    return value
Beispiel #3
0
def get(
    *,
    source: typing.Union[
        typing.Dict[str, typing.Any],
        types.ColumnSchema,
        types.ObjectRefSchema,
        types.ArrayRefSchema,
        types.ReadOnlySchema,
    ],
    name: str,
    default: typing.Optional[typing.Any] = None,
    pop: bool = False,
) -> typing.Optional[typing.Any]:
    """
    Read the value of an extension property, validate the schema and return it.

    Raise MalformedExtensionPropertyError when the schema of the extension property is
    malformed.

    Args:
        source: The object to get the extension property from.
        name: The name of the property.
        default: The default value.
        pop: Whether to remove the value from the dictionary.

    Returns:
        The value of the property or the default value if it does not exist.

    """
    # Check for presence of name
    if name not in source:
        return default

    # Retrieve value
    value = source.get(name)
    if value is None:
        raise exceptions.MalformedExtensionPropertyError(
            f"The value of the {name} extension property cannot be null."
        )

    schema = _SCHEMAS.get(name)
    try:
        facades.jsonschema.validate(instance=value, schema=schema, resolver=_resolver)
    except facades.jsonschema.ValidationError:
        raise exceptions.MalformedExtensionPropertyError(
            f"The value of the {json.dumps(name)} extension property is not "
            "valid. "
            f"The expected schema is {json.dumps(schema)}. "
            f"The given value is {json.dumps(value)}."
        )
    if pop:
        del source[name]  # type: ignore
    return value
Beispiel #4
0
def get_ext_prop(
    *, source: typing.Dict[str, typing.Any], name: str
) -> typing.Optional[typing.Union[str, bool]]:
    """
    Read the value of an extension property, validate the schema and return it.

    Raise MalformedExtensionPropertyError when the schema of the extension property is
    malformed.

    Args:
        source: The object to get the extension property from.
        name: The name of the property.

    Returns:
        The value of the property.

    """
    value = source.get(name)
    if value is None:
        return None

    schema = SCHEMAS.get(name)
    try:
        jsonschema.validate(instance=value, schema=schema)
    except jsonschema.ValidationError:
        raise exceptions.MalformedExtensionPropertyError(
            f"The value of the {json.dumps(name)} extension property is not "
            "valid. "
            f"The expected schema is {json.dumps(schema)}. "
            f"The given value is {json.dumps(value)}."
        )
    return value
Beispiel #5
0
def mixins(*, schema: types.Schema,
           schemas: types.Schemas) -> typing.Optional[typing.List[str]]:
    """
    Retrieve the x-mixins of the schema.

    Args:
        schema: The schema to get x-mixins from.
        schemas: The schemas for $ref lookup.

    Returns:
        The x-mixins or None.

    """
    key = types.ExtensionProperties.MIXINS
    value = peek_key(schema=schema, schemas=schemas, key=key)
    if value is None:
        return None

    # Check value
    ext_prop_helper.get(source={key: value}, name=key)  # type: ignore

    # Transform string to list
    if isinstance(value, str):
        value = [value]

    # Check that each value is a valid dot-separated identifier
    def valid(mixin_value: str) -> bool:
        """Check whether a mixin value is valid."""
        components = mixin_value.split(".")
        if len(components) < 2:
            return False

        invalid_components = map(
            lambda component: not component.isidentifier(), components)
        return not any(invalid_components)

    values_valid = map(lambda mixin_value: (mixin_value, valid(mixin_value)),
                       value)
    first_invalid_value = next(filter(lambda args: not args[1], values_valid),
                               None)
    if first_invalid_value is not None:
        raise exceptions.MalformedExtensionPropertyError(
            f'mixin values must be a valid import path, "{first_invalid_value[0]}" is '
            "not")

    return value
Beispiel #6
0
def get_ext_prop(
    *,
    source: typing.Dict[str, typing.Any],
    name: str,
    default: typing.Optional[typing.Any] = None,
    pop: bool = False,
) -> typing.Optional[typing.Any]:
    """
    Read the value of an extension property, validate the schema and return it.

    Raise MalformedExtensionPropertyError when the schema of the extension property is
    malformed.

    Args:
        source: The object to get the extension property from.
        name: The name of the property.
        default: The default value.
        pop: Whether to remove the value from the dictionary.

    Returns:
        The value of the property or the default value if it does not exist.

    """
    value = source.get(name)
    if value is None:
        return default

    schema = _SCHEMAS.get(name)
    try:
        jsonschema.validate(instance=value, schema=schema, resolver=_resolver)
    except jsonschema.ValidationError:
        raise exceptions.MalformedExtensionPropertyError(
            f"The value of the {json.dumps(name)} extension property is not "
            "valid. "
            f"The expected schema is {json.dumps(schema)}. "
            f"The given value is {json.dumps(value)}.")
    if pop:
        del source[name]
    return value