msfabricpysdkcore 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ import json
2
+
3
+ class Capacity:
4
+ """Class to represent a capacity in Microsoft Fabric"""
5
+
6
+
7
+ def __init__(self, id, display_name, sku, region, state):
8
+ """Constructor for the Capacity class
9
+
10
+ Args:
11
+ id (str): The ID of the capacity
12
+ display_name (str): The display name of the capacity
13
+ sku (str): The SKU of the capacity
14
+ region (str): The region of the capacity
15
+ state (str): The state of the capacity
16
+
17
+ Returns:
18
+ Capacity: The Capacity object created
19
+ """
20
+ self.id = id
21
+ self.display_name = display_name
22
+ self.sku = sku
23
+ self.region = region
24
+ self.state = state
25
+
26
+ def __str__(self):
27
+ """Method to return a string representation of the Capacity object
28
+
29
+ Returns:
30
+ str: The string representation of the Capacity object
31
+ """
32
+ dic = {
33
+ 'id': self.id,
34
+ 'display_name': self.display_name,
35
+ 'sku': self.sku,
36
+ 'region': self.region,
37
+ 'state': self.state
38
+ }
39
+ return json.dumps(dic, indent=2)
40
+
41
+ def from_dict(dic):
42
+ """Method to create a Capacity object from a dictionary
43
+
44
+ Args:
45
+ dic (dict): The dictionary containing the capacity information
46
+ Returns:
47
+ Capacity: The Capacity object created from the dictionary
48
+
49
+ """
50
+ if "display_name" not in dic:
51
+ dic["display_name"] = dic["displayName"]
52
+ return Capacity(dic['id'], dic['display_name'], dic['sku'], dic['region'], dic['state'])
53
+
@@ -3,6 +3,7 @@ import json
3
3
  import os
4
4
  from time import sleep
5
5
 
6
+ from msfabricpysdkcore.capacity import Capacity
6
7
  from msfabricpysdkcore.workspace import Workspace
7
8
  from msfabricpysdkcore.auth import FabricAuthClient, FabricServicePrincipal
8
9
 
@@ -25,10 +26,12 @@ class FabricClientCore():
25
26
  self.scope = "https://api.fabric.microsoft.com/.default"
26
27
 
27
28
 
28
- def list_workspaces(self):
29
+ def list_workspaces(self, continuationToken = None):
29
30
  """List all workspaces in the tenant"""
30
31
 
31
32
  url = "https://api.fabric.microsoft.com/v1/workspaces"
33
+ if continuationToken:
34
+ url = f"{url}?continuationToken={continuationToken}"
32
35
 
33
36
  for _ in range(10):
34
37
  response = requests.get(url=url, headers=self.auth.get_headers())
@@ -39,13 +42,16 @@ class FabricClientCore():
39
42
  if response.status_code not in (200, 429):
40
43
  print(response.status_code)
41
44
  print(response.text)
42
- items = json.loads(response.text)["value"]
45
+ raise Exception(f"Error listing workspaces: {response.status_code}, {response.text}")
43
46
  break
47
+ resp_dict = json.loads(response.text)
48
+ ws_list = resp_dict["value"]
49
+ ws_list = [Workspace.from_dict(ws, auth=self.auth) for ws in ws_list]
50
+
51
+ if "continuationToken" in resp_dict:
52
+ ws_list_next = self.list_workspaces(continuationToken=resp_dict["continuationToken"])
53
+ ws_list.extend(ws_list_next)
44
54
 
45
- ws_list = []
46
- for i in items:
47
- ws = Workspace.from_dict(i, auth=self.auth)
48
- ws_list.append(ws)
49
55
  return ws_list
50
56
 
51
57
  def get_workspace_by_name(self, name):
@@ -69,12 +75,12 @@ class FabricClientCore():
69
75
  if response.status_code not in (200, 429):
70
76
  print(response.status_code)
71
77
  print(response.text)
72
- ws_dict = json.loads(response.text)
73
- if "id" not in ws_dict:
74
78
  raise Exception(f"Error getting workspace: {response.status_code} {response.text}")
75
- ws = Workspace.from_dict(json.loads(response.text), auth=self.auth)
79
+ break
80
+ ws_dict = json.loads(response.text)
81
+ ws = Workspace.from_dict(ws_dict, auth=self.auth)
76
82
 
77
- return ws
83
+ return ws
78
84
 
79
85
 
80
86
  def get_workspace(self, id = None, name = None):
@@ -158,10 +164,13 @@ class FabricClientCore():
158
164
  ws = self.get_workspace_by_id(workspace_id)
159
165
  return ws.unassign_from_capacity()
160
166
 
161
- def list_capacities(self):
167
+ def list_capacities(self, continuationToken = None):
162
168
  """List all capacities in the tenant"""
163
169
  url = "https://api.fabric.microsoft.com/v1/capacities"
164
170
 
171
+ if continuationToken:
172
+ url = f"{url}?continuationToken={continuationToken}"
173
+
165
174
  for _ in range(10):
166
175
  response = requests.get(url=url, headers=self.auth.get_headers())
167
176
  if response.status_code == 429:
@@ -174,9 +183,17 @@ class FabricClientCore():
174
183
  raise Exception(f"Error listing capacities: {response.text}")
175
184
  break
176
185
 
177
- items = json.loads(response.text)["value"]
186
+ resp_dict = json.loads(response.text)
187
+ items = resp_dict["value"]
188
+
189
+ if "continuationToken" in resp_dict:
190
+ cap_list_next = self.list_capacities(continuationToken=resp_dict["continuationToken"])
191
+ items.extend(cap_list_next)
178
192
 
193
+ items = json.loads(response.text)["value"]
194
+ items = [Capacity.from_dict(i) for i in items]
179
195
  return items
196
+
180
197
 
181
198
  def create_item(self, workspace_id, display_name, type, definition = None, description = None):
182
199
  """Create an item in a workspace"""
@@ -187,10 +204,11 @@ class FabricClientCore():
187
204
  definition = definition,
