Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M kovalsky/reflexapis #309

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/sempy_labs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from sempy_labs._reflex import (
list_activators,
delete_activator,
)
from sempy_labs._gateways import (
list_gateway_members,
list_gateway_role_assigments,
Expand Down Expand Up @@ -456,4 +460,6 @@
"create_vnet_gateway",
"update_vnet_gateway",
"update_on_premises_gateway",
"list_activators",
"delete_activator",
]
24 changes: 24 additions & 0 deletions src/sempy_labs/_helper_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
237 changes: 237 additions & 0 deletions src/sempy_labs/_reflex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
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,
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:
"""
Shows the activators (reflexes) within a workspace.

This is a wrapper function for the following API: `Items - List Reflexes <https://learn.microsoft.com/rest/api/fabric/reflex/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)

client = fabric.FabricRestClient()
response = client.get(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 | UUID, workspace: Optional[str] = None):
"""
Deletes an activator (reflex).

This is a wrapper function for the following API: `Items - Delete Reflex <https://learn.microsoft.com/rest/api/fabric/reflex/items/delete-reflex>`_.

Parameters
----------
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
or if no lakehouse attached, resolves to the workspace of the notebook.
"""

(workspace_name, workspace_id) = resolve_workspace_name_and_id(workspace)
(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 '{item_name}' activator within the '{workspace_name}' workspace has been deleted."
)


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 <https://learn.microsoft.com/rest/api/fabric/reflex/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)

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], return_status_code=True)

print(
f"{icons.green_dot} The '{name}' activator has been created within the '{workspace_name}' workspace."
)


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 <https://learn.microsoft.com/rest/api/fabric/reflex/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_name, item_id) = resolve_item_name_and_id(item=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 '{item_name}' activator has been updated within the '{workspace_name}' 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 <https://learn.microsoft.com/rest/api/fabric/reflex/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_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",
)

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