def gather( *, schema: types.Schema, logical_name: str, schemas: types.Schemas ) -> types.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: schema: 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. """ intermediary_obj_artifacts = _handle_schema( logical_name=logical_name, schema=schema, schemas=schemas ) # Check if uselist is defined and backref is not if ( intermediary_obj_artifacts.uselist is not None and intermediary_obj_artifacts.backref is None ): raise exceptions.MalformedRelationshipError( "Relationships with x-uselist defined must also define x-backref." ) # Construct back reference back_reference = None if intermediary_obj_artifacts.backref is not None: back_reference = types.BackReferenceArtifacts( property_name=intermediary_obj_artifacts.backref, uselist=intermediary_obj_artifacts.uselist, ) return types.ObjectArtifacts( spec=intermediary_obj_artifacts.ref_schema, fk_column=intermediary_obj_artifacts.fk_column_name, relationship=types.RelationshipArtifacts( model_name=intermediary_obj_artifacts.ref_model_name, back_reference=back_reference, secondary=intermediary_obj_artifacts.secondary, kwargs=intermediary_obj_artifacts.kwargs, ), nullable=intermediary_obj_artifacts.nullable, description=intermediary_obj_artifacts.description, )
def test_secondary(mocked_facades_models): """ GIVEN schema with array referencing another schema and secondary set and schemas WHEN construct is called THEN table is set on models. """ ref_schema = { "type": "object", "x-tablename": "ref_schema", "properties": { "id": { "type": "integer", "x-primary-key": True } }, } secondary = "association" artifacts = types.ObjectArtifacts( spec=copy.deepcopy(ref_schema), fk_column="id", relationship=types.RelationshipArtifacts(model_name="RefSchema", secondary=secondary), ) tablename = "schema" model_schema = { "type": "object", "x-tablename": tablename, "properties": { "id": { "type": "integer", "x-primary-key": True } }, } schemas = {"RefSchema": copy.deepcopy(ref_schema)} array_ref._link.construct(artifacts=artifacts, model_schema=model_schema, schemas=schemas) assert mocked_facades_models.set_association.call_count == 1 if sys.version_info[1] == 8: name = mocked_facades_models.set_association.call_args.kwargs["name"] table = mocked_facades_models.set_association.call_args.kwargs["table"] else: _, kwargs = mocked_facades_models.set_association.call_args name = kwargs["name"] table = kwargs["table"] assert name == secondary assert table.name == secondary assert schemas == {"RefSchema": ref_schema}
def test_schemas(): """ GIVEN schema with array referencing another schema and schemas WHEN construct is called THEN foreign key is added to the referenced schema. """ ref_schema = { "type": "object", "x-tablename": "ref_schema", "properties": {} } artifacts = types.ObjectArtifacts( spec=copy.deepcopy(ref_schema), fk_column="id", relationship=types.RelationshipArtifacts(model_name="RefSchema"), ) tablename = "schema" model_schema = { "type": "object", "x-tablename": tablename, "properties": { "id": { "type": "integer" } }, } schemas = {"RefSchema": copy.deepcopy(ref_schema)} array_ref._link.construct(artifacts=artifacts, model_schema=model_schema, schemas=schemas) assert schemas == { "RefSchema": { "allOf": [ ref_schema, { "type": "object", "properties": { f"{tablename}_id": { "type": "integer", "x-foreign-key": f"{tablename}.id", "x-dict-ignore": True, } }, }, ] } }
def test_invalid(): """ GIVEN artifacts with None BackReferenceArtifacts WHEN _calculate_schema is called with the artifacts THEN MissingArgumentError is raised. """ artifacts = types.ObjectArtifacts( spec={}, fk_column="fk_column", relationship=types.RelationshipArtifacts( model_name="RefModel", back_reference=None, secondary=None ), ) with pytest.raises(exceptions.MissingArgumentError): backref._calculate_schema( artifacts=artifacts, ref_from_array=False, model_name="Model" )
def test_record(): """ GIVEN artifacts, schemas, whether the reference is from an array and the model name WHEN record is called with the arguments THEN the back reference schema is recorded on the references model. """ artifacts = types.ObjectArtifacts( spec={}, logical_name="logical name 1", fk_column="fk_column", relationship=types.RelationshipArtifacts( model_name="RefModel", back_reference=types.BackReferenceArtifacts(property_name="model"), ), ) schemas = {"RefModel": {"type": "object", "properties": {}}} backref.record(artifacts=artifacts, ref_from_array=False, model_name="Model", schemas=schemas) assert schemas == { "RefModel": { "allOf": [ { "type": "object", "properties": {} }, { "type": "object", "x-backrefs": { "model": { "type": "array", "items": { "type": "object", "x-de-$ref": "Model" }, } }, }, ] } }
def test_record_backref_none(): """ GIVEN artifacts with None backref WHEN record is called with the artifacts THEN MissingArgumentError is raised. """ artifacts = types.ObjectArtifacts( spec={}, fk_column="fk_column", relationship=types.RelationshipArtifacts( model_name="RefModel", back_reference=None, secondary=None ), ) schemas = {"RefModel": {"type": "object", "properties": {}}} backref.record( artifacts=artifacts, ref_from_array=False, model_name="Model", schemas={} ) assert schemas == {"RefModel": {"type": "object", "properties": {}}}
def test_valid(ref_from_array, uselist, secondary, expected_schema): """ GIVEN whether referenced from array, uselist, secondary, model name WHEN _calculate_schema is called with the parameters THEN the given expected schema is returned. """ artifacts = types.ObjectArtifacts( spec={}, fk_column="fk_column", relationship=types.RelationshipArtifacts( model_name="RefModel", back_reference=types.BackReferenceArtifacts( property_name="model", uselist=uselist ), secondary=secondary, ), ) returned_schema = backref._calculate_schema( artifacts=artifacts, ref_from_array=ref_from_array, model_name="Model" ) assert returned_schema == expected_schema
"""Tests for _calculate_schema.""" # pylint: disable=protected-access import pytest from open_alchemy import types from open_alchemy.column_factory import object_ref @pytest.mark.parametrize( "artifacts, expected_schema", [ ( types.ObjectArtifacts( spec={}, fk_column="fk_column", relationship=types.RelationshipArtifacts("RefSchema"), ), { "type": "object", "x-de-$ref": "RefSchema" }, ), ( types.ObjectArtifacts( spec={}, fk_column="fk_column", relationship=types.RelationshipArtifacts("RefSchema"), nullable=True, ), {
"""Tests for schema.""" # pylint: disable=protected-access import pytest from open_alchemy import types from open_alchemy.column_factory import array_ref @pytest.mark.parametrize( "artifacts, expected_schema", [ pytest.param( types.ObjectArtifacts( spec={}, logical_name="logical name 1", fk_column="fk_column", relationship=types.RelationshipArtifacts("RefSchema"), ), {"type": "array", "items": {"type": "object", "x-de-$ref": "RefSchema"}}, id="plain", ), pytest.param( types.ObjectArtifacts( spec={}, logical_name="logical name 1", fk_column="fk_column", relationship=types.RelationshipArtifacts("RefSchema"), description="description 1", ), { "type": "array",