Exemplo n.º 1
0
def test_resolve_ref_not_defined():
    """
    GIVEN schema that references a schema that doesn't exist
    WHEN resolve_ref is called with the schema
    THEN SchemaNotFoundError is raised.
    """
    schema = {"$ref": "#/components/schemas/RefSchema"}
    schemas = {}

    with pytest.raises(exceptions.SchemaNotFoundError):
        helpers.resolve_ref(name="name 1", schema=schema, schemas=schemas)
Exemplo n.º 2
0
def test_resolve_ref_not_schema():
    """
    GIVEN schema that references something that is not a schema
    WHEN resolve_ref is called with the schema
    THEN SchemaNotFoundError is raised.
    """
    schema = {"$ref": "#/components/not/schema"}
    schemas = {}

    with pytest.raises(exceptions.SchemaNotFoundError):
        helpers.resolve_ref(name="name 1", schema=schema, schemas=schemas)
Exemplo n.º 3
0
def test_resolve_ref_single():
    """
    GIVEN schema that references another schema and schemas
    WHEN resolve_ref is called with the schema and schemas
    THEN the referenced schema and logical name is returned.
    """
    ref_schema = {"type": "boolean"}
    ref_name = "RefSchema"
    schema = {"$ref": f"#/components/schemas/{ref_name}"}
    schemas = {ref_name: copy.deepcopy(ref_schema)}

    (return_name, return_schema) = helpers.resolve_ref(name="name 1",
                                                       schema=schema,
                                                       schemas=schemas)

    assert return_name == ref_name
    assert return_schema == ref_schema
Exemplo n.º 4
0
def test_resolve_ref_not_ref_schema():
    """
    GIVEN schema that does not have $ref and name
    WHEN resolve_ref is called with the schema and name
    THEN the schema and name are returned.
    """
    name = "name 1"
    schema = {"type": "integer"}
    schemas = {}

    (return_name,
     return_schema) = helpers.resolve_ref(name=name,
                                          schema=copy.deepcopy(schema),
                                          schemas=schemas)

    assert return_name == name
    assert return_schema == schema
Exemplo n.º 5
0
def _handle_object(
    *,
    spec: types.Schema,
    schemas: types.Schemas,
    required: typing.Optional[bool] = None,
    logical_name: str,
) -> typing.List[typing.Tuple[str, typing.Union[sqlalchemy.Column,
                                                typing.Type]]]:
    """
    Generate properties for a reference to another object.

    Assume that, when any $ref and allOf are resolved, the schema is an object.

    Args:
        spec: The schema for the column.
        schemas: Used to resolve any $ref.
        required: Whether the object property is required.
        logical_name: The logical name in the specification for the schema.

    Returns:
        The logical name and the SQLAlchemy column for the foreign key and the logical
        name and relationship for the reference to the object.

    """
    # Default backref
    backref = None

    # Checking for $ref and allOf
    ref = spec.get("$ref")
    all_of = spec.get("allOf")

    if ref is not None:
        # Handling $ref
        ref_logical_name, spec = helpers.resolve_ref(name=logical_name,
                                                     schema=spec,
                                                     schemas=schemas)
        backref = helpers.get_ext_prop(source=spec, name="x-backref")
    elif all_of is not None:
        # Checking for $ref and x-backref counts
        ref_count = 0
        backref_count = 0
        for sub_spec in all_of:
            if sub_spec.get("$ref") is not None:
                ref_count += 1
            if sub_spec.get("x-backref") is not None:
                backref_count += 1
        if ref_count != 1:
            raise exceptions.MalformedManyToOneRelationshipError(
                "Many to One relationships defined with allOf must have exactly one "
                "$ref in the allOf list.")
        if backref_count > 1:
            raise exceptions.MalformedManyToOneRelationshipError(
                "Many to One relationships may have at most 1 x-backref defined."
            )

        # Handling allOf
        for sub_spec in all_of:
            backref = helpers.get_ext_prop(source=sub_spec, name="x-backref")
            if sub_spec.get("$ref") is not None:
                ref_logical_name, spec = helpers.resolve_ref(name=logical_name,
                                                             schema=sub_spec,
                                                             schemas=schemas)
    else:
        raise exceptions.MalformedManyToOneRelationshipError(
            "Many to One relationships are defined using either $ref or allOf."
        )

    # Resolving allOf
    spec = helpers.merge_all_of(schema=spec, schemas=schemas)

    # Handling object
    foreign_key_spec = _handle_object_reference(spec=spec, schemas=schemas)
    return_value = _handle_column(logical_name=f"{logical_name}_id",
                                  spec=foreign_key_spec,
                                  required=required)

    # Creating relationship
    return_value.append((logical_name,
                         sqlalchemy.orm.relationship(ref_logical_name,
                                                     backref=backref)))
    return return_value
