"""Schema for abacus state serialization.""" from abacus_common_logic.marshalling.custom_fields import ma from marshmallow import ValidationError, validate, validates_schema from marshmallow.decorators import post_load from abacus_state.constants.constants import ( ACTION_STATUSES, MAX_BULK_QUERY_LIMIT, MAX_BULK_QUERY_OFFSET, ) class AbacusStateDetailSchema(ma.Schema): """Abacus State GET response fields.""" class Meta: """Metadata.""" title = 'Abacus state details' example = { 'abacus_state_id': 1, 'action_name': 'upload_exchange_rates', 'action_status': 'complete', 'message': None, 'created_at': '2020-02-02 02:20:02+0000', 'created_by': '00000000-0000-0000-0000-00000000000d', 'last_modified': '2020-02-03 02:20:02+0000', 'last_modified_by': '00000000-1111-1111-1111-00000000000d', } abacus_state_id = ma.IntegerId( required=True, metadata={'description': 'The ID of the state action', 'example': 1}, ) parent_table_id = ma.IntegerId( required=True, metadata={'description': 'The ID of the parent table'} ) parent_table_name = ma.NonemptyString( required=True, metadata={'description': 'Parent table name'} ) action_name = ma.NonemptyString( required=True, metadata={'description': "The abacus action's name"} ) action_status = ma.Enum( options=ACTION_STATUSES, required=True, metadata={ 'description': "One of 'init', 'running', 'error', 'complete', 'approved', or 'rejected'" }, ) message = ma.String( allow_none=True, metadata={'description': 'A description or message about the status'}, ) created_by = ma.NonemptyString( required=True, metadata={'description': 'identity id of the user who created the state'}, ) created_at = ma.FormattedDateTime( required=True, metadata={'description': 'Datetime when the state was created'} ) last_modified_by = ma.NonemptyString( required=True, metadata={'description': 'identity id of the user who last modified the state'}, ) last_modified = ma.FormattedDateTime( required=True, metadata={'description': 'Datetime when the state was last modified'}, ) class AbacusStatePostSchema(ma.Schema): """Abacus State POST request fields.""" class Meta: """Metadata.""" title = 'Abacus state POST request body' example = { 'action_name': 'upload_exchange_rates', 'parent_table_id': 1, 'parent_table_name': 'accounting_period', } action_name = ma.NonemptyString( required=True, metadata={'description': 'Name of abacus action'} ) parent_table_name = ma.NonemptyString( required=True, metadata={ 'description': "Name of parent table -- used with parent_table_id. If parent_table_name is 'accounting_period', then parent_table_id should be a valid accounting_period_id." }, ) parent_table_id = ma.IntegerId( required=True, metadata={ 'description': "ID of the entity in the parent table. If parent_table_name is 'accounting_period', then parent_table_id should be a valid accounting_period_id." }, ) class AbacusStatePutSchema(ma.Schema): """Abacus State PUT request fields.""" class Meta: """Metadata.""" title = 'Abacus state PUT request body' example = { 'action_status': 'rejected', 'message': 'A reason sent from a 3rd party API (i.e. Payoneer)', } action_status = ma.Enum( options=ACTION_STATUSES, required=True, metadata={ 'description': "One of 'init', 'running', 'error', 'complete', 'approved', or 'rejected'" }, ) message = ma.String( allow_none=True, metadata={'description': 'A description or message about the status'}, ) @post_load def _post_load(self, data, **kwargs): if data.get('message') == '': data = {**data, 'message': None} return data class AbacusStateBulkPutSchema(AbacusStatePutSchema): """Abacus State Bulk PUT request fields.""" class Meta: """Metadata.""" title = 'Abacus state bulk PUT request body' example = { 'action_status': 'complete', 'message': 'auto-completed', 'parent_table_id': 1, } parent_table_id = ma.IntegerId( required=True, metadata={'description': 'ID of the entity in the parent table.'} ) class AbacusStateIdsPostBodySchema(ma.Schema): """Abacus State IDs POST request body.""" class Meta: """Metadata.""" openapi_schema = { 'type': 'array', 'items': {'type': 'integer', 'description': 'State ids list'}, 'title': "Abacus state ID's POST request body", 'example': [1, 2, 3, 4], } class AbacusStateBulkQuerySchema(ma.Schema): """Abacus State Bulk Query request body fields (filters only).""" class Meta: """Metadata.""" title = 'Abacus state bulk query body parameters' example = { 'parent_table_name': 'accounting_period', 'parent_table_ids': [1, 2, 3], 'action_name': 'deliver_sales_files', } parent_table_name = ma.NonemptyString( required=False, metadata={'description': 'Name of parent table to filter by'}, ) parent_table_ids = ma.List( ma.IntegerId(), required=False, metadata={ 'description': 'List of parent table IDs to filter by (requires parent_table_name)' }, ) action_name = ma.NonemptyString( required=False, metadata={'description': 'Action name to filter by'}, ) @validates_schema def validate_parent_table_ids_requires_name(self, data, **kwargs): """Validate that parent_table_ids requires parent_table_name.""" parent_table_ids = data.get('parent_table_ids') if parent_table_ids and len(parent_table_ids) != len(set(parent_table_ids)): raise ValidationError( 'parent_table_ids must not contain duplicate values', field_name='parent_table_ids', ) class AbacusStateBulkQueryParamsSchema(ma.Schema): """Abacus State Bulk Query pagination query parameters.""" class Meta: """Metadata.""" title = 'Abacus state bulk query pagination parameters' limit = ma.Integer( required=False, load_default=100, validate=validate.Range(min=0, max=MAX_BULK_QUERY_LIMIT), metadata={ 'description': 'Maximum number of results to return (default: 100, max: 300, min: 0)' }, ) offset = ma.Integer( required=False, load_default=0, validate=validate.Range(min=0, max=MAX_BULK_QUERY_OFFSET), metadata={ 'description': f'Number of results to skip (default: 0, max: {MAX_BULK_QUERY_OFFSET})' }, ) class AbacusStateBulkQueryResponseSchema(ma.Schema): """Abacus State Bulk Query response fields.""" class Meta: """Metadata.""" title = 'Abacus state bulk query response' example = { 'items': [ { 'abacus_state_id': 1, 'action_name': 'deliver_sales_files', 'action_status': 'complete', 'parent_table_name': 'accounting_period', 'parent_table_id': 1, } ], 'total': 150, 'limit': 100, 'offset': 0, } items = ma.List( ma.Nested(AbacusStateDetailSchema), required=True, metadata={'description': 'List of abacus states'}, ) total = ma.Integer( required=True, metadata={'description': 'Total number of results matching the filters'}, ) limit = ma.Integer( required=True, metadata={'description': 'Maximum number of results returned'}, ) offset = ma.Integer( required=True, metadata={'description': 'Number of results skipped'}, )