Skip to content

Commit

Permalink
663 added validations
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinasKen committed Jan 30, 2025
1 parent b30c885 commit 477a8b7
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 47 deletions.
67 changes: 33 additions & 34 deletions spinta/backends/postgresql/types/array/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,39 @@
def prepare(context: Context, backend: PostgreSQL, dtype: Array, **kwargs):
prop = dtype.prop

if dtype.model is None:
columns = commands.prepare(context, backend, dtype.items, **kwargs)

assert columns is not None
if not isinstance(columns, list):
columns = [columns]
pkey_type = commands.get_primary_key_type(context, backend)
# TODO: When all list items will have unique id, also add reference to
# parent list id.
# if prop.list:
# parent_list_table_name = get_pg_name(
# get_table_name(prop.list, TableType.LIST)
# )
# columns += [
# # Parent list table id.
# sa.Column('_lid', pkey_type, sa.ForeignKey(
# f'{parent_list_table_name}._id', ondelete='CASCADE',
# )),
# ]
name = get_pg_name(get_table_name(prop, TableType.LIST))
main_table_name = get_pg_name(get_table_name(prop.model))
table = sa.Table(
name, backend.schema,
# TODO: List tables eventually will have _id in order to uniquelly
# identify list item.
# sa.Column('_id', pkey_type, primary_key=True),
sa.Column('_txn', pkey_type, index=True),
sa.Column('_rid', pkey_type, sa.ForeignKey(
f'{main_table_name}._id', ondelete='CASCADE',
)),

*columns,
)
backend.add_table(table, prop, TableType.LIST)
columns = commands.prepare(context, backend, dtype.items, **kwargs)

assert columns is not None
if not isinstance(columns, list):
columns = [columns]
pkey_type = commands.get_primary_key_type(context, backend)
# TODO: When all list items will have unique id, also add reference to
# parent list id.
# if prop.list:
# parent_list_table_name = get_pg_name(
# get_table_name(prop.list, TableType.LIST)
# )
# columns += [
# # Parent list table id.
# sa.Column('_lid', pkey_type, sa.ForeignKey(
# f'{parent_list_table_name}._id', ondelete='CASCADE',
# )),
# ]
name = get_pg_name(get_table_name(prop, TableType.LIST))
main_table_name = get_pg_name(get_table_name(prop.model))
table = sa.Table(
name, backend.schema,
# TODO: List tables eventually will have _id in order to uniquelly
# identify list item.
# sa.Column('_id', pkey_type, primary_key=True),
sa.Column('_txn', pkey_type, index=True),
sa.Column('_rid', pkey_type, sa.ForeignKey(
f'{main_table_name}._id', ondelete='CASCADE',
)),

*columns,
)
backend.add_table(table, prop, TableType.LIST)

if prop.list is None:
# For fast whole resource access we also store whole list in a JSONB.
Expand Down
50 changes: 50 additions & 0 deletions spinta/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,3 +1002,53 @@ class InvalidClientFileFormat(UserError):

class MissingRefModel(UserError):
template = 'Property "{property_name}" of type "{property_type}" in the model "{model_name}" should have a model name in the `ref` column, to which it refers.'


class UnableToMapIntermediateTable(UserError):
template = 'Unable to map intermediate table to `Array` property.'


class InvalidIntermediateTableMappingRefCount(UserError):
template = 'Intermediate table can only be mapped with 2 properties (left and right), but were given {ref_count}.'


class SameModelIntermediateTableMapping(UserError):
template = '''
Intermediate table is referencing same model, need to explicitly give the mapping order.
Set it as intermediate table's model primary key, or by explicitly setting reference properties.
First is left property (source), second is right property (targeted value).
'''


class IntermediateTableMappingInvalidType(UserError):
template = '''
Intermediate table requires `Ref` type columns for it's mapping.
{property_name!r} property is of type {property_type!r}.
'''


class IntermediateTableValueTypeMissmatch(UserError):
template = '''
Array item type needs to match intermediate table's right property type.
Given array type: {array_type!r}, intermediate table's type: {intermediate_type!r}.
'''


class IntermediateTableRefModelMissmatch(UserError):
template = '''
Intermediate table's left property `Ref` model needs to be the same as array's model.
Array's model: {array_model!r}, intermediate table's left property `Ref` model: {left_model!r}.
'''


class IntermediateTableRefPropertyModelMissmatch(UserError):
template = '''
Array's `Ref` property model does not match intermediate table's right `Ref` property model.
Array's `Ref` model: {array_ref_model!r}, intermediate table's right property `Ref` model: {right_model!r}.
'''


