Skip to content

Commit

Permalink
22285 db update for storing xml payloads (bcgov#2843)
Browse files Browse the repository at this point in the history
* 22285 db update to store xml payload

* update xml_payload_id can be nullable

* add index furnishings_furnishing_group_id

* create postgresql XML data type

* move file

* fix typo
  • Loading branch information
kzdev420 authored Jul 24, 2024
1 parent e36f105 commit 11e423d
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 1 deletion.
46 changes: 46 additions & 0 deletions legal-api/migrations/versions/b14f77400786_xml_payloads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""xml_payloads
Revision ID: b14f77400786
Revises: feb206b2ce65
Create Date: 2024-07-17 23:46:20.873233
"""
from alembic import op
import sqlalchemy as sa

from legal_api.models.custom_db_types import PostgreSQLXML

# revision identifiers, used by Alembic.
revision = 'b14f77400786'
down_revision = 'feb206b2ce65'
branch_labels = None
depends_on = None


def upgrade():
op.create_table('xml_payloads',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('payload', PostgreSQLXML(), nullable=False),
sa.Column('created_date', sa.TIMESTAMP(timezone=True), nullable=False),
sa.PrimaryKeyConstraint('id')
)

op.create_table('furnishing_groups',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('xml_payload_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['xml_payload_id'], ['xml_payloads.id']),
sa.PrimaryKeyConstraint('id')
)

op.add_column('furnishings', sa.Column('furnishing_group_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'furnishings', 'furnishing_groups', ['furnishing_group_id'], ['id'])
op.create_index(op.f('ix_furnishings_furnishing_group_id'), 'furnishings', ['furnishing_group_id'], unique=False)
pass


def downgrade():
op.drop_index(op.f('ix_furnishings_furnishing_group_id'), table_name='furnishings')
op.drop_column('furnishings', 'furnishing_group_id')
op.drop_table('furnishing_groups')
op.drop_table('xml_payloads')
pass
4 changes: 4 additions & 0 deletions legal-api/src/legal_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from .document import Document, DocumentType
from .filing import Filing
from .furnishing import Furnishing
from .furnishing_group import FurnishingGroup
from .jurisdiction import Jurisdiction
from .naics_element import NaicsElement
from .naics_structure import NaicsStructure
Expand All @@ -47,6 +48,7 @@
from .share_class import ShareClass
from .share_series import ShareSeries
from .user import User, UserRoles
from .xml_payload import XmlPayload


__all__ = (
Expand All @@ -72,6 +74,7 @@
'DocumentType',
'Filing',
'Furnishing',
'FurnishingGroup',
'Jurisdiction',
'NaicsElement',
'NaicsStructure',
Expand All @@ -89,4 +92,5 @@
'ShareSeries',
'User',
'UserRoles',
'XmlPayload'
)
67 changes: 67 additions & 0 deletions legal-api/src/legal_api/models/custom_db_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""PostgresDB data type utilities."""
from sqlalchemy.types import UserDefinedType


class PostgreSQLXML(UserDefinedType):
"""
A custom SQLAlchemy type to handle PostgreSQL's XML data type.
This class allows SQLAlchemy to store and retrieve XML data in PostgreSQL databases.
It provides methods to bind and process data when inserting into or querying from the database.
Example usage:
class MyModel(Base):
__tablename__ = 'my_model'
id = Column(Integer, primary_key=True)
payload = Column(PostgreSQLXML(), nullable=False)
"""

def get_col_spec(self):
"""
Return the column type specification.
This method returns the string 'XML' to indicate that the column should store XML data.
"""
return 'XML'

def bind_processor(self, dialect):
"""
Return a processor for binding values to the database.
Args:
dialect: The database dialect being used.
Returns:
A function that processes the value to be stored in the database. In this case, the function
returns the value unchanged, as no special processing is needed.
"""
def process(value):
return value
return process

def result_processor(self, dialect, coltype):
"""
Return a processor for retrieving values from the database.
Args:
dialect: The database dialect being used.
coltype: The column type.
Returns:
A function that processes the value retrieved from the database. In this case, the function
returns the value unchanged, as no special processing is needed.
"""
def process(value):
return value
return process
9 changes: 8 additions & 1 deletion legal-api/src/legal_api/models/furnishing.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,12 @@ class FurnishingStatus(BaseEnum):
# parent keys
batch_id = db.Column('batch_id', db.Integer, db.ForeignKey('batches.id'), index=True, nullable=False)
business_id = db.Column('business_id', db.Integer, db.ForeignKey('businesses.id'), index=True, nullable=False)
furnishing_group_id = db.Column('furnishing_group_id', db.Integer, db.ForeignKey('furnishing_groups.id'),
index=True, nullable=False)

# relationships
business = db.relationship('Business', backref=db.backref('furnishings', lazy=True))
furnishing_group = db.relationship('FurnishingGroup', backref=db.backref('furnishings', lazy=True))

def save(self):
"""Save the object to the database immediately."""
Expand All @@ -101,7 +104,8 @@ def find_by(cls, # pylint: disable=too-many-arguments
furnishing_name: str = None,
furnishing_type: str = None,
status: str = None,
grouping_identifier: int = None
grouping_identifier: int = None,
furnishing_group_id: int = None
) -> List[Furnishing]:
"""Return the Furnishing entries matching the filter."""
query = db.session.query(Furnishing)
Expand All @@ -124,6 +128,9 @@ def find_by(cls, # pylint: disable=too-many-arguments
if grouping_identifier:
query = query.filter(Furnishing.grouping_identifier == grouping_identifier)

if furnishing_group_id:
query = query.filter(Furnishing.furnishing_group_id == furnishing_group_id)

return query.all()

@classmethod
Expand Down
59 changes: 59 additions & 0 deletions legal-api/src/legal_api/models/furnishing_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This module holds data for furnishing groups."""
from __future__ import annotations

from typing import List

from .db import db


class FurnishingGroup(db.Model):
"""This class manages the furnishing groups."""

__tablename__ = 'furnishing_groups'

id = db.Column(db.Integer, primary_key=True)

# parent keys
xml_payload_id = db.Column('xml_payload_id', db.Integer, db.ForeignKey('xml_payloads.id'),
index=True, nullable=True)

# relationships
xml_payload = db.relationship('XmlPayload', backref=db.backref('furnishing_groups', lazy=True))

def save(self):
"""Save the object to the database immediately."""
db.session.add(self)
db.session.commit()

@classmethod
def find_by_id(cls, furnishing_group_id: int):
"""Return a Furnishing entry by the id."""
furnishing_group = None
if furnishing_group_id:
furnishing_group = cls.query.filter_by(id=furnishing_group_id).one_or_none()
return furnishing_group

@classmethod
def find_by(cls, # pylint: disable=too-many-arguments
xml_payload_id: int = None
) -> List[FurnishingGroup]:
"""Return the Furnishing entries matching the filter."""
query = db.session.query(FurnishingGroup)

if xml_payload_id:
query = query.filter(FurnishingGroup.xml_payload_id == xml_payload_id)

return query.all()
44 changes: 44 additions & 0 deletions legal-api/src/legal_api/models/xml_payload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This module holds data for xml payloads."""
from __future__ import annotations

from datetime import datetime

from legal_api.models.custom_db_types import PostgreSQLXML

from .db import db


class XmlPayload(db.Model):
"""This class manages the xml_payloads."""

__tablename__ = 'xml_payloads'

id = db.Column(db.Integer, primary_key=True)
payload = db.Column('payload', PostgreSQLXML(), default='', nullable=True)
created_date = db.Column('created_date', db.DateTime(timezone=True), default=datetime.utcnow)

def save(self):
"""Save the object to the database immediately."""
db.session.add(self)
db.session.commit()

@classmethod
def find_by_id(cls, xml_payload_id: int):
"""Return a XmlPayload entry by the id."""
xml_payload = None
if xml_payload_id:
xml_payload = cls.query.filter_by(id=xml_payload_id).one_or_none()
return xml_payload
70 changes: 70 additions & 0 deletions legal-api/tests/unit/models/test_furnishing_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests to assure the Furnishing Group Model.
Test-Suite to ensure that the Furnishing Group Model is working as expected.
"""
from legal_api.models import FurnishingGroup, XmlPayload

def test_valid_furnishing_save(session):
"""Assert that a valid furnishing_group can be saved."""
xml_payload = XmlPayload(
payload = '<root><element>value</element></root>',

)
xml_payload.save()

furnishing_group = FurnishingGroup(
xml_payload_id = xml_payload.id
)

furnishing_group.save()
assert furnishing_group.id


def test_find_furnishing_group_by_id(session):
"""Assert that the method returns correct value."""
xml_payload = XmlPayload(
payload = '<root><element>value</element></root>',

)
xml_payload.save()

furnishing_group = FurnishingGroup(
xml_payload_id = xml_payload.id
)
furnishing_group.save()

res = FurnishingGroup.find_by_id(furnishing_group_id=furnishing_group.id)
assert res


def test_find_furnishing_group_by(session):
"""Assert that the method returns correct values."""
xml_payload = XmlPayload(
payload = '<root><element>value</element></root>',

)
xml_payload.save()

furnishing_group = FurnishingGroup(
xml_payload_id = xml_payload.id
)
furnishing_group.save()

res = FurnishingGroup.find_by(xml_payload_id=xml_payload.id)

assert len(res) == 1
assert res[0].id == furnishing_group.id
42 changes: 42 additions & 0 deletions legal-api/tests/unit/models/test_xml_payload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright © 2024 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests to assure the XmlPayload Model.
Test-Suite to ensure that the XmlPayload Model is working as expected.
"""
from legal_api.models import XmlPayload

def test_valid_xml_payload_save(session):
"""Assert that a valid xml payload can be saved."""
xml_payload = XmlPayload(
payload = '<root><element>value</element></root>',

)

xml_payload.save()
assert xml_payload.id


def test_find_xml_payload_by_id(session):
"""Assert that the method returns correct value."""
xml_payload = XmlPayload(
payload='<root><element>value</element></root>'
)

xml_payload.save()

res = XmlPayload.find_by_id(xml_payload_id=xml_payload.id)

assert res

0 comments on commit 11e423d

Please sign in to comment.