Exemplo n.º 6
0
def gather_object_artifacts(*, spec: types.Schema, logical_name: str,
                            schemas: types.Schemas) -> ObjectArtifacts:
    """
    Collect artifacts from a specification for constructing an object reference.

    Get the prepared specification, reference logical name, back reference and foreign
    key column name from a raw object specification.

    Raise MalformedRelationshipError if neither $ref nor $allOf is found.
    Raise MalformedRelationshipError if uselist is defined but backref is not.
    Raise MalformedRelationshipError if multiple $ref, x-backref, x-secondary,
    x-foreign-key-column or x-uselist are found.

    Args:
        spec: The schema for the column.
        schemas: Used to resolve any $ref.
        logical_name: The logical name in the specification for the schema.

    Returns:
        The prepared specification, reference logical name, back reference and foreign
        key column.

    """
    # Default backref
    backref = None
    # Default uselist
    uselist = None
    # Default secondary
    secondary = None
    # Initial foreign key column
    fk_column = None

    # Checking for $ref and allOf
    ref = spec.get("$ref")
    all_of = spec.get("allOf")

    if ref is not None:
        # Handle $ref
        ref_logical_name, spec = helpers.resolve_ref(name=logical_name,
                                                     schema=spec,
                                                     schemas=schemas)
    elif all_of is not None:
        # Checking for $ref, and x-backref and x-foreign-key-column counts
        _check_object_all_of(all_of_spec=all_of)

        # Handle allOf
        for sub_spec in all_of:
            backref = helpers.get_ext_prop(source=sub_spec,
                                           name="x-backref",
                                           default=backref)
            uselist = helpers.get_ext_prop(source=sub_spec,
                                           name="x-uselist",
                                           default=uselist)
            secondary = helpers.get_ext_prop(source=sub_spec,
                                             name="x-secondary",
                                             default=secondary)
            fk_column = helpers.get_ext_prop(source=sub_spec,
                                             name="x-foreign-key-column",
                                             default=fk_column)
            if sub_spec.get("$ref") is not None:
                ref_logical_name, spec = helpers.resolve_ref(name=logical_name,
                                                             schema=sub_spec,
                                                             schemas=schemas)
    else:
        raise exceptions.MalformedRelationshipError(
            "Relationships are defined using either $ref or allOf.")

    # Resolving allOf
    spec = helpers.merge_all_of(schema=spec, schemas=schemas)

    # If backref has not been found look in referenced schema
    if backref is None:
        backref = helpers.get_ext_prop(source=spec, name="x-backref")
    # If uselist has not been found look in referenced schema
    if uselist is None:
        uselist = helpers.get_ext_prop(source=spec, name="x-uselist")
    # If secondary has not been found look in referenced schema
    if secondary is None:
        secondary = helpers.get_ext_prop(source=spec, name="x-secondary")
    # If foreign key column has not been found look in referenced schema
    if fk_column is None:
        fk_column = helpers.get_ext_prop(source=spec,
                                         name="x-foreign-key-column")
    # If foreign key column is still None, default to id
    if fk_column is None:
        fk_column = "id"

    # Check if uselist is defined and backref is not
    if uselist is not None and backref is None:
        raise exceptions.MalformedRelationshipError(
            "Relationships with x-uselist defined must also define x-backref.")

    return ObjectArtifacts(spec, ref_logical_name, backref, fk_column, uselist,
                           secondary)