예제 #1
0
def model(*, artifacts: types.ColumnSchemaArtifacts) -> str:
    """
    Calculate the Python type of a column.

    Args:
        artifacts: The artifacts from the schema of the column.

    Returns:
        The equivalent Python type.

    """
    # Determine underlying type
    return_type = _TYPE_MAPPING[artifacts.open_api.type](artifacts)
    if artifacts.open_api.type == "array" and not artifacts.extension.json:
        return return_type

    # Determine whether the type is optional
    optional = helpers.calculate_nullable(
        nullable=artifacts.open_api.nullable,
        generated=artifacts.extension.generated is True,
        required=artifacts.open_api.required,
        defaulted=artifacts.open_api.default is not None,
    )
    if optional:
        return f"typing.Optional[{return_type}]"
    return return_type
예제 #2
0
def test_calculate_nullable(required, generated, defaulted, nullable,
                            expected_result):
    """
    GIVEN required, generated, defaulted, nullable and expected result
    WHEN calculate_nullable is called with nullable, defaulted, generated and required
    THEN the expected result is returned.
    """
    result = helpers.calculate_nullable(nullable=nullable,
                                        generated=generated,
                                        required=required,
                                        defaulted=defaulted)

    assert result == expected_result
예제 #3
0
def _model_simple_property(
        *, artifacts: schemas_artifacts.types.SimplePropertyArtifacts) -> str:
    """Calculate the Python type of a simple property."""
    assert artifacts.open_api.type in oa_helpers.type_.SIMPLE_TYPES
    type_ = _SIMPLE_TYPE_MAPPING[artifacts.open_api.type](
        artifacts.open_api.format)
    optional = oa_helpers.calculate_nullable(
        nullable=artifacts.open_api.nullable,
        generated=artifacts.extension.autoincrement is True,
        required=artifacts.required,
        defaulted=artifacts.open_api.default is not None
        or artifacts.extension.server_default is not None,
    )

    if optional:
        return f"typing.Optional[{type_}]"
    return type_
예제 #4
0
def _model_relationship_property(
    *, artifacts: schemas_artifacts.types.TAnyRelationshipPropertyArtifacts
) -> str:
    """Calculate the Python type of a relationship property."""
    type_ = _RELATIONSHIP_TYPE_MAPPING[artifacts.sub_type](artifacts.parent)

    if (artifacts.sub_type == types.RelationshipType.ONE_TO_MANY
            or artifacts.sub_type == types.RelationshipType.MANY_TO_MANY):
        return type_

    optional = oa_helpers.calculate_nullable(
        nullable=artifacts.nullable,
        generated=False,
        required=artifacts.required,
        defaulted=False,
    )

    if optional:
        return f"typing.Optional[{type_}]"
    return type_
예제 #5
0
def model(*, artifacts: types.ColumnSchemaArtifacts) -> str:
    """
    Calculate the Python type of a column.

    Args:
        artifacts: The artifacts from the schema of the column.

    Returns:
        The equivalent Python type.

    """
    # Determine underlying type
    return_type = "str"
    if artifacts.type == "integer":
        return_type = "int"
    if artifacts.type == "number":
        return_type = "float"
    if artifacts.type == "boolean":
        return_type = "bool"
    if artifacts.type == "object":
        return_type = f'"T{artifacts.de_ref}"'
    if artifacts.type == "array":
        return f'typing.Sequence["T{artifacts.de_ref}"]'
    if artifacts.format == "binary":
        return_type = "bytes"
    if artifacts.format == "date":
        return_type = "datetime.date"
    if artifacts.format == "date-time":
        return_type = "datetime.datetime"

    # Determine whether the type is optional
    optional = helpers.calculate_nullable(
        nullable=artifacts.nullable,
        generated=artifacts.generated is True,
        required=artifacts.required,
        defaulted=artifacts.default is not None,
    )
    if optional:
        return f"typing.Optional[{return_type}]"
    return return_type