188
205
  description = description)
189
206
 
190
- def get_item(self, workspace_id, item_id):
207
+ def get_item(self, workspace_id = None,
208
+ item_id = None, workspace_name = None, item_name = None, item_type = None):
191
209
  """Get an item from a workspace"""
192
- ws = self.get_workspace_by_id(workspace_id)
193
- return ws.get_item(item_id)
210
+ ws = self.get_workspace(id = workspace_id, name = workspace_name)
211
+ return ws.get_item(item_id = item_id, item_name = item_name, item_type = item_type)
194
212
 
195
213
  def delete_item(self, workspace_id, item_id):
196
214
  """Delete an item from a workspace"""
@@ -280,4 +298,39 @@ class FabricClientCore():
280
298
  return ws.update_from_git(remote_commit_hash=remote_commit_hash,
281
299
  conflict_resolution=conflict_resolution,
282
300
  options=options,
283
- workspace_head=workspace_head)
301
+ workspace_head=workspace_head)
302
+
303
+ def get_capacity(self, capacity_id = None, capacity_name = None):
304
+ """Get a capacity
305
+
306
+ Args:
307
+ capacity_id (str): The ID of the capacity
308
+ capacity_name (str): The name of the capacity
309
+
310
+ Returns:
311
+ Capacity: The capacity object
312
+
313
+ Raises:
314
+ ValueError: If no capacity is found
315
+ """
316
+ if capacity_id is None and capacity_name is None:
317
+ raise ValueError("Either capacity_id or capacity_name must be provided")
318
+ caps = self.list_capacities()
319
+ for cap in caps:
320
+ if capacity_id and cap.id == capacity_id:
321
+ return cap
322
+ if capacity_name and cap.display_name == capacity_name:
323
+ return cap
324
+ raise ValueError("No capacity found")
325
+
326
+ def list_tables(self, workspace_id, item_id):
327
+ ws = self.get_workspace_by_id(workspace_id)
328
+ return ws.list_tables(item_id=item_id)
329
+
330
+ def load_table(self, workspace_id, item_id, table_name, path_type, relative_path,
331
+ file_extension = None, format_options = None,
332
+ mode = None, recursive = None, wait_for_completion = True):
333
+ ws = self.get_workspace_by_id(workspace_id)
334
+ return ws.load_table(item_id, table_name, path_type, relative_path,
335
+ file_extension, format_options,
336
+ mode, recursive, wait_for_completion)
msfabricpysdkcore/item.py CHANGED
@@ -6,6 +6,7 @@ from msfabricpysdkcore.onelakeshortcut import OneLakeShortcut
6
6
  from msfabricpysdkcore.job_instance import JobInstance
7
7
  from msfabricpysdkcore.long_running_operation import check_long_running_operation
8
8
 
9
+
9
10
  class Item:
10
11
  """Class to represent a item in Microsoft Fabric"""
11
12
 
@@ -36,6 +37,12 @@ class Item:
36
37
 
37
38
  def from_dict(item_dict, auth):
38
39
  """Create Item object from dictionary"""
40
+
41
+ if item_dict['type'] == "Lakehouse":
42
+ from msfabricpysdkcore.lakehouse import Lakehouse
43
+ return Lakehouse(id=item_dict['id'], display_name=item_dict['displayName'], type=item_dict['type'], workspace_id=item_dict['workspaceId'],
44
+ properties=item_dict.get('properties', None),
45
+ definition=item_dict.get('definition', None), description=item_dict.get('description', ""), auth=auth)
39
46
  return Item(id=item_dict['id'], display_name=item_dict['displayName'], type=item_dict['type'], workspace_id=item_dict['workspaceId'],
40
47
  properties=item_dict.get('properties', None),
41
48
  definition=item_dict.get('definition', None), description=item_dict.get('description', ""), auth=auth)
@@ -52,6 +59,7 @@ class Item:
52
59
  if response.status_code not in (200, 429):
53
60
  print(response.status_code)
54
61
  print(response.text)
62
+ print(self)
55
63
  raise Exception(f"Error deleting item: {response.text}")
56
64
  break
57
65
 
@@ -243,4 +251,11 @@ class Item:
243
251
  def cancel_item_job_instance(self, job_instance_id):
244
252
  """Cancel a job instance ofjob the item"""
245
253
  return self.get_item_job_instance(job_instance_id=job_instance_id).cancel()
