Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions .changes/unreleased/added-20251113-131349.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: added
body: set item definitions and metadata
Comment thread
may-hartov marked this conversation as resolved.
Outdated
time: 2025-11-13T13:13:49.062462673Z
39 changes: 34 additions & 5 deletions docs/commands/fs/set.md
Comment thread
may-hartov marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,47 @@ Set a workspace or item property.
**Usage:**

```
fab set <path> -q <jmespath_query> -i <input_to_replace> [-f]
fab set <path> -q <jmespath_query> -i <input_value> [--raw-string] [-f]
```

**Parameters:**

- `<path>`: Path to the resource.
- `-q, --query <jmespath_query>`: JMESPath query.
- `-i, --input <input_to_replace>`: Input value to replace.
- `-q, --query <jmespath_query>`: JMESPath query to the property.
- `-i, --input <input_value>`: Input value to set.
- `--raw-string`: Keep input as literal string without JSON parsing. Use when setting item definition properties that expect JSON-encoded strings. Only relevant for set item command. Optional.
- `-f, --force`: Force set without confirmation. Optional.

**Example:**

```bash
fab set ws1.Workspace -q displayName -i "New Name" -f
```
fab set ws1.Workspace/nb1.Notebook -q .property -i value
```

## Setting Item Properties
Comment thread
may-hartov marked this conversation as resolved.
Outdated

The `set` command supports updating properties in two categories for items:

### 1. Item Metadata

- `displayName` - The display name of the item (all item types)
- `description` - The description of the item (all item types)
- `properties` - Custom properties (`.VariableLibrary` only)

### 2. Item Definition

Any explicit path (specified via the `-q` / `--query` command argument) to properties within the item's `definition` structure according to [Microsoft Fabric item definitions](https://learn.microsoft.com/en-us/rest/api/fabric/articles/item-management/definitions).

### Limitations

- Only one property path can be specified per `-query` argument
Comment thread
may-hartov marked this conversation as resolved.
Outdated
Comment thread
may-hartov marked this conversation as resolved.
Outdated
- Paths must map directly to JSON paths without filters or wildcards
Comment thread
may-hartov marked this conversation as resolved.
Outdated
- Only paths already present in the item definition can be updated. Properties with default values may not appear when retrieved via `get` unless explicitly set previously.

### Common Item-Specific Definition Property Paths

These are friendly names that map to specific paths in the item's definition structure. When using the `set` command, you can use these names directly (as the `-query` / `--q` argument value) as they map to the correct definition paths:
Comment thread
may-hartov marked this conversation as resolved.
Outdated

- **Notebook**: `lakehouse`, `environment`, `warehouse`
- **Report**: `semanticModelId` (applies only to [Report definition.pbir version 1](https://learn.microsoft.com/en-us/power-bi/developer/projects/projects-report?tabs=v1%2Cdesktop#definitionpbir). For other versions, check the correct property path in the Report definition documentation)
- **SparkJobDefinition**: `payload`
39 changes: 30 additions & 9 deletions docs/examples/item_examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ fab ls ws1.Workspace/sem1.SemanticModel

### Update Item

For detailed information on updating item properties, including limitations and property paths, see the [`set` command documentation](../commands/fs/set.md#setting-item-properties).

#### Update Display Name

Change the display name of an item.
Expand Down Expand Up @@ -193,30 +195,49 @@ fab rm ws1.Workspace/lh1.Lakehouse -f

### Update Item Properties

**Configurable Properties by Item Type:**

- All supported items: `displayName`, `description`
- Notebook: `lakehouse`, `environment`, `warehouse`
- Report: `semanticModelId`
- SparkJobDefinition: `payload`

#### Set default lakehouse, environment, or warehouse for a notebook.

```

# Set default lakehouse

```
fab set ws1.Workspace/nb1.Notebook -q lakehouse -i '{"known_lakehouses": [{"id": "00000000-0000-0000-0000-000000000001"}],"default_lakehouse": "00000000-0000-0000-0000-000000000001", "default_lakehouse_name": "lh1","default_lakehouse_workspace_id": "00000000-0000-0000-0000-000000000000"}'
```

# Set default environment
#### Set Default Environment for a Notebook

```
fab set ws1.Workspace/nb1.Notebook -q environment -i '{"environmentId": "00000000-0000-0000-0000-000000000002", "workspaceId": "00000000-0000-0000-0000-000000000000"}'
```

# Set default warehouse
#### Set Default Warehouse for a Notebook

```
fab set ws1.Workspace/nb1.Notebook -q warehouse -i '{"known_warehouses": [{"id": "00000000-0000-0000-0000-000000000003", "type": "Datawarehouse"}], "default_warehouse": "00000000-0000-0000-0000-000000000003"}'
```

#### Rebind Report to Semantic Model

For Report PBIR definition version 1:

```
fab set ws1.Workspace/rep1.Report -q semanticModelId -i "00000000-0000-0000-0000-000000000000"
```

For Report PBIR definition version 2:

