Delete Twins

When Digital Twins are no longer needed they can be deleted through the IOTICSpace user interface or the IOTICS API.

This page covers:


Introduction to Delete Digital Twins

You may wish to delete one or more Twins if you no longer need them and no other Twins are following their data feeds. You can delete Twins and Twin Models in the same way you have created them, either through the IOTICS user interface or the IOTICS API.

❗️

A Twin can be only updated and/or deleted with the credentials initially used to create the Twin

The IOTICS user interface and the IOTICS API use different credentials to create Twins. This means that, if you have created your Digital Twin through the user interface, you won't be able to delete it via the API, and vice-versa.

Delete Digital Twins

Delete a Twin with the IOTICS user interface

Navigate to the Twins section on your IOTICSpace user interface. Once you have found the Digital Twin to delete, click on the delete icon in the ACTIONS section.

Delete a Twin with the IOTICS API

Click here to see the prerequisites.

from requests import request

response = request(
    method="DELETE",
    url=f"{HOST}/qapi/hosts/{host_id}/twins/{twin_did}",
    headers=headers,
)
response.raise_for_status()

Delete Digital Twin Models

Delete a Twin Model with the IOTICS user interface

Navigate to the Twin Models section on your IOTICSpace user interface. Once you have found the Model to delete, click on the delete icon in the ACTIONS section. Deleting a Model will automatically delete all the associated Twins.

Delete a Twin Model with the IOTICS API

When deleting a Twin Model, make sure you also delete all the Twins belonging to that Model. In fact, a Twin without its Model:

  • by concept is meaningless: every Twin must belong to a specific existing Twin template (e.g. Twin Model);
  • the Twin's property Twin from Model would point to a non-existing Twin DID.
Click to see the entire code on how to Delete a Twin Model along with all its Twins from Model

import json
from datetime import datetime, timedelta, timezone
from typing import List

from iotics.lib.identity.api.high_level_api import get_rest_high_level_identity_api
from requests import request

RESOLVER_URL = "resolver_url"
HOST = "host_url"

USER_KEY_NAME = "user_key_name"
AGENT_KEY_NAME = "agent_key_name"
USER_SEED = bytes.fromhex("user_seed")
AGENT_SEED = bytes.fromhex("agent_seed")


class IoticsRest:
    def __init__(self):
        self._high_level_api = get_rest_high_level_identity_api(
            resolver_url=RESOLVER_URL
        )

        (
            self._user_registered_id,
            self._agent_registered_id,
        ) = self._high_level_api.create_user_and_agent_with_auth_delegation(
            user_seed=USER_SEED,
            user_key_name=USER_KEY_NAME,
            agent_seed=AGENT_SEED,
            agent_key_name=AGENT_KEY_NAME,
            delegation_name="#AuthDeleg",
        )

        token = self._high_level_api.create_agent_auth_token(
            agent_registered_identity=self._agent_registered_id,
            user_did=self._user_registered_id.did,
            duration=600,
        )

        self._headers = {
            "accept": "application/json",
            "Iotics-ClientAppId": "example_code",
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        }

    def _make_api_call(self, method: str, url: str, json: dict = None):
        response = request(method=method, url=url, headers=self._headers, json=json)
        response.raise_for_status()

        return response.json()

    def get_host_id(self):
        host_id = self._make_api_call(method="GET", url=f"{HOST}/qapi/host/id")

        return host_id["hostId"]

    def search_twins(
        self,
        text: str = None,
        location: dict = None,
        properties: List[dict] = None,
        scope: str = "LOCAL",
        response_type: str = "FULL",
    ):
        twins_list = []

        search_headers = self._headers.copy()
        # Search headers require a new header "Iotics-RequestTimeout".
        # The latter is used to stop the request once the timeout is reached
        search_headers.update(
            {
                "Iotics-RequestTimeout": (
                    datetime.now(tz=timezone.utc) + timedelta(seconds=5)
                ).isoformat(),
            }
        )

        payload = {"responseType": response_type, "filter": {}}

        if text:
            payload["filter"]["text"] = text
        if properties:
            payload["filter"]["properties"] = properties
        if location:
            payload["filter"]["location"] = location

        with request(
            method="POST",
            url=f"{HOST}/qapi/searches",
            headers=search_headers,
            stream=True,
            verify=True,
            params={"scope": scope},
            json=payload,
        ) as resp:
            resp.raise_for_status()
            # Iterates over the response data, one Host at a time
            for chunk in resp.iter_lines():
                response = json.loads(chunk)
                twins_found = []
                try:
                    twins_found = response["result"]["payload"]["twins"]
                except KeyError:
                    continue
                finally:
                    if twins_found:
                        # Append the twins found to the list of twins
                        twins_list.extend(twins_found)

        print(f"Found {len(twins_list)} twin(s)")

        return twins_list

    def delete_twin(self, twin_did: str, host_id: str):
        self._make_api_call(
            method="DELETE", url=f"{HOST}/qapi/hosts/{host_id}/twins/{twin_did}"
        )


def main():
    iotics_rest = IoticsRest()
    host_id = iotics_rest.get_host_id()
    twin_model_list = iotics_rest.search_twins(
        properties=[
            {
                "key": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
                "uriValue": {"value": "https://data.iotics.com/app#Model"},
            }
        ]
    )

    twin_model_did = ""

    for twin_model in twin_model_list:
        print(twin_model)
        twin_model_did = twin_model["twinId"]["id"]
        answer = input(
            f"\nDo you want to delete this Twin Model with all its Twins from Model (y/n) ? "
        )
        if answer == "y":
            break

    if twin_model_did:
        try:
            iotics_rest.delete_twin(twin_did=twin_model_did, host_id=host_id)
        except Exception:
            print(f"Can't delete Twin {twin_model_did}")
        else:
            print(f"Twin Model {twin_model_did} deleted")

        print("Searching for Twins from Model...")
        twin_from_model_list = iotics_rest.search_twins(
            properties=[
                {
                    "key": "https://data.iotics.com/app#model",
                    "uriValue": {"value": twin_model_did},
                }
            ]
        )

        for twin in twin_from_model_list:
            twin_did = twin["twinId"]["id"]

            try:
                iotics_rest.delete_twin(twin_did=twin_did, host_id=host_id)
            except Exception:
                print(f"Can't delete Twin {twin_did}")
            else:
                print(f"Twin {twin_did} deleted")


if __name__ == "__main__":
    main()