246
-
254
+
255
+ def list_tables(self):
256
+ raise NotImplementedError("List tables only works on Lakehouse Items")
257
+
258
+ def load_table(self, table_name, path_type, relative_path,
259
+ file_extension = None, format_options = None,
260
+ mode = None, recursive = None, wait_for_completion = True):
261
+ raise NotImplementedError("Load table only works on Lakehouse Items")
@@ -0,0 +1,96 @@
1
+ import json
2
+ import requests
3
+ from time import sleep
4
+
5
+ from msfabricpysdkcore.long_running_operation import check_long_running_operation
6
+ from msfabricpysdkcore.item import Item
7
+
8
+ class Lakehouse(Item):
9
+ """Class to represent a item in Microsoft Fabric"""
10
+
11
+ def __init__(self, id, display_name, type, workspace_id, auth, properties = None, definition=None, description=""):
12
+ super().__init__(id, display_name, type, workspace_id, auth, properties, definition, description)
13
+
14
+
15
+ def list_tables(self, continuationToken = None):
16
+ """List all tables in the lakehouse"""
17
+ # GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/lakehouses/{lakehouseId}/tables
18
+ url = f"https://api.fabric.microsoft.com/v1/workspaces/{self.workspace_id}/lakehouses/{self.id}/tables"
19
+
20
+ if continuationToken:
21
+ url = f"{url}?continuationToken={continuationToken}"
22
+
23
+ for _ in range(10):
24
+ response = requests.get(url=url, headers=self.auth.get_headers())
25
+ if response.status_code == 429:
26
+ print("Too many requests, waiting 10 seconds")
27
+ sleep(10)
28
+ continue
29
+ if response.status_code not in (200, 429):
30
+ print(response.status_code)
31
+ print(response.text)
32
+ raise Exception(f"Error listing tables: {response.status_code}, {response.text}")
33
+ break
34
+ resp_dict = json.loads(response.text)
35
+
36
+ table_list = resp_dict["data"]
37
+
38
+ if "continuationToken" in resp_dict and resp_dict["continuationToken"] is not None:
39
+ table_list_next = self.list_tables(continuationToken=resp_dict["continuationToken"])
40
+ table_list.extend(table_list_next)
41
+
42
+ return table_list
43
+
44
+ def load_table(self, table_name, path_type, relative_path,
45
+ file_extension = None, format_options = None,
46
+ mode = None, recursive = None, wait_for_completion = True):
47
+ """Load a table in the lakehouse"""
48
+ # POST https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/lakehouses/{lakehouseId}/tables/{tableName}/load
49
+ url = f"https://api.fabric.microsoft.com/v1/workspaces/{self.workspace_id}/lakehouses/{self.id}/tables/{table_name}/load"
50
+
51
+ body = {
52
+ "relativePath": relative_path,
53
+ "pathType": path_type,
54
+ }
55
+
56
+ if file_extension:
57
+ body["fileExtension"] = file_extension
58
+ if format_options:
59
+ body["formatOptions"] = format_options
60
+ if mode:
61
+ body["mode"] = mode
62
+ if recursive:
63
+ body["recursive"] = recursive
64
+
65
+ for _ in range(10):
66
+ response = requests.post(url=url, headers=self.auth.get_headers(), json=body)
67
+ if response.status_code == 429:
68
+ print("Too many requests, waiting 10 seconds")
69
+ sleep(10)
70
+ continue
71
+ if response.status_code == 202:
72
+ if wait_for_completion:
73
+ success = self.check_if_table_is_created(table_name)
74
+
75
+ if not success:
76
+ print("Warning: Table not created after 3 minutes")
77
+ else:
78
+ print("Table created")
79
+ if response.status_code not in (202, 429):
80
+ print(response.status_code)
81
+ print(response.text)
82
+ raise Exception(f"Error loading table: {response.status_code}, {response.text}")
83
+ break
84
+
85
+ return response.status_code
86
+
87
+ def check_if_table_is_created(self, table_name):
88
+ """Check if the table is created"""
89
+ for _ in range(60):
90
+ table_names = [table["name"] for table in self.list_tables()]
91
+ if table_name in table_names:
92
+ return True
93
+
94
+ sleep(3)
95
+ return False
96
+
File without changes
@@ -0,0 +1,62 @@
1
+ import unittest
2
+ from msfabricpysdkcore.client import FabricClientCore
3
+ from datetime import datetime
4
+ from dotenv import load_dotenv
5
+
6
+ class TestFabricClientCore(unittest.TestCase):
7
+
8
+ def __init__(self, *args, **kwargs):
9
+ super(TestFabricClientCore, self).__init__(*args, **kwargs)
10
+ #load_dotenv()
11
+ self.fc = FabricClientCore()
12
+ self.workspace_id = "fd3ba978-0b94-43e2-9f23-f65761e9ff34"
13
+
14
+ def test_git(self):
15
+
16
+ datetime_str = datetime.now().strftime("%Y%m%d%H%M%S")
17
+ ws2_name = "git" + datetime_str
18
+ self.fc.create_workspace(display_name=ws2_name)
19
+ ws2 = self.fc.get_workspace_by_name(name=ws2_name)
20
+ self.fc.assign_to_capacity(workspace_id=ws2.id, capacity_id="a089a991-221c-49be-a41e-9020198cf7af")
21
+
22
+ git_provider_details = {'organizationName': 'dasenf1860',
23
+ 'projectName': 'fabrictest',
24
+ 'gitProviderType': 'AzureDevOps',
25
+ 'repositoryName': 'fabrictest',
26
+ 'branchName': 'main',
27
+ 'directoryName': '/folder1'}
28
+
29
+ status_code = self.fc.git_connect(workspace_id=ws2.id, git_provider_details=git_provider_details)
30
+
31
+ self.assertEqual(status_code, 204)
32
+
33
+ initialization_strategy = "PreferWorkspace"
34
+
35
+ status_code = self.fc.git_initialize_connection(workspace_id=ws2.id, initialization_strategy=initialization_strategy)
36
+ self.assertEqual(status_code, 200)
37
+
38
+ connection_details = self.fc.git_get_connection(workspace_id=ws2.id)
39
+ self.assertEqual(connection_details['gitConnectionState'], 'ConnectedAndInitialized')
40
+
41
+ status = self.fc.git_get_status(workspace_id=ws2.id)
42
+ self.assertTrue(len(status["changes"]) > 0)
43
+
44
+ status_code = self.fc.update_from_git(workspace_id=ws2.id, remote_commit_hash=status["remoteCommitHash"])
45
+
46
+ self.assertEqual(status_code, 202)
47
+
48
+ blubb_lakehouse = False
49
+ for item in ws2.list_items():
50
+ if item.type == "Lakehouse" and item.display_name == "blubb":
51
+ blubb_lakehouse = True
52
+
53
+ self.assertTrue(blubb_lakehouse)
54
+
55
+ status_code = self.fc.git_disconnect(workspace_id=ws2.id)
56
+
57
+ self.assertEqual(status_code, 204)
58
+
59
+ ws2.delete()
60
+
61
+ if __name__ == "__main__":
62
+ unittest.main()
@@ -0,0 +1,69 @@
1
+ import unittest
2
+ from datetime import datetime
3
+ from dotenv import load_dotenv
4
+ from time import sleep
5
+ from msfabricpysdkcore.client import FabricClientCore
6
+
7
+ class TestFabricClientCore(unittest.TestCase):
8
+
9
+ def __init__(self, *args, **kwargs):
10
+ super(TestFabricClientCore, self).__init__(*args, **kwargs)
11
+ #load_dotenv()
12
+ self.fc = FabricClientCore()
13
+ self.workspace_id = "c3352d34-0b54-40f0-b204-cc964b1beb8d"
14
+
15
+ datetime_str = datetime.now().strftime("%Y%m%d%H%M%S")
16
+ self.item_name = "testitem" + datetime_str
17
+ self.item_type = "Notebook"
18
+
19
+ def test_item_end_to_end(self):
20
+
21
+ item = self.fc.create_item(display_name=self.item_name, type=self.item_type, workspace_id=self.workspace_id)
22
+ self.assertEqual(item.display_name, self.item_name)
23
+ self.assertEqual(item.type, self.item_type)
24
+ self.assertEqual(item.workspace_id, self.workspace_id)
25
+ self.assertEqual(item.description, "")
26
+
27
+ item = self.fc.get_item(workspace_id=self.workspace_id, item_id=item.id)
28
+ item_ = self.fc.get_item(workspace_id=self.workspace_id,
29
+ item_name=self.item_name, item_type=self.item_type)
30
+ self.assertEqual(item.id, item_.id)
31
+ self.assertEqual(item.display_name, self.item_name)
32
+ self.assertEqual(item.type, self.item_type)
33
+ self.assertEqual(item.workspace_id, self.workspace_id)
34
+ self.assertEqual(item.description, "")
35
+
36
+ item_list = self.fc.list_items(workspace_id=self.workspace_id)
37
+ self.assertTrue(len(item_list) > 0)
38
+
39
+ item_ids = [item_.id for item_ in item_list]
40
+ self.assertIn(item.id, item_ids)
41
+
42
+ self.fc.update_item(workspace_id=self.workspace_id, item_id=item.id, display_name=f"u{self.item_name}")
43
+ item = self.fc.get_item(workspace_id=self.workspace_id, item_id=item.id)
44
+ self.assertEqual(item.display_name, f"u{self.item_name}")
45
+
46
+ status_code = self.fc.delete_item(workspace_id=self.workspace_id, item_id=item.id)
47
+
48
+ self.assertAlmostEqual(status_code, 200)
49
+
50
+ def test_lakehouse(self):
51
+
52
+ lakehouse = self.fc.get_item(workspace_id=self.workspace_id, item_name="lakehouse1", item_type="Lakehouse")
53
+ item_id = lakehouse.id
54
+ date_str = datetime.now().strftime("%Y%m%d%H%M%S")
55
+ table_name = f"table{date_str}"
56
+
57
+
58
+ status_code = self.fc.load_table(workspace_id=self.workspace_id, item_id=item_id, table_name=table_name,
59
+ path_type="File", relative_path="Files/folder1/titanic.csv")
60
+
61
+ self.assertEqual(status_code, 202)
62
+
63
+ table_list = self.fc.list_tables(workspace_id=self.workspace_id, item_id=item_id)
64
+ table_names = [table["name"] for table in table_list]
65
+
66
+ self.assertIn(table_name, table_names)
67
+
68
+ if __name__ == "__main__":
69
+ unittest.main()
@@ -0,0 +1,41 @@
1
+ import unittest
2
+ from msfabricpysdkcore.client import FabricClientCore
3
+ from datetime import datetime
4
+ from dotenv import load_dotenv
5
+
6
+ class TestFabricClientCore(unittest.TestCase):
7
+
8
+ def __init__(self, *args, **kwargs):
9
+ super(TestFabricClientCore, self).__init__(*args, **kwargs)
10
+ #load_dotenv()
11
+ self.fc = FabricClientCore()
12
+ self.workspace_id = "c3352d34-0b54-40f0-b204-cc964b1beb8d"
13
+ self.item_id = "7e38f344-81df-4805-83b6-b9cc16830500"
14
+
15
+
16
+ def test_jobs_end_to_end(self):
17
+ job = self.fc.run_on_demand_item_job(workspace_id=self.workspace_id,
18
+ item_id=self.item_id,
19
+ job_type="RunNotebook")
20
+
21
+ self.assertEqual(job.item_id, self.item_id)
22
+ self.assertEqual(job.workspace_id, self.workspace_id)
23
+ self.assertEqual(job.job_type, "RunNotebook")
24
+ self.assertIn(job.status, ["NotStarted", "InProgress"])
25
+ self.assertEqual(job.invoke_type, "Manual")
26
+
27
+ job2 = self.fc.get_item_job_instance(workspace_id=self.workspace_id,
28
+ item_id=self.item_id,
29
+ job_instance_id=job.id)
30
+
31
+ self.assertEqual(job.id, job2.id)
32
+
33
+ status_code = self.fc.cancel_item_job_instance(workspace_id=self.workspace_id,
34
+ item_id=self.item_id,
35
+ job_instance_id=job.id)
36
+
37
+ self.assertEqual(status_code, 202)
38
+
39
+ if __name__ == "__main__":
40
+ unittest.main()
41
+
@@ -0,0 +1,53 @@
1
+ import unittest
2
+ from msfabricpysdkcore.client import FabricClientCore
3
+ from datetime import datetime
4
+ from dotenv import load_dotenv
5
+
6
+ class TestFabricClientCore(unittest.TestCase):
7
+
8
+ def __init__(self, *args, **kwargs):
9
+ super(TestFabricClientCore, self).__init__(*args, **kwargs)
10
+ #load_dotenv()
11
+ self.fc = FabricClientCore()
12
+ self.workspace_id = "c3352d34-0b54-40f0-b204-cc964b1beb8d"
13
+
14
+ self.lakehouse_target = "cb4ca0b5-b53b-4879-b206-a53c35cbff55"
15
+ self.lakehouse_shortcut = "e2c09c89-bf97-4f71-bdeb-36338795ec36"
16
+
17
+ datetime_str = datetime.now().strftime("%Y%m%d%H%M%S")
18
+ self.shortcutname = "shortcut" + datetime_str
19
+ self.path_target = "Files/folder1"
20
+ self.path_shortcut = "Files/shortcutfolder"
21
+
22
+ self.target = {'oneLake': {'itemId': self.lakehouse_target,
23
+ 'path': self.path_target,
24
+ 'workspaceId': self.workspace_id}}
25
+
26
+ def test_shortcut_end_to_end(self):
27
+
28
+ item = self.fc.create_shortcut(workspace_id=self.workspace_id,
29
+ item_id=self.lakehouse_shortcut,
30
+ path=self.path_shortcut,
31
+ name=self.shortcutname,
32
+ target=self.target)
33
+ self.assertEqual(item.name, self.shortcutname)
34
+ self.assertEqual(item.path, self.path_shortcut)
35
+ self.assertEqual(item.target, self.target)
36
+
37
+ item = self.fc.get_shortcut(workspace_id=self.workspace_id,
38
+ item_id=self.lakehouse_shortcut,
39
+ path=self.path_shortcut,
40
+ name=self.shortcutname)
41
+ self.assertEqual(item.name, self.shortcutname)
42
+ self.assertEqual(item.path, self.path_shortcut)
43
+ self.assertEqual(item.target, self.target)
44
+
45
+ status_code = self.fc.delete_shortcut(workspace_id=self.workspace_id,
46
+ item_id=self.lakehouse_shortcut,
47
+ path=self.path_shortcut,
48
+ name=self.shortcutname)
49
+
50
+ self.assertAlmostEqual(status_code, 200)
51
+
52
+ if __name__ == "__main__":
53
+ unittest.main()
@@ -0,0 +1,146 @@
1
+ import unittest
2
+ from dotenv import load_dotenv
3
+ from datetime import datetime
4
+ from msfabricpysdkcore.client import FabricClientCore
5
+
6
+ load_dotenv()
7
+
8
+ class TestFabricClientCore(unittest.TestCase):
9
+
10
+ def __init__(self, *args, **kwargs):
11
+ super(TestFabricClientCore, self).__init__(*args, **kwargs)
12
+ self.fc = FabricClientCore()
13
+ datetime_str = datetime.now().strftime("%Y%m%d%H%M%S")
14
+ self.display_name = "testws" + datetime_str
15
+ self.workspace_id = None
16
+
17
+ def test_end_to_end_workspace(self):
18
+
19
+ ws_created = self.fc.create_workspace(display_name=self.display_name,
20
+ description="test workspace",
21
+ exists_ok=False)
22
+ # Add assertions here to verify the result
23
+ self.assertEqual(ws_created.display_name, self.display_name)
24
+ self.workspace_id = ws_created.id
25
+ ws = self.fc.get_workspace_by_id(id = self.workspace_id)
26
+ self.assertEqual(ws.display_name, self.display_name)
27
+ self.assertEqual(ws.description, "test workspace")
28
+
29
+ # def test_assign_to_capacity(self):
30
+
31
+ result_status_code = self.fc.assign_to_capacity(workspace_id=ws.id,
32
+ capacity_id="41cb829c-c231-4e9f-b4fc-f9042a6f9840")
33
+ self.assertEqual(result_status_code, 202)
34
+
35
+
36
+ # def test_list_workspaces(self):
37
+
38
+ result = self.fc.list_workspaces()
39
+ display_names = [ws.display_name for ws in result]
40
+ self.assertIn(self.display_name, display_names)
41
+
42
+ for ws in result:
43
+ if ws.display_name == self.display_name:
44
+ self.assertEqual(ws.capacity_id, "41cb829c-c231-4e9f-b4fc-f9042a6f9840")
45
+
46
+
47
+ # def test_get_workspace_by_name(self):
48
+
49
+ workspace_name = self.display_name
50
+ ws = self.fc.get_workspace_by_name(name = workspace_name)
51
+ self.assertEqual(ws.display_name, self.display_name)
52
+
53
+ # def test_get_workspace_by_id(self):
54
+ ws = self.fc.get_workspace_by_id(id = self.workspace_id)
55
+ self.assertEqual(self.display_name, ws.display_name)
56
+
57
+
58
+ # def test_get_workspace(self):
59
+ result = self.fc.get_workspace_by_id(id = self.workspace_id)
60
+ self.assertEqual(result.display_name, self.display_name)
61
+
62
+ # def test_add_role_assignment(self):
63
+ result_status = self.fc.add_workspace_role_assignment(workspace_id = ws.id,
64
+ principal = {"id" : "fe9dee5d-d244-4c93-8ea1-d5e6a2225c69",
65
+ "type" : "ServicePrincipal"},
66
+ role = 'Member')
67
+
68
+ self.assertEqual(result_status, 200)
69
+
70
+ # def test_get_workspace_role_assignments(self):
71
+ result = self.fc.get_workspace_role_assignments(workspace_id = ws.id)
72
+ self.assertTrue("value" in result)
73
+ self.assertTrue(len(result["value"]) == 2)
74
+ for user in result["value"]:
75
+ if user["principal"]["displayName"] == "fabrictestuser":
76
+ self.assertTrue(user["role"] == "Member")
77
+
78
+ # def test_update_workspace_role_assignment(self):
79
+
80
+ result_status_code = self.fc.update_workspace_role_assignment(workspace_id = ws.id,
81
+ role = "Contributor",
82
+ principal_id = "fe9dee5d-d244-4c93-8ea1-d5e6a2225c69")
83
+
84
+ self.assertEqual(result_status_code, 200)
85
+
86
+ result = self.fc.get_workspace_role_assignments(workspace_id = ws.id)
87
+ self.assertTrue("value" in result)
88
+ self.assertTrue(len(result["value"]) == 2)
89
+ for user in result["value"]:
90
+ if user["principal"]["displayName"] == "fabrictestuser":
91
+ self.assertTrue(user["role"] == "Contributor")
92
+
93
+ # def test_delete_role_assignment(self):
94
+ result_status_code = self.fc.delete_workspace_role_assignment(workspace_id = ws.id,
95
+ principal_id = "fe9dee5d-d244-4c93-8ea1-d5e6a2225c69")
96
+ self.assertEqual(result_status_code, 200)
97
+
98
+ # def test_get_workspace_role_assignments(self):
99
+ result = self.fc.get_workspace_role_assignments(workspace_id = ws.id)
100
+ self.assertTrue("value" in result)
101
+ self.assertTrue(len(result["value"]) == 1)
102
+ user = result["value"][0]
103
+ # self.assertTrue(user["principal"]["displayName"] == "fabricapi")
104
+ self.assertTrue(user["role"] == "Admin")
105
+
106
+ # def test_update_workspace(self):
107
+ ws_updated = self.fc.update_workspace(workspace_id=ws.id,
108
+ display_name="newname8912389u1293",
109
+ description="new description")
110
+ self.assertEqual(ws_updated.display_name, "newname8912389u1293")
111
+ self.assertEqual(ws_updated.description, "new description")
112
+ ws = self.fc.get_workspace_by_id(id = ws.id)
113
+ self.assertEqual(ws.display_name, "newname8912389u1293")
114
+ self.assertEqual(ws.description, "new description")
115
+
116
+ # def test_unassign_from_capacity(self):
117
+
118
+ result_status_code = self.fc.unassign_from_capacity(workspace_id=ws.id)
119
+ self.assertEqual(result_status_code, 202)
120
+ ws = self.fc.get_workspace_by_id(ws.id)
121
+ self.assertEqual(ws.capacity_id, None)
122
+
123
+ # def test_delete_workspace(self):
124
+ result_status = self.fc.delete_workspace(display_name="newname8912389u1293")
125
+ self.assertEqual(result_status, 200)
126
+
127
+ def test_list_capacities(self):
128
+ result = self.fc.list_capacities()
129
+ self.assertTrue(len(result) > 0)
130
+ cap_ids = [cap.id for cap in result]
131
+ self.assertIn("41cb829c-c231-4e9f-b4fc-f9042a6f9840", cap_ids)
132
+
133
+ def test_get_capacity(self):
134
+ capacity = self.fc.get_capacity(capacity_id = "41cb829c-c231-4e9f-b4fc-f9042a6f9840")
135
+ self.assertEqual(capacity.id, "41cb829c-c231-4e9f-b4fc-f9042a6f9840")
136
+
137
+ cap = self.fc.get_capacity(capacity_name= capacity.display_name)
138
+
139
+ self.assertEqual(capacity.id, cap.id)
140
+ self.assertIsNotNone(cap.state)
141
+ self.assertIsNotNone(cap.sku)
142
+ self.assertIsNotNone(cap.region)
143
+
144
+
145
+ if __name__ == "__main__":
146
+ unittest.main()
@@ -240,14 +240,29 @@ class Workspace:
240
240
  print(response.text)
