Skip to content

Commit c2f4315

Browse files
ayeshurunAlon YeshurunCopilotCopilot
authored
refactor(core): consolidate format-building logic in utils (#195)
Co-authored-by: Alon Yeshurun <alonyeshurun+microsoft@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 6be9809 commit c2f4315

15 files changed

Lines changed: 219 additions & 185 deletions

File tree

src/fabric_cli/client/fab_api_item.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ def get_item(
100100

101101
def get_item_definition(args: Namespace) -> ApiResponse:
102102
"""https://learn.microsoft.com/en-us/rest/api/fabric/core/items/get-item-definition"""
103-
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/getDefinition{args.format}"
103+
args.uri = f"workspaces/{args.ws_id}/items/{args.id}/getDefinition"
104+
if args.format:
105+
args.uri += f"?format={args.format}"
104106
args.method = "post"
105107
args.wait = True
106108

src/fabric_cli/commands/fs/export/fab_fs_export_item.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
from fabric_cli.core import fab_constant
1111
from fabric_cli.core.fab_commands import Command
1212
from fabric_cli.core.fab_exceptions import FabricCLIError
13-
from fabric_cli.core.fab_types import ItemType, definition_format_mapping
13+
from fabric_cli.core.fab_types import ItemType
1414
from fabric_cli.core.hiearchy.fab_folder import Folder
1515
from fabric_cli.core.hiearchy.fab_hiearchy import Item, Workspace
16-
from fabric_cli.errors import ErrorMessages
1716
from fabric_cli.utils import fab_cmd_export_utils as utils_export
1817
from fabric_cli.utils import fab_item_util, fab_mem_store, fab_storage, fab_ui
1918

@@ -103,27 +102,8 @@ def export_single_item(
103102
args.from_path = item.path.strip("/")
104103
args.ws_id, args.id, args.item_type = workspace_id, item_id, str(item_type)
105104

106-
# Get definition_format_mapping for item without default fallback
107-
valid_export_formats = definition_format_mapping.get(item_type, {})
108-
# Get export_format_param from args without default
109105
export_format_param = getattr(args, "format", None)
110-
111-
if export_format_param not in valid_export_formats:
112-
# Export format not in definition_format_mapping
113-
if not export_format_param:
114-
# Empty format param - use default formats if exists
115-
args.format = valid_export_formats.get("default", "")
116-
else:
117-
# Non-empty format param but not supported
118-
available_formats = [k for k in valid_export_formats.keys() if k != "default"]
119-
raise FabricCLIError(
120-
ErrorMessages.Export.invalid_export_format(available_formats),
121-
fab_constant.ERROR_INVALID_INPUT,
122-
)
123-
else:
124-
# Export format is explicitly supported
125-
args.format = valid_export_formats[export_format_param]
126-
106+
args.format = fab_item_util.resolve_definition_format(item_type, export_format_param)
127107

128108
item_def = item_api.get_item_withdefinition(args, item_uri)
129109

src/fabric_cli/commands/fs/impor/fab_fs_import_item.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,19 @@
88
from fabric_cli.client.fab_api_types import ApiResponse
99
from fabric_cli.core import fab_constant, fab_logger
1010
from fabric_cli.core.fab_exceptions import FabricCLIError
11-
from fabric_cli.core.fab_types import ItemType, definition_format_mapping
11+
from fabric_cli.core.fab_types import ItemType
1212
from fabric_cli.core.hiearchy.fab_hiearchy import Item
1313
from fabric_cli.utils import fab_cmd_import_utils as utils_import
14+
from fabric_cli.utils import fab_item_util
1415
from fabric_cli.utils import fab_mem_store as utils_mem_store
1516
from fabric_cli.utils import fab_storage as utils_storage
1617
from fabric_cli.utils import fab_ui as utils_ui
1718

1819

1920
def import_single_item(item: Item, args: Namespace) -> None:
20-
_input_format = None
21-
if args.format:
22-
_input_format = args.format
23-
if item.item_type in definition_format_mapping:
24-
valid_formats = list(
25-
definition_format_mapping[item.item_type].keys())
26-
if _input_format not in valid_formats:
27-
available_formats = [
28-
k for k in valid_formats if k != "default"]
29-
raise FabricCLIError(
30-
f"Invalid format. Only the following formats are supported: {', '.join(available_formats)}",
31-
fab_constant.ERROR_INVALID_INPUT,
32-
)
33-
else:
34-
raise FabricCLIError(
35-
f"Invalid format. No formats are supported",
36-
fab_constant.ERROR_INVALID_INPUT,
37-
)
21+
_input_format = fab_item_util.resolve_definition_format(
22+
item_type=item.item_type, format_param=getattr(args, "format", None)
23+
)
3824

3925
args.ws_id = item.workspace.id
4026
input_path = utils_storage.get_import_path(args.input)
@@ -54,7 +40,7 @@ def import_single_item(item: Item, args: Namespace) -> None:
5440

5541
# Get the payload
5642
payload = utils_import.get_payload_for_item_type(
57-
_input_path, item, _input_format
43+
_input_path, item, input_format=_input_format
5844
)
5945

6046
if item_exists:

src/fabric_cli/commands/fs/set/fab_fs_set_item.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,24 @@ def exec(item: Item, args: Namespace) -> None:
3333
args.item_uri = format_mapping.get(item.item_type, "items")
3434

3535
if query_value.startswith(fab_constant.ITEM_QUERY_DEFINITION):
36-
formats = definition_format_mapping.get(item.item_type, {"default": ""})
36+
formats = definition_format_mapping.get(
37+
item.item_type, {"default": ""})
38+
# plain value; query param built in get_item_definition()
3739
args.format = formats["default"]
3840
def_response = item_api.get_item_definition(args)
3941
definition = json.loads(def_response.text)
4042

41-
updated_def = _update_item_definition(definition, query_value, args.input)
43+
updated_def = _update_item_definition(
44+
definition, query_value, args.input)
4245

4346
update_item_definition_payload = json.dumps(updated_def)
4447

4548
utils_ui.print_grey(f"Setting new property for '{item.name}'...")
46-
item_api.update_item_definition(args, update_item_definition_payload)
49+
item_api.update_item_definition(
50+
args, update_item_definition_payload)
4751
else:
48-
item_metadata = json.loads(item_api.get_item(args, item_uri=True).text)
52+
item_metadata = json.loads(
53+
item_api.get_item(args, item_uri=True).text)
4954

5055
update_payload_dict = _update_item_metadata(
5156
item_metadata, query_value, args.input

src/fabric_cli/core/fab_types.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -583,19 +583,19 @@ class MirroredDatabaseFolders(Enum):
583583

584584
definition_format_mapping = {
585585
ItemType.SPARK_JOB_DEFINITION: {
586-
"default": "?format=SparkJobDefinitionV1",
587-
"SparkJobDefinitionV1": "?format=SparkJobDefinitionV1",
588-
"SparkJobDefinitionV2": "?format=SparkJobDefinitionV2",
586+
"default": "SparkJobDefinitionV1",
587+
"SparkJobDefinitionV1": "SparkJobDefinitionV1",
588+
"SparkJobDefinitionV2": "SparkJobDefinitionV2",
589589
},
590590
ItemType.NOTEBOOK: {
591-
"default": "?format=ipynb",
592-
".py": "?format=fabricGitSource",
593-
".ipynb": "?format=ipynb",
591+
"default": "ipynb",
592+
".py": "fabricGitSource",
593+
".ipynb": "ipynb",
594594
},
595595
ItemType.SEMANTIC_MODEL: {
596596
"default": "",
597-
"TMDL": "?format=TMDL",
598-
"TMSL": "?format=TMSL",
597+
"TMDL": "TMDL",
598+
"TMSL": "TMSL",
599599
},
600600
ItemType.COSMOS_DB_DATABASE: {"default": ""},
601601
ItemType.USER_DATA_FUNCTION: {"default": ""},

src/fabric_cli/core/hiearchy/fab_item.py

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -74,89 +74,5 @@ def workspace(self) -> Workspace:
7474
assert isinstance(self.parent, Folder)
7575
return self.parent.workspace
7676

77-
def get_payload(self, definition, input_format=None) -> dict:
78-
match self.item_type:
79-
80-
case ItemType.SPARK_JOB_DEFINITION:
81-
return {
82-
"type": str(self.item_type),
83-
"description": "Imported from fab",
84-
"folderId": self.folder_id,
85-
"displayName": self.short_name,
86-
"definition": {
87-
"format": (
88-
"SparkJobDefinitionV1"
89-
if input_format is None
90-
else input_format
91-
),
92-
"parts": definition["parts"],
93-
},
94-
}
95-
case ItemType.NOTEBOOK:
96-
return {
97-
"type": str(self.item_type),
98-
"description": "Imported from fab",
99-
"folderId": self.folder_id,
100-
"displayName": self.short_name,
101-
"definition": {
102-
**(
103-
{"parts": definition["parts"]}
104-
if input_format == ".py"
105-
else {"format": "ipynb", "parts": definition["parts"]}
106-
)
107-
},
108-
}
109-
case ItemType.SEMANTIC_MODEL:
110-
return {
111-
"type": str(self.item_type),
112-
"description": "Imported from fab",
113-
"folderId": self.folder_id,
114-
"displayName": self.short_name,
115-
"definition": (
116-
definition
117-
if input_format is None
118-
else {
119-
"format": input_format,
120-
"parts": definition["parts"],
121-
}
122-
),
123-
}
124-
case (
125-
ItemType.REPORT
126-
| ItemType.KQL_DASHBOARD
127-
| ItemType.DATA_PIPELINE
128-
| ItemType.KQL_QUERYSET
129-
| ItemType.EVENTHOUSE
130-
| ItemType.KQL_DATABASE
131-
| ItemType.MIRRORED_DATABASE
132-
| ItemType.DIGITAL_TWIN_BUILDER
133-
| ItemType.REFLEX
134-
| ItemType.EVENTSTREAM
135-
| ItemType.MOUNTED_DATA_FACTORY
136-
| ItemType.COPYJOB
137-
| ItemType.VARIABLE_LIBRARY
138-
| ItemType.GRAPHQLAPI
139-
| ItemType.DATAFLOW
140-
| ItemType.SQL_DATABASE
141-
| ItemType.COSMOS_DB_DATABASE
142-
| ItemType.GRAPH_QUERY_SET
143-
| ItemType.USER_DATA_FUNCTION
144-
| ItemType.MAP
145-
):
146-
return {
147-
"type": str(self.item_type),
148-
"description": "Imported from fab",
149-
"folderId": self.folder_id,
150-
"displayName": self.short_name,
151-
"definition": definition,
152-
}
153-
case _:
154-
raise FabricCLIError(
155-
ErrorMessages.Hierarchy.item_type_doesnt_support_definition_payload(
156-
str(self.item_type)
157-
),
158-
fab_constant.ERROR_UNSUPPORTED_COMMAND,
159-
)
160-
16177
def get_folders(self) -> List[str]:
16278
return ItemFoldersMap.get(self.item_type, [])

src/fabric_cli/errors/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from .config import ConfigErrors
88
from .context import ContextErrors
99
from .cp import CpErrors
10-
from .export import ExportErrors
1110
from .hierarchy import HierarchyErrors
1211
from .labels import LabelsErrors
1312
from .mkdir import MkdirErrors
@@ -23,7 +22,6 @@ class ErrorMessages:
2322
Config = ConfigErrors
2423
Context = ContextErrors
2524
Cp = CpErrors
26-
Export = ExportErrors
2725
Hierarchy = HierarchyErrors
2826
Labels = LabelsErrors
2927
Mkdir = MkdirErrors

src/fabric_cli/errors/common.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,12 @@ def gateway_property_not_supported_for_type(
248248
@staticmethod
249249
def query_not_supported_for_set(query: str) -> str:
250250
return f"Query '{query}' is not supported for set command"
251+
252+
@staticmethod
253+
def invalid_definition_format(valid_formats: list[str]) -> str:
254+
if valid_formats:
255+
message = f"Only the following formats are supported: {', '.join(valid_formats)}"
256+
else:
257+
message = "No formats are supported"
258+
return f"Invalid format. {message}"
259+

src/fabric_cli/errors/export.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/fabric_cli/utils/fab_cmd_import_utils.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,17 @@ def get_payload_for_item_type(
2525
if item.item_type == ItemType.ENVIRONMENT:
2626
return _build_environment_payload(path)
2727
else:
28-
base64_definition = _build_payload(path)
29-
return item.get_payload(base64_definition, input_format)
28+
definition = _build_definition(path, input_format)
29+
return {
30+
"type": str(item.item_type),
31+
"description": "Imported from fab",
32+
"folderId": item.folder_id,
33+
"displayName": item.short_name,
34+
"definition": definition,
35+
}
3036

3137

32-
def _build_payload(input_path: Any) -> dict:
38+
def _build_definition(input_path: Any, input_format: Optional[str] = None) -> dict:
3339
directory = input_path
3440
parts = []
3541

@@ -67,9 +73,10 @@ def _build_payload(input_path: Any) -> dict:
6773
}
6874
)
6975

70-
# Create the final JSON structure
71-
payload_structure = {"parts": parts}
72-
return payload_structure
76+
definition: dict = {"parts": parts}
77+
if input_format:
78+
definition["format"] = input_format
79+
return definition
7380

7481

7582
def _encode_file_to_base64(file_path: str) -> str:

0 commit comments

Comments
 (0)