Skip to content

Commit

Permalink
Merge pull request #553 from boozallen/543-records-relations-inbound-…
Browse files Browse the repository at this point in the history
…outbound

#543 Record with relations should as Inbound/Outbound types
  • Loading branch information
carter-cundiff authored Jan 30, 2025
2 parents 926da92 + 483fa7d commit 23e9099
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ class ${record.capitalizedName}Base(ABC):
self._${field.snakeCaseName}: ${field.shortType} = None
#end
#if ($record.hasRelations())
#foreach ($relation in $record.relations)
#foreach ($relation in $record.relations)
${relation.relationPropDeclaration}
#end
#end
#end

#if ($pysparkSupport)

@classmethod
def from_row(cls, row: Row):
"""
Expand Down Expand Up @@ -70,14 +71,15 @@ class ${record.capitalizedName}Base(ABC):
#end
#end
)

#end

@classmethod
def from_dict(cls, dict_obj: Dict[str, Any]):
"""
Creates a record with the given dictionary's data.
"""
#if ($record.hasFields())
#if ($record.hasFields() || $record.hasRelations())
record = cls()
if dict_obj is not None:
#foreach ($field in $record.fields)
Expand All @@ -89,24 +91,19 @@ class ${record.capitalizedName}Base(ABC):
#else
record.${field.snakeCaseName} = dict_obj.get('${field.fieldName}')
#end

#end
#foreach ($relation in $record.relations)
#if ($relation.isOneToManyRelation())
record.${relation.snakeCaseName} = [${relation.name}.from_dict(${relation.snakeCaseName}) for ${relation.snakeCaseName} in dict_obj.get('${relation.name}')]
#else
record.${relation.snakeCaseName} = ${relation.name}.from_dict(dict_obj.get('${relation.name}'))
#end
#end
return record
#else
pass
#end

#if ($record.hasRelations())
#foreach($relation in $record.relations)
@property
${relation.relationGetterSignature}
return self._${relation.snakeCaseName}

@${relation.snakeCaseName}.setter
${relation.relationSetterSignature}
self._${relation.snakeCaseName} = ${relation.snakeCaseName}
#end
#end

def as_dict(self) -> Dict[str, Any]:
"""
Expand All @@ -124,7 +121,13 @@ class ${record.capitalizedName}Base(ABC):
dict_obj['${field.fieldName}'] = self.${field.snakeCaseName}
#end
#end

#foreach ($relation in $record.relations)
#if ($relation.isOneToManyRelation())
dict_obj['${relation.name}'] = [${relation.snakeCaseName}.as_dict() for ${relation.snakeCaseName} in self.${relation.snakeCaseName}]
#else
dict_obj['${relation.name}'] = self.${relation.snakeCaseName}.as_dict()
#end
#end
return dict_obj


Expand Down Expand Up @@ -154,6 +157,20 @@ class ${record.capitalizedName}Base(ABC):
def ${field.snakeCaseName}(self, ${field.snakeCaseName}: ${field.shortType}) -> None:
self._${field.snakeCaseName} = ${field.snakeCaseName}
#end
#if ($record.hasRelations())
#foreach($relation in $record.relations)


@property
${relation.relationGetterSignature}
return self._${relation.snakeCaseName}


@${relation.snakeCaseName}.setter
${relation.relationSetterSignature}
self._${relation.snakeCaseName} = ${relation.snakeCaseName}
#end
#end


def validate(self) -> None:
Expand Down Expand Up @@ -186,7 +203,6 @@ class ${record.capitalizedName}Base(ABC):
#end
#end