241
241
  raise Exception(f"Error creating item: {response.text}")
242
242
  break
243
-
243
+
244
244
  item_dict = json.loads(response.text)
245
+ if item_dict is None:
246
+ print("Item not returned by API, trying to get it by name")
247
+ return self.get_item_by_name(display_name, type)
245
248
  return Item.from_dict(item_dict, auth=self.auth)
246
249
 
247
250
 
248
- def get_item(self, item_id):
251
+ def get_item_by_name(self, item_name, item_type):
252
+ """Get an item from a workspace by name"""
253
+ ws_items = self.list_items()
254
+ for item in ws_items:
255
+ if item.display_name == item_name and item.type == item_type:
256
+ return item
257
+
258
+ def get_item(self, item_id = None, item_name = None, item_type = None):
249
259
  # GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/items/{itemId}
250
260
  """Get an item from a workspace"""
261
+ if item_id is None and item_name is not None and item_type is not None:
262
+ return self.get_item_by_name(item_name, item_type)
263
+ elif item_id is None:
264
+ raise Exception("item_id or the combination item_name + item_type is required")
265
+
251
266
  url = f"https://api.fabric.microsoft.com/v1/workspaces/{self.id}/items/{item_id}"
252
267
 
253
268
  for _ in range(10):
@@ -259,7 +274,7 @@ class Workspace:
259
274
  if response.status_code not in (200, 429):
