Skip to content

Commit

Permalink
ADT refactor --replace to --if-none-match (#315)
Browse files Browse the repository at this point in the history
* Use if_none_match instead of replace
* pylint error fix
* update help messaages
* update help and params, add etags to it
  • Loading branch information
vilit1 authored Feb 25, 2021
1 parent 38eb40c commit 65028a5
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 24 deletions.
28 changes: 28 additions & 0 deletions azext_iot/digitaltwins/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ def load_digitaltwins_help():
az dt twin create -n {instance_or_hostname} --dtmi "dtmi:com:example:Room;1"
--twin-id {twin_id}
- name: Create a digital twin from an existing (prior-created) model with if-none-match tag.
text: >
az dt twin create -n {instance_or_hostname} --dtmi "dtmi:com:example:Room;1"
--twin-id {twin_id} --if-none-match
- name: Create a digital twin from an existing (prior-created) model. Instantiate with property values.
text: >
az dt twin create -n {instance_or_hostname} --dtmi "dtmi:com:example:DeviceInformation;1"
Expand Down Expand Up @@ -478,6 +483,11 @@ def load_digitaltwins_help():
az dt twin update -n {instance_or_hostname} --twin-id {twin_id}
--json-patch '{"op":"replace", "path":"/Temperature", "value": 20.5}'
- name: Update a digital twin via JSON patch specification and using etag.
text: >
az dt twin update -n {instance_or_hostname} --twin-id {twin_id} --etag {etag}
--json-patch '{"op":"replace", "path":"/Temperature", "value": 20.5}'
- name: Update a digital twin via JSON patch specification.
text: >
az dt twin update -n {instance_or_hostname} --twin-id {twin_id}
Expand Down Expand Up @@ -524,6 +534,10 @@ def load_digitaltwins_help():
- name: Remove a digital twin by Id.
text: >
az dt twin delete -n {instance_or_hostname} --twin-id {twin_id}
- name: Remove a digital twin by Id using the etag.
text: >
az dt twin delete -n {instance_or_hostname} --twin-id {twin_id} --etag {etag}
"""

helps["dt twin relationship"] = """
Expand All @@ -542,6 +556,11 @@ def load_digitaltwins_help():
az dt twin relationship create -n {instance_or_hostname} --relationship-id {relationship_id} --relationship contains
--twin-id {source_twin_id} --target {target_twin_id}
- name: Create a relationship between two digital twins with if-none-match tag
text: >
az dt twin relationship create -n {instance_or_hostname} --relationship-id {relationship_id} --relationship contains
--twin-id {source_twin_id} --target {target_twin_id} --if-none-match
- name: Create a relationship with initialized properties between two digital twins.
text: >
az dt twin relationship create -n {instance_or_hostname} --relationship-id {relationship_id} --relationship contains
Expand Down Expand Up @@ -593,6 +612,11 @@ def load_digitaltwins_help():
az dt twin relationship update -n {instance_or_hostname} --twin-id {twin_id} --relationship-id {relationship_id}
--relationship contains --json-patch '{"op":"replace", "path":"/Temperature", "value": 20.5}'
- name: Update a digital twin relationship via JSON patch specification and using etag.
text: >
az dt twin relationship update -n {instance_or_hostname} --twin-id {twin_id} --relationship-id {relationship_id}
--relationship contains --json-patch '{"op":"replace", "path":"/Temperature", "value": 20.5}' --etag {etag}
- name: Update a digital twin relationship via JSON patch specification.
text: >
az dt twin relationship update -n {instance_or_hostname} --twin-id {twin_id} --relationship-id {relationship_id}
Expand All @@ -615,6 +639,10 @@ def load_digitaltwins_help():
- name: Delete a digital twin relationship.
text: >
az dt twin relationship delete -n {instance_or_hostname} --twin-id {twin_id} --relationship-id {relationship_id}
- name: Delete a digital twin relationship using the etag.
text: >
az dt twin relationship delete -n {instance_or_hostname} --twin-id {twin_id} --relationship-id {relationship_id} --etag {etag}
"""

helps["dt twin telemetry"] = """
Expand Down
8 changes: 4 additions & 4 deletions azext_iot/digitaltwins/commands_twins.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ def create_twin(
name_or_hostname,
twin_id,
model_id,
replace=False,
if_none_match=False,
properties=None,
resource_group_name=None
):
twin_provider = TwinProvider(cmd=cmd, name=name_or_hostname, rg=resource_group_name)
return twin_provider.create(
twin_id=twin_id, model_id=model_id, replace=replace, properties=properties
twin_id=twin_id, model_id=model_id, if_none_match=if_none_match, properties=properties
)


Expand All @@ -54,7 +54,7 @@ def create_relationship(
target_twin_id,
relationship_id,
relationship,
replace=False,
if_none_match=False,
properties=None,
resource_group_name=None,
):
Expand All @@ -64,7 +64,7 @@ def create_relationship(
target_twin_id=target_twin_id,
relationship_id=relationship_id,
relationship=relationship,
replace=replace,
if_none_match=if_none_match,
properties=properties,
)

Expand Down
9 changes: 5 additions & 4 deletions azext_iot/digitaltwins/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,17 +279,18 @@ def load_digitaltwins_arguments(self, _):
"Operations are limited to add, replace and remove. Provide file path or inline JSON.",
)
context.argument(
"etag", options_list=["--etag", "-e"], help="Entity tag value."
"etag", options_list=["--etag", "-e"], help="Entity tag value. The command will succeed if "
"the etag matches the current etag for the resource."
)
context.argument(
"component_path",
options_list=["--component"],
help="The path to the DTDL component.",
)
context.argument(
"replace",
options_list=["--replace"],
help="Indicates the operation should replace an existing twin if it exists."
"if_none_match",
options_list=["--if-none-match"],
help="Indicates the create operation should fail if an existing twin with the same id exists."
)