def get_value_by_field(self, field: ${record.capitalizedName}Field) -> any:
"""
Returns the value of the given field for this record.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@
"package": "com.boozallen.aiops.mda.pattern.dictionary"
},
"recordType": {
"name": "CustomData",
"name": "PersonWithMToOneRelation",
"package": "com.boozallen.aiops.mda.pattern.record"
}
}
Expand Down Expand Up @@ -282,7 +282,7 @@
"package": "com.boozallen.aiops.mda.pattern.dictionary"
},
"recordType": {
"name": "CustomData",
"name": "PersonWithOneToMRelation",
"package": "com.boozallen.aiops.mda.pattern.record"
}
}
Expand Down Expand Up @@ -315,7 +315,7 @@
"inbound": {
"type": "native",
"recordType": {
"name": "CustomData",
"name": "PersonWithOneToOneRelation",
"package": "com.boozallen.aiops.mda.pattern.record"
}
}
Expand All @@ -326,7 +326,7 @@
"inbound": {
"type": "native",
"recordType": {
"name": "CustomData",
"name": "PersonWithMToOneRelation",
"package": "com.boozallen.aiops.mda.pattern.record"
}
}
Expand All @@ -341,7 +341,7 @@
"package": "com.boozallen.aiops.mda.pattern.dictionary"
},
"recordType": {
"name": "CustomData",
"name": "PersonWithOneToMRelation",
"package": "com.boozallen.aiops.mda.pattern.record"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
"name": "PersonWithMToOneRelation",
"package": "com.boozallen.aiops.mda.pattern.record",
"description": "Person custom record",
"fields": [
{
"name": "customField",
"type": {
"name": "customType",
"package": "com.boozallen.aiops.mda.pattern.dictionary"
}
}
],
"relations": [
{
"package":"com.boozallen.aiops.mda.pattern.record",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,26 @@ Scenario Outline: Accessors are generated for relations
| multiplicity | type |
| 1-1 | Address |
| 1-M | List[Address] |
| M-1 | Address |
| M-1 | Address |

Scenario Outline: Records with relations can be serialized as JSON strings
Given a record "Person" that has a "<multiplicity>" relation to a record "Address"
When the record is serialized
Then the record relations are maintained as JSON string

Examples:
| multiplicity |
| 1-1 |
| 1-M |
| M-1 |

Scenario Outline: Records with relations can be deserialized as Record objects
Given a JSON string that has a "<multiplicity>" record relation encoded
When the JSON string is deserialized
Then the record relations are maintained as a Record object

Examples:
| multiplicity |
| 1-1 |
| 1-M |
| M-1 |
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
import ast
import sys
from pathlib import Path
from aissemble_test_data_delivery_pyspark_model.schema.custom_data_schema import (
CustomDataSchema,
)


@given("python files are generated")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from aissemble_test_data_delivery_pyspark_model.record.person_with_one_to_m_relation import (
PersonWithOneToMRelation,
)
from aissemble_test_data_delivery_pyspark_model.record.address import (
Address,
)


def initialize(context):
Expand All @@ -30,18 +33,81 @@ def initialize(context):
"M-1": PersonWithMToOneRelation,
}

context.relation_json_map = {
"1-1": '{"Address": {"street": "123 Test St", "city": "Testville", "zipcode": 12345, "state": "Test"}}',
"1-M": '{"Address": [{"street": "123 Test St", "city": "Testville", "zipcode": 12345, "state": "Test"}, {"street": "123 Test St", "city": "Testville", "zipcode": 12345, "state": "Test"}]}',
"M-1": '{"customField": "Test Field", "Address": {"street": "123 Test St", "city": "Testville", "zipcode": 12345, "state": "Test"}}',
}

address = Address()
address.street = "123 Test St"
address.city = "Testville"
address.state = "Test"
address.zipcode = 12345
context.address = address


@given('a record "Person" that has a "{multiplicity}" relation to a record "Address"')
def step_impl(context, multiplicity):
initialize(context)
context.multiplicity = multiplicity

match multiplicity:
case "1-1":
context.person_with_one_to_one_relation = PersonWithOneToOneRelation()
context.person_with_one_to_one_relation.address = context.address
case "1-M":
context.person_with_one_to_m_relation = PersonWithOneToMRelation()
context.person_with_one_to_m_relation.address = [
context.address,
context.address,
]
case "M-1":
context.person_with_m_to_one_relation = PersonWithMToOneRelation()
context.person_with_m_to_one_relation.address = context.address
context.person_with_m_to_one_relation.custom_field = "Test Field"


@given('a JSON string that has a "{multiplicity}" record relation encoded')
def step_impl(context, multiplicity):
initialize(context)
context.multiplicity = multiplicity
context.json_string = context.relation_json_map.get(context.multiplicity)


@when('the "Person" class is generated')
def step_impl(context):
context.person = context.relation_class_name_map.get(context.multiplicity)


@when("the record is serialized")
def step_impl(context):
match context.multiplicity:
case "1-1":
context.json_string = context.person_with_one_to_one_relation.as_json()
case "1-M":
context.json_string = context.person_with_one_to_m_relation.as_json()
case "M-1":
context.json_string = context.person_with_m_to_one_relation.as_json()


@when("the JSON string is deserialized")
def step_impl(context):
match context.multiplicity:
case "1-1":
context.person_with_one_to_one_relation = (
PersonWithOneToOneRelation.from_json(context.json_string)
)
case "1-M":
context.person_with_one_to_m_relation = PersonWithOneToMRelation.from_json(
context.json_string
)
case "M-1":
context.person_with_m_to_one_relation = PersonWithMToOneRelation.from_json(
context.json_string
)


@then('"Person" has a method address which returns "{type}"')
def step_impl(context, type):
address = getattr(context.person, "address")
Expand All @@ -53,3 +119,58 @@ def step_impl(context, type):
str(return_type),
)
nt.assert_true(return_type == type)


@then("the record relations are maintained as JSON string")
def step_impl(context):
expected_json_string = context.relation_json_map.get(context.multiplicity)

nt.assert_equal(
expected_json_string,
context.json_string,
msg="Serialized JSON string did not match the expected JSON string",
)


@then("the record relations are maintained as a Record object")
def step_impl(context):
match context.multiplicity:
case "1-1":
assert_address(
context.address, context.person_with_one_to_one_relation.address
)
case "M-1":
assert_address(
context.address, context.person_with_m_to_one_relation.address
)
nt.assert_equal(
"Test Field",
context.person_with_m_to_one_relation.custom_field,
msg="Deserialized JSON string did not have the expected Custom Field",
)
case "1-M":
for deserialized_address in context.person_with_one_to_m_relation.address:
assert_address(context.address, deserialized_address)


def assert_address(expected_address, actual_address):
nt.assert_equal(
expected_address.street,
actual_address.street,
msg="Deserialized JSON string did not have the expected Street",
)
nt.assert_equal(
expected_address.city,
actual_address.city,
msg="Deserialized JSON string did not have the expected City",
)
nt.assert_equal(
expected_address.state,
actual_address.state,
msg="Deserialized JSON string did not have the expected State",
)
nt.assert_equal(
expected_address.zipcode,
actual_address.zipcode,
msg="Deserialized JSON string did not have the expected Zipcode",
)
Loading

0 comments on commit 23e9099

Please sign in to comment.