260
275
  print(response.status_code)
261
276
  print(response.text)
262
- raise Exception(f"Error getting item capacity: {response.text}")
277
+ raise Exception(f"Error getting item: {response.text}")
263
278
  break
264
279
 
265
280
  item_dict = json.loads(response.text)
@@ -268,11 +283,15 @@ class Workspace:
268
283
  def delete_item(self, item_id):
269
284
  """Delete an item from a workspace"""
270
285
  return self.get_item(item_id).delete()
286
+
271
287
 
272
- def list_items(self):
288
+ def list_items(self, continuationToken = None):
273
289
  """List items in a workspace"""
274
290
  url = f"https://api.fabric.microsoft.com/v1/workspaces/{self.id}/items"
275
291
 
292
+ if continuationToken:
293
+ url = f"{url}?continuationToken={continuationToken}"
294
+
276
295
  for _ in range(10):
277
296
  response = requests.get(url=url, headers=self.auth.get_headers())
278
297
  if response.status_code == 429:
@@ -284,8 +303,16 @@ class Workspace:
284
303
  print(response.text)
285
304
  raise Exception(f"Error listing items: {response.text}")
286
305
  break
287
- items = json.loads(response.text)["value"]
288
- return [Item.from_dict(item, auth=self.auth) for item in items]
306
+
307
+ resp_dict = json.loads(response.text)
308
+ items = resp_dict["value"]
309
+ items = [Item.from_dict(item, auth=self.auth) for item in items]
310
+
311
+ if "continuationToken" in resp_dict:
312
+ item_list_next = self.list_items(continuationToken=resp_dict["continuationToken"])
313
+ items.extend(item_list_next)
314
+
315
+ return items
289
316
 
