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
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
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_
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_
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
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
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
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