```
fab set ws1.Workspace/rep1.Report -q definition.parts[0].payload.datasetReference.byConnection.ConnectionString -i "ConnectionStringPrefix....semanticmodelid=00000000-0000-0000-0000-000000000000"
```

#### Update Notebook Cell Code

Update the code in a specific notebook cell.

```
fab set ws1.Workspace/rep1.Report -q semanticModelId -i "00000000-0000-0000-0000-000000000000
fab set nb1.Notebook -q definition.parts[0].payload.cells[0].source[0] -i "someCode"
Comment thread
may-hartov marked this conversation as resolved.
Outdated
```


Expand Down
21 changes: 13 additions & 8 deletions src/fabric_cli/client/fab_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@
from fabric_cli.errors import ErrorMessages
from fabric_cli.utils import fab_error_parser as utils_errors
from fabric_cli.utils import fab_files as files_utils
from fabric_cli.utils.fab_http_polling_utils import get_polling_interval
from fabric_cli.utils import fab_ui as utils_ui
from fabric_cli.utils.fab_http_polling_utils import get_polling_interval

GUID_PATTERN = r"([a-f0-9\-]{36})"
FABRIC_WORKSPACE_URI_PATTERN = rf"workspaces/{GUID_PATTERN}"




def do_request(
args,
json=None,
Expand Down Expand Up @@ -91,10 +89,12 @@ def do_request(

# Get token
from fabric_cli.core.fab_auth import FabAuth

token = FabAuth().get_access_token(scope)

# Build headers
from fabric_cli.core.fab_context import Context as FabContext

ctxt_cmd = FabContext().command
headers = {
"Authorization": "Bearer " + str(token),
Expand Down Expand Up @@ -355,10 +355,10 @@ def _poll_operation(
args.method = "get"
args.wait = False
args.params = {}

initial_interval = get_polling_interval(original_response.headers)
time.sleep(initial_interval)

while True:
response = do_request(args, hostname=hostname)

Expand All @@ -373,9 +373,14 @@ def _poll_operation(
original_response.status_code = 200
return original_response
elif scope == fab_constant.SCOPE_FABRIC_DEFAULT:
return _fetch_operation_result(
args, uri, response, original_response
)
location_header = response.headers.get("Location", "")
Comment thread
may-hartov marked this conversation as resolved.
if location_header:
return _fetch_operation_result(
args, uri, response, original_response
)

original_response.status_code = 200
return original_response
elif status == "Failed":
fab_logger.log_progress(status)
raise FabricCLIError(
Expand Down
31 changes: 22 additions & 9 deletions src/fabric_cli/client/fab_api_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,11 @@ def delete_item(


def get_item_withdefinition(args: Namespace, item_uri: Optional[bool] = False) -> dict:
"""https://learn.microsoft.com/en-us/rest/api/fabric/core/items/get-item-definition"""
response = get_item(args, item_uri)
item = json.loads(response.text)

args.uri = f"workspaces/{args.ws_id}/items/{args.id}/getDefinition{args.format}"
args.method = "post"
args.wait = True # Wait for the details to be retrieved

try:
def_response = fabric_api.do_request(args)
def_response = get_item_definition(args)
definition = json.loads(def_response.text)
if isinstance(definition, dict):
item.update(definition)
Expand All @@ -77,7 +72,10 @@ def get_item_withdefinition(args: Namespace, item_uri: Optional[bool] = False) -
)
except FabricCLIError as ex:
# Case where user can view the item but not its definitions we will return the item without definitions
if ex.status_code == fab_constant.ERROR_UNAUTHORIZED or ex.status_code == fab_constant.ERROR_FORBIDDEN:
if (
ex.status_code == fab_constant.ERROR_UNAUTHORIZED
or ex.status_code == fab_constant.ERROR_FORBIDDEN
):
return item
else:
raise ex
Expand All @@ -100,9 +98,24 @@ def get_item(
return fabric_api.do_request(args)


def update_item_definition(args: Namespace, payload: str) -> ApiResponse:
def get_item_definition(args: Namespace) -> ApiResponse:
Comment thread
may-hartov marked this conversation as resolved.
"""https://learn.microsoft.com/en-us/rest/api/fabric/core/items/get-item-definition"""
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/getDefinition{args.format}"
args.method = "post"
args.wait = True

return fabric_api.do_request(args)


def update_item_definition(
args: Namespace, payload: str, item_uri: Optional[bool] = False
) -> ApiResponse:
"""https://learn.microsoft.com/en-us/rest/api/fabric/core/items/update-item-definition"""
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/updateDefinition"
if item_uri:
args.uri = f"workspaces/{args.ws_id}/{args.item_uri}/{args.id}/updateDefinition"
else:
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/updateDefinition"

args.method = "post"

return fabric_api.do_request(args, data=payload)
Expand Down
92 changes: 58 additions & 34 deletions src/fabric_cli/commands/fs/set/fab_fs_set_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
from argparse import Namespace

from fabric_cli.client import fab_api_item as item_api
from fabric_cli.commands.fs.get import fab_fs_get_item as get_item
from fabric_cli.core import fab_constant
from fabric_cli.core.fab_commands import Command
from fabric_cli.core.fab_types import definition_format_mapping, format_mapping
from fabric_cli.core.hiearchy.fab_hiearchy import Item
from fabric_cli.utils import fab_cmd_set_utils as utils_set
from fabric_cli.utils import fab_mem_store as utils_mem_store
Expand All @@ -16,49 +17,72 @@
def exec(item: Item, args: Namespace) -> None:
force = args.force
query = args.query
raw_string = getattr(args, "raw_string", False)
Comment thread
may-hartov marked this conversation as resolved.
Outdated

utils_set.validate_expression(query, item.get_mutable_properties())
query_value = item.get_mutable_prop_path(query)
Comment thread
may-hartov marked this conversation as resolved.
Outdated

# Get item
args.output = None
args.deep_traversal = True
item_def = get_item.exec(item, args, verbose=False, decode=False)
if query_value is None:
query_value = query

utils_set.validate_item_query(query_value)

utils_set.print_set_warning()
if force or utils_ui.prompt_confirm():
args.output = None
args.deep_traversal = True
args.ws_id = item.workspace.id
args.id = item.id
args.item_uri = format_mapping.get(item.item_type, "items")

query_value = item.get_property_value(query)
if query_value == fab_constant.ITEM_QUERY_DEFINITION or query_value.startswith(
Comment thread
may-hartov marked this conversation as resolved.
Outdated
Comment thread
may-hartov marked this conversation as resolved.
Outdated
f"{fab_constant.ITEM_QUERY_DEFINITION}."
):
if not item.check_command_support(Command.FS_EXPORT):
raise utils_set.FabricCLIError(
f"Item type '{item.item_type}' does not support definition updates",
Comment thread
may-hartov marked this conversation as resolved.
Outdated
utils_set.fab_constant.ERROR_UNSUPPORTED_COMMAND,
Comment thread
may-hartov marked this conversation as resolved.
Outdated
)

# Update item
json_payload, updated_def = utils_set.update_fabric_element(
item_def, query_value, args.input, decode_encode=True
)
args.format = definition_format_mapping.get(item.item_type, "")
def_response = item_api.get_item_definition(args)
definition = json.loads(def_response.text)

definition_base64_to_update, name_description_properties = (
utils_set.extract_json_schema(updated_def)
)
json_payload, updated_def = utils_set.update_fabric_element(
definition,
query_value,
args.input,
decode_encode=True,
raw_string=raw_string,
)

args.ws_id = item.workspace.id
args.id = item.id
update_item_definition_payload = json.dumps(definition_base64_to_update)
update_item_payload = json.dumps(name_description_properties)

utils_ui.print_grey(f"Setting new property for '{item.name}'...")
item_api.update_item(args, update_item_payload)

try:
if query_value.startswith("definition") and item.check_command_support(
Command.FS_EXPORT
):
item_api.update_item_definition(args, update_item_definition_payload)
except Exception:
utils_ui.print_grey(
"Item supports only updating displayName or description, not definition",
definition_base64_to_update, _ = utils_set.extract_json_schema(updated_def)
Comment thread
may-hartov marked this conversation as resolved.
update_item_definition_payload = json.dumps(definition_base64_to_update)

utils_ui.print_grey(f"Setting new property for '{item.name}'...")
item_api.update_item_definition(args, update_item_definition_payload)
Comment thread
may-hartov marked this conversation as resolved.
else:
item_metadata = json.loads(item_api.get_item(args, item_uri=True).text)

json_payload, updated_metadata = utils_set.update_fabric_element(
item_metadata,
query_value,
args.input,
decode_encode=False,
raw_string=raw_string,
)

update_payload_dict = utils_set.extract_updated_properties(
updated_metadata, query_value
)
item_update_payload = json.dumps(update_payload_dict)

utils_ui.print_grey(f"Setting new property for '{item.name}'...")

item_api.update_item(args, item_update_payload, item_uri=True)

# Update mem_store
new_item_name = name_description_properties["displayName"]
item._name = new_item_name
utils_mem_store.upsert_item_to_cache(item)
if fab_constant.ITEM_QUERY_DISPLAY_NAME in updated_metadata:
new_item_name = updated_metadata["displayName"]
item._name = new_item_name
utils_mem_store.upsert_item_to_cache(item)

utils_ui.print_output_format(args, message="Item updated")
13 changes: 13 additions & 0 deletions src/fabric_cli/core/fab_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,16 @@
"workspaceId",
"folderId",
}

# Item set constants
ITEM_QUERY_DEFINITION = "definition"
ITEM_QUERY_PROPERTIES = "properties"
ITEM_QUERY_DISPLAY_NAME = "displayName"
ITEM_QUERY_DESCRIPTION = "description"

# Allowed metadata keys for item set operations
ITEM_SET_ALLOWED_METADATA_KEYS = [
Comment thread
may-hartov marked this conversation as resolved.
ITEM_QUERY_DISPLAY_NAME,
ITEM_QUERY_DESCRIPTION,
ITEM_QUERY_PROPERTIES,
]
Loading