290
317
  def get_item_definition(self, item_id):
291
318
  """Get the definition of an item from a workspace"""
@@ -478,4 +505,14 @@ class Workspace:
478
505
  raise Exception(f"Error updating from git: {response.text}")
479
506
  break
480
507
 
481
- return response.status_code
508
+ return response.status_code
509
+
510
+ def list_tables(self, item_id):
511
+ return self.get_item(item_id=item_id).list_tables()
512
+
513
+ def load_table(self, item_id, table_name, path_type, relative_path,
514
+ file_extension = None, format_options = None,
515
+ mode = None, recursive = None, wait_for_completion = True):
516
+ return self.get_item(item_id).load_table(table_name, path_type, relative_path,
517
+ file_extension, format_options,
518
+ mode, recursive, wait_for_completion)
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: msfabricpysdkcore
3
- Version: 0.0.1
4
- Summary: A Microsoft Fabric SDK for Python
3
+ Version: 0.0.3
4
+ Summary: A Python SDK for Microsoft Fabric
5
5
  Author: Andreas Rederer
6
6
  Project-URL: Homepage, https://github.com/DaSenf1860/ms-fabric-sdk-core
7
7
  Classifier: Programming Language :: Python :: 3
@@ -13,9 +13,9 @@ License-File: LICENSE
13
13
  Requires-Dist: requests >=2.30.0