class IntermediateTableMissingMappingProperty(UserError):
template = '''
Intermediate table's {side} property cannot be None.
'''
41 changes: 32 additions & 9 deletions spinta/types/array/check.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from spinta import commands
from spinta.components import Context
from spinta.exceptions import IntermediateTableMappingInvalidType, IntermediateTableValueTypeMissmatch, \
IntermediateTableRefModelMissmatch, IntermediateTableRefPropertyModelMissmatch, \
IntermediateTableMissingMappingProperty
from spinta.types.datatype import Array, Ref


Expand All @@ -10,22 +13,42 @@ def check(context: Context, dtype: Array):

if dtype.model is not None:
if dtype.left_prop is None:
raise Exception("IF INTERMEDIATE MODEL GIVEN, LEFT PROP IS MUST")
raise IntermediateTableMissingMappingProperty(dtype, side="left")

if dtype.right_prop is None:
raise Exception("IF INTERMEDIATE MODEL GIVEN, RIGHT PROP IS MUST")
raise IntermediateTableMissingMappingProperty(dtype, side="right")

if not isinstance(dtype.left_prop.dtype, Ref):
raise Exception("INTERMEDIATE MODEL MAPPING PROP NEEDS TO BE REF TYPE")
raise IntermediateTableMappingInvalidType(
dtype,
prop_name=dtype.left_prop.name,
prop_type=dtype.left_prop.dtype.name
)

if not isinstance(dtype.right_prop.dtype, Ref):
raise Exception("INTERMEDIATE MODEL MAPPING PROP NEEDS TO BE REF TYPE")

if dtype.items is not None and not isinstance(dtype.items.dtype, Ref):
raise Exception("INTERMEDIATE MODEL REQUIRES REF TYPE TO BE MAPPED FOR RETURN")
raise IntermediateTableMappingInvalidType(
dtype,
prop_name=dtype.right_prop.name,
prop_type=dtype.right_prop.dtype.name
)

if dtype.items is not None and not isinstance(dtype.items.dtype, type(dtype.right_prop.dtype)):
raise IntermediateTableValueTypeMissmatch(
dtype,
array_type=dtype.items.dtype.name,
intermediate_type=dtype.right_prop.dtype.name
)

if dtype.left_prop.dtype.model != dtype.prop.model:
raise Exception("INTERMEDIATE MODEL LEFT PROP MODEL HAS TO BE SAME AS SOURCE")
raise IntermediateTableRefModelMissmatch(
dtype,
array_model=dtype.prop.model.name,
left_model=dtype.left_prop.dtype.model.name
)

if dtype.items.dtype.model != dtype.right_prop.dtype.model:
raise Exception("INTERMEDIATE MODEL RIGHT PROP DOES NOT MATCH REF MODEL WITH ITEM")
raise IntermediateTableRefPropertyModelMissmatch(
dtype,
array_ref_model=dtype.items.dtype.model.name,
right_model=dtype.right_prop.dtype.model.name
)
9 changes: 5 additions & 4 deletions spinta/types/array/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from spinta import commands
from spinta.components import Context, Model, Property
from spinta.exceptions import ModelReferenceNotFound, ModelReferenceKeyNotFound
from spinta.exceptions import ModelReferenceNotFound, ModelReferenceKeyNotFound, \
InvalidIntermediateTableMappingRefCount, UnableToMapIntermediateTable, SameModelIntermediateTableMapping
from spinta.types.datatype import Array, PartialArray, ArrayBackRef, Ref, DataType
from spinta.types.helpers import set_dtype_backend

Expand Down Expand Up @@ -54,7 +55,7 @@ def _compare_models(source: Model, target: str | Model) -> bool:
def _extract_intermediate_table_properties(source: Array, intermediate_model: Model) -> (Property, Property):
if source.refprops:
if len(source.refprops) != 2:
raise Exception("EXPOSED INTERMEDIATE ARRAY TABLE REQUIRES 2 REF PROPS (LEFT, RIGHT)")
raise InvalidIntermediateTableMappingRefCount(source, ref_count=len(source.refprops))

return source.refprops

Expand All @@ -67,10 +68,10 @@ def _extract_intermediate_table_properties(source: Array, intermediate_model: Mo

ref_properties = [prop for prop in intermediate_model.flatprops.values() if isinstance(prop.dtype, Ref)]
if len(ref_properties) != 2:
raise Exception("COULD NOT MAP INTERMEDIATE ARRAY TABLE")
raise UnableToMapIntermediateTable(source)

if all(_compare_models(source.prop.model, prop.dtype.model) for prop in ref_properties):
raise Exception("SAME MODELS BEING REFERENCED, CANNOT DETERMINE WHICH ONE IS LEFT AND WHICH ONE IS RIGHT")
raise SameModelIntermediateTableMapping(source)
return ref_properties


Expand Down

0 comments on commit 477a8b7

Please sign in to comment.