From 6f66eafbeae5e398c9b9f15710c0c106b91b18b6 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2024 13:26:53 +0200 Subject: [PATCH 1/6] reflex apis --- src/sempy_labs/_reflex.py | 111 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/sempy_labs/_reflex.py diff --git a/src/sempy_labs/_reflex.py b/src/sempy_labs/_reflex.py new file mode 100644 index 00000000..6c9f239c --- /dev/null +++ b/src/sempy_labs/_reflex.py @@ -0,0 +1,111 @@ +import sempy.fabric as fabric +import pandas as pd +import sempy_labs._icons as icons +from typing import Optional +from sempy_labs._helper_functions import ( + resolve_workspace_name_and_id, + lro, + _conv_b64, +) +from sempy.fabric.exceptions import FabricHTTPException + + +def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: + + (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) + + client = fabric.FabricRestClient() + response = client.post(f"/v1/workspaces/{workspace_id}/reflexes") + + if response.status_code != 200: + raise FabricHTTPException(response) + + df = pd.DataFrame(columns=['Activator Name', 'Activator Id', 'Description']) + + for v in response.json().get('value'): + new_data = { + "Activator Name": v.get('displayName'), + "Activator Id": v.get('id'), + "Description": v.get('description'), + } + + df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True) + + return df + + +def delete_activator(activator: str, workspace: Optional[str] = None): + + (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) + + item_id = fabric.resolve_item_id(item_name=activator, type='Reflex', workspace=workspace_id) + fabric.delete_item(item_id=item_id, workspace=workspace_id) + + print(f"{icons.green_dot} The '{activator}' activator within the '{workspace}' workspace has been deleted.") + + +def create_activator(name: str, definition: Optional[str] = None, description: Optional[str] = None, workspace: Optional[str] = None): + + (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) + + payload = { + "displayName": name + } + + if description is not None: + payload['description'] = description + + if definition is not None: + reflex_payload = _conv_b64(definition) + # platform_payload = '' + payload['definition'] = { + "format": "json", + "parts": [ + { + "path": "ReflexEntities.json", + "payload": reflex_payload, + "payloadType": "InlineBase64" + }, + # { + # "path": ".platform", + # "payload": platform_payload, + # "payloadType": "InlineBase64" + # } + ] + } + + client = fabric.FabricRestClient() + response = client.post(f"/v1/workspaces/{workspace_id}/reflexes", json=payload) + + lro(client, response, status_codes=[201, 202]) + + print( + f"{icons.green_dot} The '{name}' activator has been created within the '{workspace}' workspace." + ) + + +def update_activator_definition(activator: str, definition: str, workspace: Optional[str] = None): + + (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) + item_id = fabric.resolve_item_id(item_name=activator, type='Reflex', workspace=workspace_id) + reflex_payload = _conv_b64(definition) + + client = fabric.FabricRestClient() + payload = { + "definition": { + "parts": [ + { + "path": "ReflexEntities.json", + "payload": reflex_payload, + "payloadType": "InlineBase64" + } + ] + } + } + response = client.post(f"/v1/workspaces/{workspace_id}/reflexes/{item_id}/updateDefinition", json=payload) + + lro(client, response, status_codes=[200, 202], return_status_code=True) + + print( + f"{icons.green_dot} The '{activator}' activator has been updated within the '{workspace}' workspace." + ) From 6a17ce936de64da4cd1b10dd10a093b7b4e11792 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2024 13:32:53 +0200 Subject: [PATCH 2/6] added descriptions --- src/sempy_labs/__init__.py | 10 ++++++ src/sempy_labs/_reflex.py | 69 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/sempy_labs/__init__.py b/src/sempy_labs/__init__.py index 90cead12..b51ab60b 100644 --- a/src/sempy_labs/__init__.py +++ b/src/sempy_labs/__init__.py @@ -1,3 +1,9 @@ +from sempy_labs._reflex import ( + list_activators, + create_activator, + delete_activator, + update_activator_definition, +) from sempy_labs._gateways import ( list_gateway_members, list_gateway_role_assigments, @@ -456,4 +462,8 @@ "create_vnet_gateway", "update_vnet_gateway", "update_on_premises_gateway", + "list_activators", + "create_activator", + "delete_activator", + "update_activator_definition", ] diff --git a/src/sempy_labs/_reflex.py b/src/sempy_labs/_reflex.py index 6c9f239c..e5159879 100644 --- a/src/sempy_labs/_reflex.py +++ b/src/sempy_labs/_reflex.py @@ -11,6 +11,23 @@ def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: + """ + Shows the activators (reflexes) within a workspace. + + This is a wrapper function for the following API: `Items - List Reflexes `_. + + Parameters + ---------- + workspace : str, default=None + The Fabric workspace name. + Defaults to None which resolves to the workspace of the attached lakehouse + or if no lakehouse attached, resolves to the workspace of the notebook. + + Returns + ------- + pandas.DataFrame + A pandas dataframe showing the activators (reflexes) within a workspace. + """ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) @@ -35,6 +52,20 @@ def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: def delete_activator(activator: str, workspace: Optional[str] = None): + """ + Deletes an activator (reflex). + + This is a wrapper function for the following API: `Items - Delete Reflex `_. + + Parameters + ---------- + activator : str + The name of the activator/reflex. + workspace : str, default=None + The Fabric workspace name. + Defaults to None which resolves to the workspace of the attached lakehouse + or if no lakehouse attached, resolves to the workspace of the notebook. + """ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) @@ -44,7 +75,25 @@ def delete_activator(activator: str, workspace: Optional[str] = None): print(f"{icons.green_dot} The '{activator}' activator within the '{workspace}' workspace has been deleted.") -def create_activator(name: str, definition: Optional[str] = None, description: Optional[str] = None, workspace: Optional[str] = None): +def create_activator(name: str, definition: Optional[dict] = None, description: Optional[str] = None, workspace: Optional[str] = None): + """ + Creates an activator (reflex). + + This is a wrapper function for the following API: `Items - Create Reflex `_. + + Parameters + ---------- + name : str + The name of the activator/reflex. + definition : dict, default=None + The .json definition of an activator/reflex. + description : str, default=None + The description of the activator/reflex. + workspace : str, default=None + The Fabric workspace name. + Defaults to None which resolves to the workspace of the attached lakehouse + or if no lakehouse attached, resolves to the workspace of the notebook. + """ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) @@ -84,7 +133,23 @@ def create_activator(name: str, definition: Optional[str] = None, description: O ) -def update_activator_definition(activator: str, definition: str, workspace: Optional[str] = None): +def update_activator_definition(activator: str, definition: dict, workspace: Optional[str] = None): + """ + Updates the definition of an activator (reflex). + + This is a wrapper function for the following API: `Items - Update Reflex Definition `_. + + Parameters + ---------- + activator : str + The name of the activator/reflex. + definition : dict, default=None + The .json definition of an activator/reflex. + workspace : str, default=None + The Fabric workspace name. + Defaults to None which resolves to the workspace of the attached lakehouse + or if no lakehouse attached, resolves to the workspace of the notebook. + """ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) item_id = fabric.resolve_item_id(item_name=activator, type='Reflex', workspace=workspace_id) From 51ea7cf6bc000faabdc113e55bc0a592997bfe87 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2024 13:34:59 +0200 Subject: [PATCH 3/6] black --- src/sempy_labs/_reflex.py | 52 +++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/sempy_labs/_reflex.py b/src/sempy_labs/_reflex.py index e5159879..bcb3328f 100644 --- a/src/sempy_labs/_reflex.py +++ b/src/sempy_labs/_reflex.py @@ -37,13 +37,13 @@ def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: if response.status_code != 200: raise FabricHTTPException(response) - df = pd.DataFrame(columns=['Activator Name', 'Activator Id', 'Description']) + df = pd.DataFrame(columns=["Activator Name", "Activator Id", "Description"]) - for v in response.json().get('value'): + for v in response.json().get("value"): new_data = { - "Activator Name": v.get('displayName'), - "Activator Id": v.get('id'), - "Description": v.get('description'), + "Activator Name": v.get("displayName"), + "Activator Id": v.get("id"), + "Description": v.get("description"), } df = pd.concat([df, pd.DataFrame(new_data, index=[0])], ignore_index=True) @@ -69,13 +69,22 @@ def delete_activator(activator: str, workspace: Optional[str] = None): (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) - item_id = fabric.resolve_item_id(item_name=activator, type='Reflex', workspace=workspace_id) + item_id = fabric.resolve_item_id( + item_name=activator, type="Reflex", workspace=workspace_id + ) fabric.delete_item(item_id=item_id, workspace=workspace_id) - print(f"{icons.green_dot} The '{activator}' activator within the '{workspace}' workspace has been deleted.") + print( + f"{icons.green_dot} The '{activator}' activator within the '{workspace}' workspace has been deleted." + ) -def create_activator(name: str, definition: Optional[dict] = None, description: Optional[str] = None, workspace: Optional[str] = None): +def create_activator( + name: str, + definition: Optional[dict] = None, + description: Optional[str] = None, + workspace: Optional[str] = None, +): """ Creates an activator (reflex). @@ -97,30 +106,28 @@ def create_activator(name: str, definition: Optional[dict] = None, description: (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) - payload = { - "displayName": name - } + payload = {"displayName": name} if description is not None: - payload['description'] = description + payload["description"] = description if definition is not None: reflex_payload = _conv_b64(definition) # platform_payload = '' - payload['definition'] = { + payload["definition"] = { "format": "json", "parts": [ { "path": "ReflexEntities.json", "payload": reflex_payload, - "payloadType": "InlineBase64" + "payloadType": "InlineBase64", }, # { # "path": ".platform", # "payload": platform_payload, # "payloadType": "InlineBase64" # } - ] + ], } client = fabric.FabricRestClient() @@ -133,7 +140,9 @@ def create_activator(name: str, definition: Optional[dict] = None, description: ) -def update_activator_definition(activator: str, definition: dict, workspace: Optional[str] = None): +def update_activator_definition( + activator: str, definition: dict, workspace: Optional[str] = None +): """ Updates the definition of an activator (reflex). @@ -152,7 +161,9 @@ def update_activator_definition(activator: str, definition: dict, workspace: Opt """ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) - item_id = fabric.resolve_item_id(item_name=activator, type='Reflex', workspace=workspace_id) + item_id = fabric.resolve_item_id( + item_name=activator, type="Reflex", workspace=workspace_id + ) reflex_payload = _conv_b64(definition) client = fabric.FabricRestClient() @@ -162,12 +173,15 @@ def update_activator_definition(activator: str, definition: dict, workspace: Opt { "path": "ReflexEntities.json", "payload": reflex_payload, - "payloadType": "InlineBase64" + "payloadType": "InlineBase64", } ] } } - response = client.post(f"/v1/workspaces/{workspace_id}/reflexes/{item_id}/updateDefinition", json=payload) + response = client.post( + f"/v1/workspaces/{workspace_id}/reflexes/{item_id}/updateDefinition", + json=payload, + ) lro(client, response, status_codes=[200, 202], return_status_code=True) From a6c96cd54c017ce8e2e40828dfb13e507769b627 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2024 13:46:04 +0200 Subject: [PATCH 4/6] added get_activator_definition --- src/sempy_labs/__init__.py | 2 ++ src/sempy_labs/_reflex.py | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/sempy_labs/__init__.py b/src/sempy_labs/__init__.py index b51ab60b..520ea07b 100644 --- a/src/sempy_labs/__init__.py +++ b/src/sempy_labs/__init__.py @@ -3,6 +3,7 @@ create_activator, delete_activator, update_activator_definition, + get_activator_definition, ) from sempy_labs._gateways import ( list_gateway_members, @@ -466,4 +467,5 @@ "create_activator", "delete_activator", "update_activator_definition", + "get_activator_definition", ] diff --git a/src/sempy_labs/_reflex.py b/src/sempy_labs/_reflex.py index bcb3328f..1917811e 100644 --- a/src/sempy_labs/_reflex.py +++ b/src/sempy_labs/_reflex.py @@ -6,8 +6,10 @@ resolve_workspace_name_and_id, lro, _conv_b64, + _decode_b64, ) from sempy.fabric.exceptions import FabricHTTPException +import json def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: @@ -188,3 +190,53 @@ def update_activator_definition( print( f"{icons.green_dot} The '{activator}' activator has been updated within the '{workspace}' workspace." ) + + +def get_activator_definition( + activator: str, workspace: Optional[str] = None, decode: bool = True, +) -> dict: + """ + Gets the definition of an activator (reflex). + + This is a wrapper function for the following API: `Items - Update Reflex Definition `_. + + Parameters + ---------- + activator : str + The name of the activator/reflex. + definition : dict, default=None + The .json definition of an activator/reflex. + workspace : str, default=None + The Fabric workspace name. + Defaults to None which resolves to the workspace of the attached lakehouse + or if no lakehouse attached, resolves to the workspace of the notebook. + decode : bool, default=True + If True, decodes the activator definition file into .json format. + If False, obtains the activator definition file in base64 format. + + Returns + ------- + dict + The activator definition. + """ + + (workspace, workspace_id) = resolve_workspace_name_and_id(workspace) + item_id = fabric.resolve_item_id( + item_name=activator, type="Reflex", workspace=workspace + ) + client = fabric.FabricRestClient() + response = client.post( + f"v1/workspaces/{workspace_id}/reflexes/{item_id}/getDefinition", + ) + + result = lro(client, response).json() + df_items = pd.json_normalize(result["definition"]["parts"]) + df_items_filt = df_items[df_items["path"] == "ReflexEntities.json"] + payload = df_items_filt["payload"].iloc[0] + + if decode: + result = json.loads(_decode_b64(payload)) + else: + result = payload + + return result From aab6ad08618fed57d7e257b53e993d63ba443b9d Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 28 Nov 2024 09:52:33 +0200 Subject: [PATCH 5/6] fix --- src/sempy_labs/_reflex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sempy_labs/_reflex.py b/src/sempy_labs/_reflex.py index 1917811e..d9bd8f13 100644 --- a/src/sempy_labs/_reflex.py +++ b/src/sempy_labs/_reflex.py @@ -226,7 +226,7 @@ def get_activator_definition( ) client = fabric.FabricRestClient() response = client.post( - f"v1/workspaces/{workspace_id}/reflexes/{item_id}/getDefinition", + f"/v1/workspaces/{workspace_id}/reflexes/{item_id}/getDefinition", ) result = lro(client, response).json() From b44b6d69211a3d5fc5b10a5b527818c58982b265 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 29 Dec 2024 12:06:53 +0200 Subject: [PATCH 6/6] removed reflex functions for apis which do not work --- src/sempy_labs/__init__.py | 6 ----- src/sempy_labs/_helper_functions.py | 24 +++++++++++++++++++ src/sempy_labs/_reflex.py | 37 +++++++++++++---------------- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/sempy_labs/__init__.py b/src/sempy_labs/__init__.py index 520ea07b..8326610f 100644 --- a/src/sempy_labs/__init__.py +++ b/src/sempy_labs/__init__.py @@ -1,9 +1,6 @@ from sempy_labs._reflex import ( list_activators, - create_activator, delete_activator, - update_activator_definition, - get_activator_definition, ) from sempy_labs._gateways import ( list_gateway_members, @@ -464,8 +461,5 @@ "update_vnet_gateway", "update_on_premises_gateway", "list_activators", - "create_activator", "delete_activator", - "update_activator_definition", - "get_activator_definition", ] diff --git a/src/sempy_labs/_helper_functions.py b/src/sempy_labs/_helper_functions.py index 01495de9..33a71592 100644 --- a/src/sempy_labs/_helper_functions.py +++ b/src/sempy_labs/_helper_functions.py @@ -160,6 +160,30 @@ def resolve_report_name(report_id: UUID, workspace: Optional[str] = None) -> str return obj +def resolve_item_name_and_id( + item: str | UUID, type: Optional[str] = None, workspace: Optional[str | UUID] = None +) -> Tuple[str, UUID]: + + (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) + + if _is_valid_uuid(item): + item_id = item + item_name = fabric.resolve_item_name( + item_id=item_id, type=type, workspace=workspace_id + ) + else: + if type is None: + raise ValueError( + f"{icons.warning} Must specify a 'type' if specifying a name as the 'item'." + ) + item_name = item + item_id = fabric.resolve_item_id( + item_name=item, type=type, workspace=workspace_id + ) + + return item_name, item_id + + def resolve_dataset_id(dataset: str, workspace: Optional[str] = None) -> UUID: """ Obtains the ID of the semantic model. diff --git a/src/sempy_labs/_reflex.py b/src/sempy_labs/_reflex.py index d9bd8f13..bb5fe171 100644 --- a/src/sempy_labs/_reflex.py +++ b/src/sempy_labs/_reflex.py @@ -4,12 +4,14 @@ from typing import Optional from sempy_labs._helper_functions import ( resolve_workspace_name_and_id, + resolve_item_name_and_id, lro, _conv_b64, _decode_b64, ) from sempy.fabric.exceptions import FabricHTTPException import json +from uuid import UUID def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: @@ -34,7 +36,7 @@ def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) client = fabric.FabricRestClient() - response = client.post(f"/v1/workspaces/{workspace_id}/reflexes") + response = client.get(f"/v1/workspaces/{workspace_id}/reflexes") if response.status_code != 200: raise FabricHTTPException(response) @@ -53,7 +55,7 @@ def list_activators(workspace: Optional[str] = None) -> pd.DataFrame: return df -def delete_activator(activator: str, workspace: Optional[str] = None): +def delete_activator(activator: str | UUID, workspace: Optional[str] = None): """ Deletes an activator (reflex). @@ -61,8 +63,8 @@ def delete_activator(activator: str, workspace: Optional[str] = None): Parameters ---------- - activator : str - The name of the activator/reflex. + activator : str | uuid.UUID + The name or ID of the activator/reflex. workspace : str, default=None The Fabric workspace name. Defaults to None which resolves to the workspace of the attached lakehouse @@ -70,18 +72,15 @@ def delete_activator(activator: str, workspace: Optional[str] = None): """ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) - - item_id = fabric.resolve_item_id( - item_name=activator, type="Reflex", workspace=workspace_id - ) + (item_name, item_id) = resolve_item_name_and_id(item=activator, type='Reflex', workspace=workspace_id) fabric.delete_item(item_id=item_id, workspace=workspace_id) print( - f"{icons.green_dot} The '{activator}' activator within the '{workspace}' workspace has been deleted." + f"{icons.green_dot} The '{item_name}' activator within the '{workspace_name}' workspace has been deleted." ) -def create_activator( +def _create_activator( name: str, definition: Optional[dict] = None, description: Optional[str] = None, @@ -135,14 +134,14 @@ def create_activator( client = fabric.FabricRestClient() response = client.post(f"/v1/workspaces/{workspace_id}/reflexes", json=payload) - lro(client, response, status_codes=[201, 202]) + lro(client, response, status_codes=[201, 202], return_status_code=True) print( - f"{icons.green_dot} The '{name}' activator has been created within the '{workspace}' workspace." + f"{icons.green_dot} The '{name}' activator has been created within the '{workspace_name}' workspace." ) -def update_activator_definition( +def _update_activator_definition( activator: str, definition: dict, workspace: Optional[str] = None ): """ @@ -163,9 +162,7 @@ def update_activator_definition( """ (workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace) - item_id = fabric.resolve_item_id( - item_name=activator, type="Reflex", workspace=workspace_id - ) + (item_name, item_id) = resolve_item_name_and_id(item=activator, type='Reflex', workspace=workspace_id) reflex_payload = _conv_b64(definition) client = fabric.FabricRestClient() @@ -188,11 +185,11 @@ def update_activator_definition( lro(client, response, status_codes=[200, 202], return_status_code=True) print( - f"{icons.green_dot} The '{activator}' activator has been updated within the '{workspace}' workspace." + f"{icons.green_dot} The '{item_name}' activator has been updated within the '{workspace_name}' workspace." ) -def get_activator_definition( +def _get_activator_definition( activator: str, workspace: Optional[str] = None, decode: bool = True, ) -> dict: """ @@ -221,9 +218,7 @@ def get_activator_definition( """ (workspace, workspace_id) = resolve_workspace_name_and_id(workspace) - item_id = fabric.resolve_item_id( - item_name=activator, type="Reflex", workspace=workspace - ) + (item_name, item_id) = resolve_item_name_and_id(item=activator, type='Reflex', workspace=workspace_id) client = fabric.FabricRestClient() response = client.post( f"/v1/workspaces/{workspace_id}/reflexes/{item_id}/getDefinition",