14
14
  Requires-Dist: azure-identity >=1.15.0
15
15
 
16
- # A Microsoft Fabric SDK for Python
16
+ # A Python SDK for Microsoft Fabric
17
17
 
18
- This is a Python SDK for Microsoft Fabric. It is a wrapper around the REST APIs of Fabric*.
18
+ This is a Python SDK for Microsoft Fabric. It is a wrapper around the REST APIs (v1) of Fabric*.
19
19
 
20
20
  ![Python hugging a F](assets/fabricpythontransparent.png)
21
21
 
@@ -24,15 +24,17 @@ They are designed to automate your Fabric processes.
24
24
 
25
25
  This SDK helps to interact with the Fabric APIs in a more Pythonic way.
26
26
  Additionally it brings some extra features like:
27
- - Authentication is handled for you (currently Azure CLI Authentification and Service Principal Authentification are supported)
27
+ - Authentication is handled for you (currently Azure CLI Authentication and Service Principal Authentication are supported)
28
28
  - Waiting for completion of long running operations
29
29
  - Retry logic when hitting the API rate limits
30
30
  - Referencing objects by name instead of ID
31
- - More granular objects, e.g. a Workpace and Item object instead of referencing IDs all the time
32
- - Do bulk operations**
33
- - Pagination support**
31
+ - More granular objects, e.g. a Workspace and Item object instead of referencing IDs all the time
32
+ - Do bulk operations (see [Usage Patterns](usage_patterns.md))
33
+ - Pagination support
34
34
 