with self.argument_context("dt twin create") as context:
Expand Down
8 changes: 4 additions & 4 deletions azext_iot/digitaltwins/providers/twin.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def invoke_query(self, query, show_cost):

return query_result

def create(self, twin_id, model_id, replace=False, properties=None):
def create(self, twin_id, model_id, if_none_match=False, properties=None):
twin_request = {
"$dtId": twin_id,
"$metadata": {"$model": model_id},
Expand All @@ -72,7 +72,7 @@ def create(self, twin_id, model_id, replace=False, properties=None):
logger.info("Twin payload %s", json.dumps(twin_request))

try:
options = TwinOptions(if_none_match=(None if replace else "*"))
options = TwinOptions(if_none_match=("*" if if_none_match else None))
return self.twins_sdk.add(id=twin_id, twin=twin_request, digital_twins_add_options=options)
except ErrorResponseException as e:
raise CLIError(unpack_msrest_error(e))
Expand Down Expand Up @@ -117,7 +117,7 @@ def add_relationship(
target_twin_id,
relationship_id,
relationship,
replace=False,
if_none_match=False,
properties=None,
):
relationship_request = {
Expand All @@ -133,7 +133,7 @@ def add_relationship(

logger.info("Relationship payload %s", json.dumps(relationship_request))
try:
options = TwinOptions(if_none_match=(None if replace else "*"))
options = TwinOptions(if_none_match=("*" if if_none_match else None))
return self.twins_sdk.add_relationship(
id=twin_id,
relationship_id=relationship_id,
Expand Down
2 changes: 1 addition & 1 deletion azext_iot/operations/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -2223,7 +2223,7 @@ def http_wrap(target, device_id, generator):
max_runs=msg_count,
return_handle=True,
)
while True and op.is_alive():
while op.is_alive():
_handle_c2d_msg(target, device_id, receive_settle)
sleep(SIM_RECEIVE_SLEEP_SEC)

Expand Down
110 changes: 107 additions & 3 deletions azext_iot/tests/digitaltwins/test_dt_twin_lifecycle_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def test_dt_twin(self):
room_dtmi = "dtmi:com:example:Room;1"
room_twin_id = "myroom"
thermostat_component_id = "Thermostat"
etag = 'AAAA=='

create_output = self.cmd(
"dt create -n {} -g {} -l {}".format(instance_name, self.rg, self.region)
Expand Down Expand Up @@ -112,7 +113,7 @@ def test_dt_twin(self):
)

replaced_room_twin = self.cmd(
"dt twin create -n {} -g {} --dtmi {} --twin-id {} --replace --properties '{}'".format(
"dt twin create -n {} -g {} --dtmi {} --twin-id {} --properties '{}'".format(
instance_name,
self.rg,
room_dtmi,
Expand All @@ -129,9 +130,9 @@ def test_dt_twin(self):
component_name=thermostat_component_id,
)

# new twin cannot be created with same twin_id if replace not provided
# new twin cannot be created with same twin_id if if-none-match provided
self.cmd(
"dt twin create -n {} -g {} --dtmi {} --twin-id {} --properties '{}'".format(
"dt twin create -n {} -g {} --dtmi {} --twin-id {} --if-none-match --properties '{}'".format(
instance_name,
self.rg,
room_dtmi,
Expand All @@ -141,6 +142,17 @@ def test_dt_twin(self):
expect_failure=True
)

# delete command should fail if etag is different
self.cmd(
"dt twin delete -n {} -g {} --twin-id {} --etag '{}'".format(
instance_name,
self.rg,
room_twin_id,
etag
),
expect_failure=True
)

self.cmd(
"dt twin delete -n {} -g {} --twin-id {}".format(
instance_name,
Expand Down Expand Up @@ -241,6 +253,30 @@ def test_dt_twin(self):
== json.loads(self.kwargs["temperatureJsonPatch"])["value"]
)

self.cmd(
"dt twin update -n {} --twin-id {} --json-patch '{}' --etag '{}'".format(
instance_name,
room_twin_id,
"{temperatureJsonPatch}",
etag
),
expect_failure=True
)

update_twin_result = self.cmd(
"dt twin update -n {} --twin-id {} --json-patch '{}' --etag '{}'".format(
instance_name,
room_twin_id,
"{temperatureJsonPatch}",
update_twin_result["$etag"]
)
).get_output_in_json()

assert (
update_twin_result["Temperature"]
== json.loads(self.kwargs["temperatureJsonPatch"])["value"]
)

twin_query_result = self.cmd(
"dt twin query -n {} -g {} -q 'select * from digitaltwins'".format(
instance_name, self.rg
Expand All @@ -257,6 +293,18 @@ def test_dt_twin(self):
{"op": "replace", "path": "/ownershipUser", "value": "meme"}
)

twin_relationship_create_result = self.cmd(
"dt twin relationship create -n {} -g {} --relationship-id {} --relationship {} --twin-id {} "
"--target-twin-id {}".format(
instance_name,
self.rg,
relationship_id,
relationship,
floor_twin_id,
room_twin_id,
)
).get_output_in_json()

twin_relationship_create_result = self.cmd(
"dt twin relationship create -n {} -g {} --relationship-id {} --relationship {} --twin-id {} "
"--target-twin-id {} --properties '{}'".format(
Expand All @@ -279,6 +327,21 @@ def test_dt_twin(self):
properties=self.kwargs["relationshipJson"],
)

# new twin cannot be created with same twin_id if if-none-match provided
twin_relationship_create_result = self.cmd(
"dt twin relationship create -n {} -g {} --relationship-id {} --relationship {} --twin-id {} "
"--target-twin-id {} --if-none-match --properties '{}'".format(
instance_name,
self.rg,
relationship_id,
relationship,
floor_twin_id,
room_twin_id,
"{relationshipJson}",
),
expect_failure=True
)

twin_relationship_show_result = self.cmd(
"dt twin relationship show -n {} -g {} --twin-id {} --relationship-id {}".format(
instance_name,
Expand Down Expand Up @@ -313,6 +376,37 @@ def test_dt_twin(self):
== json.loads(self.kwargs["relationshipJsonPatch"])["value"]
)

# Fail to update if the etag if different
self.cmd(
"dt twin relationship update -n {} -g {} --relationship-id {} --twin-id {} "
"--json-patch '{}' --etag '{}'".format(
instance_name,
self.rg,
relationship_id,
floor_twin_id,
"{relationshipJsonPatch}",
etag
),
expect_failure=True
)

twin_edge_update_result = self.cmd(
"dt twin relationship update -n {} -g {} --relationship-id {} --twin-id {} "
"--json-patch '{}' --etag '{}'".format(
instance_name,
self.rg,
relationship_id,
floor_twin_id,
"{relationshipJsonPatch}",
twin_edge_update_result["$etag"]
)
).get_output_in_json()

assert (
twin_edge_update_result["ownershipUser"]
== json.loads(self.kwargs["relationshipJsonPatch"])["value"]
)

twin_relationship_list_result = self.cmd(
"dt twin relationship list -n {} --twin-id {}".format(
instance_name,
Expand Down Expand Up @@ -354,6 +448,16 @@ def test_dt_twin(self):
).get_output_in_json()
assert len(twin_relationship_list_result) == 1

self.cmd(
"dt twin relationship delete -n {} --twin-id {} -r {} --etag '{}'".format(
instance_name,
floor_twin_id,
relationship_id,
etag
),
expect_failure=True
)

# No output from API for delete edge
self.cmd(
"dt twin relationship delete -n {} --twin-id {} -r {}".format(
Expand Down
Loading

0 comments on commit 65028a5

Please sign in to comment.