예제 #6
0
def gather_artifacts(
    *,
    model_schema: types.Schema,
    logical_name: str,
    schemas: types.Schemas,
    fk_column: str,
    required: typing.Optional[bool] = None,
    nullable: typing.Optional[bool] = None,
) -> typing.Tuple[str, types.ColumnArtifacts]:
    """
    Gather artifacts for a foreign key to implement an object reference.

    Assume any object schema level allOf and $ref have already been resolved.

    Raise MalformedSchemaError if x-tablename or properties are missing. Also raise if
    the foreign key column is not found in the model schema or it does not have a type.

    Args:
        model_schema: The schema of the referenced model.
        logical_name: The logical name of the property with the object reference.
        schemas: All model schemas used to resolve any $ref.
        fk_column: The name of the foreign key column.
        required: Whether the foreign key is constructed for a property that is
            required.
        nullable: Whether the foreign key is constructed for a property that is
            nullable.

    Returns:
        The logical name of the foreign key and the artifacts required to construct it.

    """
    tablename = helpers.ext_prop.get(source=model_schema, name="x-tablename")
    if not tablename:
        raise exceptions.MalformedSchemaError(
            "Referenced object is missing x-tablename property.")
    properties = model_schema.get("properties")
    if properties is None:
        raise exceptions.MalformedSchemaError(
            "Referenced object does not have any properties.")
    fk_schema = properties.get(fk_column)
    if fk_schema is None:
        raise exceptions.MalformedSchemaError(
            f"Referenced object does not have {fk_column} property.")

    # Gather artifacts
    try:
        fk_type = helpers.peek.type_(schema=fk_schema, schemas=schemas)
    except exceptions.TypeMissingError:
        raise exceptions.MalformedSchemaError(
            f"Referenced object {fk_column} property does not have a type.")
    fk_format = helpers.peek.format_(schema=fk_schema, schemas=schemas)
    fk_max_length = helpers.peek.max_length(schema=fk_schema, schemas=schemas)
    fk_default = helpers.peek.default(schema=fk_schema, schemas=schemas)
    nullable = helpers.calculate_nullable(
        nullable=nullable,
        generated=False,
        required=required,
        defaulted=fk_default is not None,
    )

    # Construct return values
    return_logical_name = f"{logical_name}_{fk_column}"
    artifacts = types.ColumnArtifacts(
        open_api=types.OpenAPiColumnArtifacts(
            type=fk_type,
            format=fk_format,
            nullable=nullable,
            max_length=fk_max_length,
            default=fk_default,
        ),
        extension=types.ExtensionColumnArtifacts(
            foreign_key=f"{tablename}.{fk_column}"),
    )
    return return_logical_name, artifacts
예제 #7
0
def check_schema(
        *,
        schema: types.Schema,
        required: typing.Optional[bool] = None) -> types.ColumnArtifacts:
    """
    Check schema and transform into consistent schema and get the column artifacts.

    Raise TypeMissingError of the type is not in the schema or is not a string.
    Raise MalformedSchemaError if format, maxLength or nullable are not of the correct
    type.
    Raise MalformedExtensionPropertyError if an extension property is of the wrong
    type.

    Args:
        schema: The schema to check.

    Returns:
        The schema against which to check dictionaries and the artifacts required to
        construct the column as a tuple.

    """
    # Retrieve artifacts
    type_ = helpers.peek.type_(schema=schema, schemas={})
    format_ = helpers.peek.format_(schema=schema, schemas={})
    max_length = helpers.peek.max_length(schema=schema, schemas={})
    nullable = helpers.peek.nullable(schema=schema, schemas={})
    description = helpers.peek.description(schema=schema, schemas={})
    default = helpers.peek.default(schema=schema, schemas={})
    primary_key = helpers.ext_prop.get(source=schema, name="x-primary-key")
    autoincrement = helpers.ext_prop.get(source=schema, name="x-autoincrement")
    index = helpers.ext_prop.get(source=schema, name="x-index")
    unique = helpers.ext_prop.get(source=schema, name="x-unique")
    foreign_key = helpers.ext_prop.get(source=schema, name="x-foreign-key")
    foreign_key_kwargs = helpers.ext_prop.get_kwargs(
        source=schema, name="x-foreign-key-kwargs")
    if foreign_key_kwargs is not None and foreign_key is None:
        raise exceptions.MalformedSchemaError(
            "The column must be a foreign key column if foreign key kwargs are defined."
        )
    kwargs = helpers.ext_prop.get_kwargs(
        source=schema,
        reserved={
            "nullable",
            "default",
            "primary_key",
            "autoincrement",
            "index",
            "unique",
        },
    )

    # Construct return artifacts
    nullable_artefact = helpers.calculate_nullable(
        nullable=nullable,
        generated=autoincrement is True,
        required=required,
        defaulted=default is not None,
    )
    return_artifacts = types.ColumnArtifacts(
        open_api=types.OpenAPiColumnArtifacts(
            type=type_,
            format=format_,
            max_length=max_length,
            nullable=nullable_artefact,
            description=description,
            default=default,
        ),
        extension=types.ExtensionColumnArtifacts(
            primary_key=primary_key,
            autoincrement=autoincrement,
            index=index,
            unique=unique,
            foreign_key=foreign_key,
            foreign_key_kwargs=foreign_key_kwargs,
            kwargs=kwargs,
        ),
    )

    return return_artifacts