35
- Currently it supports all Core APIs, i.e.:
35
+ See the latest release notes [here](releasenotes/release_notes.md).
36
+
37
+ Currently it supports all Core APIs and Lakehouse APIs, i.e.:
36
38
  - [Capacities](#working-with-capacities)
37
39
  - [Git](#working-with-git)
38
40
  - [Items](#working-with-items)
@@ -40,13 +42,13 @@ Currently it supports all Core APIs, i.e.:
40
42
  - Long Running Operations
41
43
  - [OneLakeShortcuts](#working-with-one-lake-shortcuts)
42
44
  - [Workspaces](#working-with-workspaces)
45
+ - [Lakehouse APIs](#lakehouse-apis)
43
46
 
44
- It is planned to support also the Admin and Lakehouse APIs and new APIs which are not released yet.
47
+ It is planned to support also the Admin APIs and new APIs which are not released yet.
45
48
  Eventually Power BI APIs like the Scanner API will be covered as well.
46
49
 
47
50
  *Because this SDK uses the API in the background, all limitations and restrictions of the API apply to this SDK as well. This includes rate limits, permissions, etc.
48
51
 
49
- **These features are not yet implemented but are planned for the near future.
50
52
 
51
53
 
52
54
 
@@ -160,11 +162,16 @@ ws.delete_role_assignment(principal_id = "abadfbafb")
160
162
 
161
163
  ```python
162
164
 
165
+
166
+ capacity_object = fc.get_capacity(capacity_id = "0129389012u8938491")
167
+ #or
168
+ capacity_object = fc.get_capacity(capacity_name = "sandboxcapacitygermanywc")
169
+
163
170
  # Assign a capaycity to a workspace
164
171
  fc.assign_to_capacity(workspace_id=workspace_id,
165
- capacity_id="capacityid123123")
172
+ capacity_id=capacity_object.id)
166
173
  # or
167
- ws.assign_to_capacity(capacity_id="capacityid123123")
174
+ ws.assign_to_capacity(capacity_id=capacity_object.id)
168
175
 
169
176
  # Unassign from capacity
170
177
  fc.unassign_from_capacity(workspace_id=ws.id)
@@ -359,7 +366,34 @@ item.cancel_item_job_instance(job_instance_id="job_instance_id")
359
366
 
360
367
  ```
361
368
 
369
+ ## Lakehouse APIs
362
370
 
371
+ # List tables in a Lakehouse
372
+
373
+ ```python
374
+ from msfabricpysdkcore import FabricClientCore
375
+
376
+ fc = FabricClientCore()
377
+ ws = fc.get_workspace_by_name("testworkspace")
378
+ lakehouse = ws.get_item_by_name(item_name="lakehouse1", item_type="Lakehouse")
379
+ table_list = lakehouse.list_tables()
380
+ # or
381
+ table_list = ws.list_tables(item_id = "someitemid")
382
+ # or
383
+ table_list = fc.list_tables(workspace_id = "someworkspaceid", item_id = "someitemid")
384
+
385
+
386
+ # Load a file (like a csv) into a Lakehouse table
387
+
388
+ lakehouse.load_table(table_name="testtable", path_type= "File", relative_path="Files/folder1/titanic.csv")
389
+ # or
390
+ ws.load_table(item_id = "someitemid", table_name="testtable",
391
+ path_type= "File", relative_path="Files/folder1/titanic.csv")
392
+ # or
393
+ fc.load_table(workspace_id = "someworkspaceid", item_id = "someitemid", table_name="testtable",
394
+ path_type= "File", relative_path="Files/folder1/titanic.csv")
395
+
396
+ ```
363
397
 
364
398
  Note: This SDK is not an official SDK from Microsoft. It is a community project and not supported by Microsoft. Use it at your own risk.
365
399
  Also the API is still in preview and might change. This SDK is not yet feature complete and might not cover all APIs yet. Feel free to contribute to this project to make it better.
@@ -0,0 +1,21 @@
1
+ msfabricpysdkcore/__init__.py,sha256=HJeyM1aLgf9AvUGkU4Olky90YyySletA281VbSa0Q2c,36
2
+ msfabricpysdkcore/auth.py,sha256=1t5prgSg2hK7vMutif2lxHOQk_qTo3152h3FMof3y_4,1929
3
+ msfabricpysdkcore/capacity.py,sha256=PD089m34I0BEXGTzLsF0VfLVDp6sixyDR_uUfQvwV8k,1696
4
+ msfabricpysdkcore/client.py,sha256=j2X3VO44mbB2YW0UNPHoArHl6GE54KS7LsFWFD_F0o0,15047
5
+ msfabricpysdkcore/item.py,sha256=654cssmSKmhCF-scNH8J8TQLB_fVP2oRU8aAAU7zJF0,11086
6
+ msfabricpysdkcore/job_instance.py,sha256=Rjv2u1XXMPVew-9gGFQLrnhzXmYfzlO9SeUvA19DDTI,2759
7
+ msfabricpysdkcore/lakehouse.py,sha256=phXIuKdW0khJMJx_BK2X3TJRPXSJ_9B1QKFnt2k7WH4,4011
8
+ msfabricpysdkcore/long_running_operation.py,sha256=PvrVEuasD9lwdJZ6ElsNwBE1G8xW5uDQ8zXcKqpOpXc,3021
9
+ msfabricpysdkcore/onelakeshortcut.py,sha256=20AZJ79v7FvgKLYbJi0BIaawi6qMjnhqUTIlniwnVBI,2033
10
+ msfabricpysdkcore/workspace.py,sha256=8LJeKwKetm9ASsN19a5C5AU3O2cjR1kWtigxdBKGu0E,21475
11
+ msfabricpysdkcore/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ msfabricpysdkcore/tests/test_git.py,sha256=jrbPu5CNdwbnnbLIQv7Z7hdwXzsZuv3tYG92qBy6_U4,2397
13
+ msfabricpysdkcore/tests/test_items_incl_lakehouse.py,sha256=QT-4saUpIN5AXhY3_A1uIGytlQ6yLcTzPoT11L6IhyE,2940
14
+ msfabricpysdkcore/tests/test_jobs.py,sha256=ee6Dsi1YsBQpF0jESY6yNBbjwB-5OOhIcBGG_d4rId4,1643
15
+ msfabricpysdkcore/tests/test_shortcuts.py,sha256=A7KxKXIRVVavzz9CPcNMJg3SWlnfozYOBZuI1u4GVIg,2389
16
+ msfabricpysdkcore/tests/test_workspaces_capacities.py,sha256=-IeQ6trY2lkgfzuQhoTGtrj07tFlxkStJKN_xaHCJT0,6562
17
+ msfabricpysdkcore-0.0.3.dist-info/LICENSE,sha256=1NrGuF-zOmzbwzk3iI6lsP9koyDeKO1B0-8OD_tTvOQ,1156
18
+ msfabricpysdkcore-0.0.3.dist-info/METADATA,sha256=WsTSN8Gd8zVGsrBiN5CI9VWx-SWOLBIutvuOSuUMZJg,12513
19
+ msfabricpysdkcore-0.0.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
20
+ msfabricpysdkcore-0.0.3.dist-info/top_level.txt,sha256=3iRonu6ptDGQN4Yl6G76XGM7xbFNsskiEHW-P2gMQGY,18
21
+ msfabricpysdkcore-0.0.3.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- msfabricpysdkcore/__init__.py,sha256=HJeyM1aLgf9AvUGkU4Olky90YyySletA281VbSa0Q2c,36
2
- msfabricpysdkcore/auth.py,sha256=1t5prgSg2hK7vMutif2lxHOQk_qTo3152h3FMof3y_4,1929
3
- msfabricpysdkcore/client.py,sha256=NutMuFGVHUU8CMj2mSJT5Zw1A_vQ-PRJ_yH0nqwk9aI,12536
4
- msfabricpysdkcore/item.py,sha256=lQODVPxqMLKUwhit7KMDFu3GahW5o7wiWA6KHxMbqu0,10188
5
- msfabricpysdkcore/job_instance.py,sha256=Rjv2u1XXMPVew-9gGFQLrnhzXmYfzlO9SeUvA19DDTI,2759
6
- msfabricpysdkcore/long_running_operation.py,sha256=PvrVEuasD9lwdJZ6ElsNwBE1G8xW5uDQ8zXcKqpOpXc,3021
7
- msfabricpysdkcore/onelakeshortcut.py,sha256=20AZJ79v7FvgKLYbJi0BIaawi6qMjnhqUTIlniwnVBI,2033
8
- msfabricpysdkcore/workspace.py,sha256=mweliwZup5XKy5yx-BGIvhZboRrtvwFYzQ4JFEICv4E,19796
9
- msfabricpysdkcore-0.0.1.dist-info/LICENSE,sha256=1NrGuF-zOmzbwzk3iI6lsP9koyDeKO1B0-8OD_tTvOQ,1156
10
- msfabricpysdkcore-0.0.1.dist-info/METADATA,sha256=-N1T5JesALc6RZ7gJofCmr60ZCjtE8hUV0mEvmqfbyQ,11323
11
- msfabricpysdkcore-0.0.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
12
- msfabricpysdkcore-0.0.1.dist-info/top_level.txt,sha256=3iRonu6ptDGQN4Yl6G76XGM7xbFNsskiEHW-P2gMQGY,18
13
- msfabricpysdkcore-0.0.1.dist-info/RECORD,,