Skip to content

Commit

Permalink
Add Regex and OR support for transform (#687)
Browse files Browse the repository at this point in the history
* Add Regex and OR support for transform

* Update regex, unit test and
  • Loading branch information
anshikg authored Feb 20, 2021
1 parent 1379997 commit 95b5666
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 25 deletions.
73 changes: 55 additions & 18 deletions src/rpdk/core/contract/resource_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,28 +215,65 @@ def get_metadata(self):
and properties[prop]["insertionOrder"] == "false"
}

def transform_model(self, input_model, output_model):
def transform_model(self, input_model, output_model): # pylint: disable=R0914
if self.property_transform_keys:
self.check_npm()
for prop in self.property_transform_keys:
document_input, _path_input, _parent_input = traverse(
input_model, list(prop)[1:]
)
document_output, _path_output, _parent_output = traverse(
output_model, list(prop)[1:]
)
if document_input != document_output:
transformed_property = self.transform(prop, input_model)
self.update_transformed_property(
prop, transformed_property, input_model
for prop in self.property_transform_keys: # pylint: disable=R1702
try:
document_input, _path_input, _parent_input = traverse(
input_model, list(prop)[1:]
)
document_output, _path_output, _parent_output = traverse(
output_model, list(prop)[1:]
)
if not self.compare(document_input, document_output):
path = "/" + "/".join(prop)
property_transform_value = self.property_transform[path].replace(
'"', '\\"'
)
if "$OR" in property_transform_value:
transform_functions = property_transform_value.split("$OR")
for transform_function in transform_functions:
transformed_property = self.transform(
transform_function, input_model
)
value = self.convert_type(
document_input, transformed_property
)
if self.compare(value, document_output):
self.update_transformed_property(
prop, value, input_model
)
else:
transformed_property = self.transform(
property_transform_value, input_model
)
value = self.convert_type(document_input, transformed_property)
self.update_transformed_property(prop, value, input_model)

except KeyError:
pass

def transform(self, property_path, input_model):
@staticmethod
def convert_type(document_input, transformed_property):
if isinstance(document_input, bool):
return bool(transformed_property)
if isinstance(document_input, int):
return int(transformed_property)
if isinstance(document_input, float):
return float(transformed_property)
return transformed_property

path = "/" + "/".join(property_path)
property_transform_value = json.dumps(self.property_transform[path])
# self.property_transform[path].replace('"', '\\"')
@staticmethod
def compare(document_input, document_output):
try:
if isinstance(document_input, str):
return re.match(f"^{document_input}$", document_output)
return document_input == document_output
except re.error:
return document_input == document_output

def transform(self, property_transform_value, input_model):
LOG.warning("This is the transform %s", property_transform_value)
content = self.transformation_template.render(
input_model=input_model, jsonata_expression=property_transform_value
Expand Down Expand Up @@ -458,7 +495,7 @@ def make_request(
creds,
token,
callback_context=None,
**kwargs
**kwargs,
):
return {
"requestData": {
Expand Down Expand Up @@ -534,7 +571,7 @@ def _make_payload(self, action, current_model, previous_model=None, **kwargs):
self._session, LOWER_CAMEL_CRED_KEYS, self._role_arn
),
self.generate_token(),
**kwargs
**kwargs,
)

def _call(self, payload):
Expand Down
2 changes: 1 addition & 1 deletion src/rpdk/core/contract/templates/transformation.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var jsonata = require('jsonata')
var model = {{input_model}}
var expression = jsonata({{jsonata_expression}});
var expression = jsonata("{{jsonata_expression}}");
var result = expression.evaluate(model);
console.log(result);
53 changes: 47 additions & 6 deletions tests/contract/test_resource_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,21 @@
"b": {"type": "number"},
"c": {"type": "number"},
"d": {"type": "number"},
"e": {"type": "string"},
},
"readOnlyProperties": ["/properties/b"],
"createOnlyProperties": ["/properties/c"],
"primaryIdentifier": ["/properties/c"],
"primaryIdentifier": ["/properties/b"],
"writeOnlyProperties": ["/properties/d"],
"propertyTransform": {"/properties/a": '$join([a, "Test"])'},
"propertyTransform": {
"/properties/a": '$join([a, "Test"])',
"/properties/c": "$power(2, 2)",
"/properties/e": '$join([e, "Test"]) $OR $join([e, "Value"])',
},
}

TRANSFORM_OUTPUT = {"a": "ValueATest", "c": 1}
TRANSFORM_OUTPUT = {"a": "ValueATest", "c": 4}
TRANSFORM_OUTPUT_WITHOUT_C = {"a": "ValueATest", "c": 1}
INPUT = {"a": "ValueA", "c": 1}
INVALID_OUTPUT = {"a": "ValueB", "c": 1}

Expand Down Expand Up @@ -834,7 +840,11 @@ def test_call_async(resource_client, action):
)

mock_client.invoke.side_effect = [
{"Payload": StringIO('{"status": "IN_PROGRESS", "resourceModel": {"c": 3} }')},
{
"Payload": StringIO(
'{"status": "IN_PROGRESS", "resourceModel": {"c": 3, "b": 1} }'
)
},
{"Payload": StringIO('{"status": "SUCCESS"}')},
]

Expand Down Expand Up @@ -1238,7 +1248,27 @@ def test_transform_model_equal_output(resource_client):
output_model = TRANSFORM_OUTPUT.copy()

resource_client.transform_model(input_model, output_model)
assert input_model == TRANSFORM_OUTPUT
assert input_model == output_model


@pytest.mark.parametrize(
"output",
[{"e": "newValue", "a": "ValueA", "c": 1}, {"e": "newTest", "a": "ValueA", "c": 1}],
)
def test_transform_model_equal_output_OR(resource_client, output):
input_model = {"e": "new", "a": "ValueA", "c": 1}

resource_client.transform_model(input_model, output)
assert input_model == output


def test_transform_model_not_equal_output_OR(resource_client):
input_model = {"e": "new", "a": "ValueA", "c": 1}
output_model = {"e": "not-newValue", "a": "ValueA", "c": 4}

resource_client.transform_model(input_model, output_model)
assert input_model != output_model
assert input_model == {"a": "ValueA", "c": 4, "e": "new"}


def test_transform_model_unequal_models(resource_client):
Expand All @@ -1247,7 +1277,7 @@ def test_transform_model_unequal_models(resource_client):

resource_client.transform_model(input_model, output_model)
assert input_model != output_model
assert input_model == TRANSFORM_OUTPUT
assert input_model == TRANSFORM_OUTPUT_WITHOUT_C


def test_non_transform_model_not_equal(resource_client_inputs_schema):
Expand All @@ -1256,3 +1286,14 @@ def test_non_transform_model_not_equal(resource_client_inputs_schema):

resource_client_inputs_schema.transform_model(input_model, output_model)
assert input_model != output_model


def test_compare_raise_exception(resource_client):
assert not resource_client.compare("he(lo", "hello")


@pytest.mark.parametrize("value", [1, 2.3, True, "test"])
def test_convert_type(resource_client, value):
converted_value = resource_client.convert_type(value, str(value))
assert isinstance(converted_value, type(value))
assert converted_value == value

0 comments on commit 95b5666

Please sign in to comment.