예제 #8
0
def check_schema(
    *, schema: types.Schema, required: typing.Optional[bool] = None
) -> types.ColumnArtifacts:
    """
    Check schema and transform into consistent schema and get the column artifacts.

    Raise TypeMissingError of the type is not in the schema or is not a string.
    Raise MalformedSchemaError if format, maxLength or nullable are not of the correct
    type.
    Raise MalformedExtensionPropertyError if an extension property is of the wrong
    type.

    Args:
        schema: The schema to check.

    Returns:
        The schema against which to check dictionaries and the artifacts required to
        construct the column as a tuple.

    """
    # Retrieve OpenAPI artifacts
    type_ = helpers.peek.type_(schema=schema, schemas={})
    artifacts = types.ColumnArtifacts(open_api=types.OpenAPiColumnArtifacts(type=type_))
    artifacts.open_api.format = helpers.peek.format_(schema=schema, schemas={})
    artifacts.open_api.max_length = helpers.peek.max_length(schema=schema, schemas={})
    nullable = helpers.peek.nullable(schema=schema, schemas={})
    artifacts.open_api.description = helpers.peek.description(schema=schema, schemas={})
    artifacts.open_api.default = helpers.peek.default(schema=schema, schemas={})
    artifacts.open_api.read_only = helpers.peek.read_only(schema=schema, schemas={})
    artifacts.open_api.write_only = helpers.peek.write_only(schema=schema, schemas={})
    # Retrieve extension artifacts
    artifacts.extension.primary_key = helpers.ext_prop.get(
        source=schema, name="x-primary-key"
    )
    artifacts.extension.autoincrement = helpers.ext_prop.get(
        source=schema, name="x-autoincrement"
    )
    artifacts.extension.index = helpers.ext_prop.get(source=schema, name="x-index")
    artifacts.extension.unique = helpers.ext_prop.get(source=schema, name="x-unique")
    artifacts.extension.json = helpers.ext_prop.get(source=schema, name="x-json")
    artifacts.extension.foreign_key = helpers.ext_prop.get(
        source=schema, name="x-foreign-key"
    )
    artifacts.extension.foreign_key_kwargs = helpers.ext_prop.get_kwargs(
        source=schema, name="x-foreign-key-kwargs"
    )
    if (
        artifacts.extension.foreign_key_kwargs is not None
        and artifacts.extension.foreign_key is None
    ):
        raise exceptions.MalformedSchemaError(
            "The column must be a foreign key column if foreign key kwargs are defined."
        )
    artifacts.extension.kwargs = helpers.ext_prop.get_kwargs(
        source=schema,
        reserved={
            "nullable",
            "default",
            "primary_key",
            "autoincrement",
            "index",
            "unique",
        },
    )

    # Update nullable to consider autoincrement, required and default
    artifacts.open_api.nullable = helpers.calculate_nullable(
        nullable=nullable,
        generated=artifacts.extension.autoincrement is True,
        required=required,
        defaulted=artifacts.open_api.default is not None,
    )

    return artifacts