msfabricpysdkcore 0.0.2__py3-none-any.whl → 0.0.4__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.
- msfabricpysdkcore/__init__.py +2 -1
- msfabricpysdkcore/admin_item.py +106 -0
- msfabricpysdkcore/admin_workspace.py +127 -0
- msfabricpysdkcore/adminapi.py +535 -0
- msfabricpysdkcore/capacity.py +56 -0
- msfabricpysdkcore/client.py +4 -265
- msfabricpysdkcore/coreapi.py +324 -0
- msfabricpysdkcore/domain.py +378 -0
- msfabricpysdkcore/item.py +19 -1
- msfabricpysdkcore/job_instance.py +3 -0
- msfabricpysdkcore/lakehouse.py +96 -0
- msfabricpysdkcore/onelakeshortcut.py +3 -0
- msfabricpysdkcore/tests/__init__.py +0 -0
- msfabricpysdkcore/tests/test_admin_apis.py +69 -0
- msfabricpysdkcore/tests/test_domains.py +125 -0
- msfabricpysdkcore/tests/test_git.py +62 -0
- msfabricpysdkcore/tests/test_items_incl_lakehouse.py +69 -0
- msfabricpysdkcore/tests/test_jobs.py +39 -0
- msfabricpysdkcore/tests/test_shortcuts.py +53 -0
- msfabricpysdkcore/tests/test_workspaces_capacities.py +146 -0
- msfabricpysdkcore/workspace.py +47 -7
- {msfabricpysdkcore-0.0.2.dist-info → msfabricpysdkcore-0.0.4.dist-info}/METADATA +201 -19
- msfabricpysdkcore-0.0.4.dist-info/RECORD +28 -0
- msfabricpysdkcore-0.0.2.dist-info/RECORD +0 -13
- {msfabricpysdkcore-0.0.2.dist-info → msfabricpysdkcore-0.0.4.dist-info}/LICENSE +0 -0
- {msfabricpysdkcore-0.0.2.dist-info → msfabricpysdkcore-0.0.4.dist-info}/WHEEL +0 -0
- {msfabricpysdkcore-0.0.2.dist-info → msfabricpysdkcore-0.0.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,378 @@
|
|
1
|
+
import json
|
2
|
+
from time import sleep
|
3
|
+
|
4
|
+
import requests
|
5
|
+
from msfabricpysdkcore.long_running_operation import check_long_running_operation
|
6
|
+
|
7
|
+
class Domain:
|
8
|
+
"""Class to represent a domain in Microsoft Fabric"""
|
9
|
+
|
10
|
+
def __init__(self, id, display_name, description, parent_domain_id, contributors_scope, auth):
|
11
|
+
"""Constructor for the Domain class
|
12
|
+
|
13
|
+
Args:
|
14
|
+
id (str): The ID of the domain
|
15
|
+
display_name (str): The display name of the domain
|
16
|
+
description (str): The description of the domain
|
17
|
+
parent_domain_id (str): The parent domain ID of the domain
|
18
|
+
contributors_scope (str): The contributors scope of the domain
|
19
|
+
Returns:
|
20
|
+
Domain: The Domain object created"""
|
21
|
+
|
22
|
+
self.id = id
|
23
|
+
self.display_name = display_name
|
24
|
+
self.description = description
|
25
|
+
self.parent_domain_id = parent_domain_id
|
26
|
+
self.contributors_scope = contributors_scope
|
27
|
+
|
28
|
+
self.auth = auth
|
29
|
+
|
30
|
+
def __str__(self):
|
31
|
+
"""Method to return a string representation of the Domain object
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
str: The string representation of the Domain object
|
35
|
+
"""
|
36
|
+
dic = {
|
37
|
+
'id': self.id,
|
38
|
+
'display_name': self.display_name,
|
39
|
+
'description': self.description,
|
40
|
+
'parent_domain_id': self.parent_domain_id,
|
41
|
+
'contributors_scope': self.contributors_scope
|
42
|
+
}
|
43
|
+
return json.dumps(dic, indent=2)
|
44
|
+
|
45
|
+
def __repr__(self) -> str:
|
46
|
+
return self.__str__()
|
47
|
+
|
48
|
+
def from_dict(dic, auth):
|
49
|
+
"""Method to create a Domain object from a dictionary
|
50
|
+
|
51
|
+
Args:
|
52
|
+
dic (dict): The dictionary containing the domain information
|
53
|
+
Returns:
|
54
|
+
Domain: The Domain object created from the dictionary
|
55
|
+
|
56
|
+
"""
|
57
|
+
if "display_name" not in dic:
|
58
|
+
dic["display_name"] = dic["displayName"]
|
59
|
+
if "parent_domain_id" not in dic:
|
60
|
+
dic["parent_domain_id"] = dic["parentDomainId"]
|
61
|
+
if "contributors_scope" not in dic:
|
62
|
+
dic["contributors_scope"] = dic["contributorsScope"]
|
63
|
+
return Domain(id=dic['id'], display_name=dic['display_name'],
|
64
|
+
description=dic['description'], parent_domain_id=dic['parent_domain_id'],
|
65
|
+
contributors_scope=dic['contributors_scope'], auth=auth)
|
66
|
+
|
67
|
+
def list_domain_workspaces(self, workspace_objects = False, continuationToken = None):
|
68
|
+
"""Method to list the workspaces in the domain
|
69
|
+
|
70
|
+
Args:
|
71
|
+
continuationToken (str): The continuation token to use for pagination
|
72
|
+
Returns:
|
73
|
+
list: The list of workspaces in the domain
|
74
|
+
"""
|
75
|
+
if workspace_objects:
|
76
|
+
from msfabricpysdkcore import FabricClientCore
|
77
|
+
fc = FabricClientCore()
|
78
|
+
|
79
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/workspaces"
|
80
|
+
if continuationToken:
|
81
|
+
url = f"{url}?continuationToken={continuationToken}"
|
82
|
+
|
83
|
+
for _ in range(10):
|
84
|
+
response = requests.get(url=url, headers=self.auth.get_headers())
|
85
|
+
if response.status_code == 429:
|
86
|
+
print("Too many requests, waiting 10 seconds")
|
87
|
+
sleep(10)
|
88
|
+
continue
|
89
|
+
if response.status_code not in (200, 429):
|
90
|
+
print(response.status_code)
|
91
|
+
print(response.text)
|
92
|
+
raise Exception(f"Error listing workspaces: {response.text}")
|
93
|
+
break
|
94
|
+
|
95
|
+
resp_dict = json.loads(response.text)
|
96
|
+
workspaces = resp_dict["value"]
|
97
|
+
|
98
|
+
if workspace_objects:
|
99
|
+
workspaces = [fc.get_workspace_by_id(workspace["id"]) for workspace in workspaces]
|
100
|
+
|
101
|
+
if "continuationToken" in resp_dict:
|
102
|
+
workspaces_next = self.list_domain_workspaces(continuationToken=resp_dict["continuationToken"])
|
103
|
+
workspaces.extend(workspaces_next)
|
104
|
+
|
105
|
+
return workspaces
|
106
|
+
|
107
|
+
def delete(self):
|
108
|
+
"""Method to delete the domain
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
int: The status code of the response
|
112
|
+
"""
|
113
|
+
# DELETE https://api.fabric.microsoft.com/v1/admin/domains/{domainId}
|
114
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}"
|
115
|
+
for _ in range(10):
|
116
|
+
response = requests.delete(url=url, headers=self.auth.get_headers())
|
117
|
+
if response.status_code == 429:
|
118
|
+
print("Too many requests, waiting 10 seconds")
|
119
|
+
sleep(10)
|
120
|
+
continue
|
121
|
+
if response.status_code not in (200, 429):
|
122
|
+
print(response.status_code)
|
123
|
+
print(response.text)
|
124
|
+
raise Exception(f"Error deleting domain: {response.text}")
|
125
|
+
break
|
126
|
+
|
127
|
+
return response.status_code
|
128
|
+
|
129
|
+
|
130
|
+
# PATCH https://api.fabric.microsoft.com/v1/admin/domains/{domainId}
|
131
|
+
|
132
|
+
def update(self, description = None, display_name = None, contributors_scope = None):
|
133
|
+
"""Method to update the domain
|
134
|
+
|
135
|
+
Args:
|
136
|
+
description (str): The description of the domain
|
137
|
+
display_name (str): The display name of the domain
|
138
|
+
contributors_scope (str): The contributors scope of the domain
|
139
|
+
Returns:
|
140
|
+
Domain: The Domain object created from the dictionary
|
141
|
+
"""
|
142
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}"
|
143
|
+
body = {}
|
144
|
+
if description:
|
145
|
+
body["description"] = description
|
146
|
+
else:
|
147
|
+
body["description"] = self.description
|
148
|
+
|
149
|
+
if display_name:
|
150
|
+
body["displayName"] = display_name
|
151
|
+
else:
|
152
|
+
body["displayName"] = self.display_name
|
153
|
+
|
154
|
+
if contributors_scope:
|
155
|
+
body["contributorsScope"] = contributors_scope
|
156
|
+
else:
|
157
|
+
body["contributorsScope"] = self.contributors_scope
|
158
|
+
|
159
|
+
for _ in range(10):
|
160
|
+
response = requests.patch(url=url, headers=self.auth.get_headers(), json=body)
|
161
|
+
if response.status_code == 429:
|
162
|
+
print("Too many requests, waiting 10 seconds")
|
163
|
+
sleep(10)
|
164
|
+
continue
|
165
|
+
if response.status_code not in (200, 429):
|
166
|
+
print(response.status_code)
|
167
|
+
print(response.text)
|
168
|
+
raise Exception(f"Error updating domain: {response.text}")
|
169
|
+
break
|
170
|
+
|
171
|
+
self.description = body["description"]
|
172
|
+
self.display_name = body["displayName"]
|
173
|
+
self.contributors_scope = body["contributorsScope"]
|
174
|
+
|
175
|
+
return self
|
176
|
+
|
177
|
+
def assign_workspaces_by_capacities(self, capacities_ids, wait_for_completion=True):
|
178
|
+
"""Method to assign workspaces to the domain based on capacities_ids
|
179
|
+
|
180
|
+
Args:
|
181
|
+
capacities_ids (list): The list of capacitiy ids to assign workspaces
|
182
|
+
Returns:
|
183
|
+
int: The status code of the response
|
184
|
+
"""
|
185
|
+
# POST https://api.fabric.microsoft.com/v1/admin/domains/{domainId}/assignWorkspacesByCapacities
|
186
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/assignWorkspacesByCapacities"
|
187
|
+
|
188
|
+
body = {
|
189
|
+
"capacitiesIds": capacities_ids
|
190
|
+
}
|
191
|
+
|
192
|
+
for _ in range(10):
|
193
|
+
response = requests.post(url=url, headers=self.auth.get_headers(), json=body)
|
194
|
+
if response.status_code == 429:
|
195
|
+
print("Too many requests, waiting 10 seconds")
|
196
|
+
sleep(10)
|
197
|
+
continue
|
198
|
+
if response.status_code == 202 and wait_for_completion:
|
199
|
+
check_long_running_operation(response.headers, self.auth)
|
200
|
+
if response.status_code not in (202, 429):
|
201
|
+
print(response.status_code)
|
202
|
+
print(response.text)
|
203
|
+
raise Exception(f"Error assigning workspaces by capacities: {response.text}")
|
204
|
+
break
|
205
|
+
|
206
|
+
return response.status_code
|
207
|
+
|
208
|
+
|
209
|
+
def assign_workspaces_by_ids(self, workspaces_ids):
|
210
|
+
|
211
|
+
"""Method to assign workspaces to the domain based on workspaces_ids
|
212
|
+
|
213
|
+
Args:
|
214
|
+
workspaces_ids (list): The list of workspace ids to assign workspaces
|
215
|
+
Returns:
|
216
|
+
int: The status code of the response
|
217
|
+
"""
|
218
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/assignWorkspaces"
|
219
|
+
body = {
|
220
|
+
"workspacesIds": workspaces_ids
|
221
|
+
}
|
222
|
+
|
223
|
+
for _ in range(10):
|
224
|
+
response = requests.post(url=url, headers=self.auth.get_headers(), json=body)
|
225
|
+
if response.status_code == 429:
|
226
|
+
print("Too many requests, waiting 10 seconds")
|
227
|
+
sleep(10)
|
228
|
+
continue
|
229
|
+
if response.status_code not in (200, 429):
|
230
|
+
print(response.status_code)
|
231
|
+
print(response.text)
|
232
|
+
raise Exception(f"Error assigning workspaces by ids: {response.text}")
|
233
|
+
break
|
234
|
+
|
235
|
+
return response.status_code
|
236
|
+
|
237
|
+
|
238
|
+
def assign_workspaces_by_principals(self, principals, wait_for_completion=True):
|
239
|
+
"""Method to assign workspaces to the domain based on principals
|
240
|
+
|
241
|
+
Args:
|
242
|
+
principals (list): The list of principals to assign workspaces
|
243
|
+
Returns:
|
244
|
+
int: The status code of the response
|
245
|
+
"""
|
246
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/assignWorkspacesByPrincipals"
|
247
|
+
body = {
|
248
|
+
"principals": principals
|
249
|
+
}
|
250
|
+
|
251
|
+
for _ in range(10):
|
252
|
+
response = requests.post(url=url, headers=self.auth.get_headers(), json=body)
|
253
|
+
if response.status_code == 429:
|
254
|
+
print("Too many requests, waiting 10 seconds")
|
255
|
+
sleep(10)
|
256
|
+
continue
|
257
|
+
if response.status_code == 202 and wait_for_completion:
|
258
|
+
check_long_running_operation(response.headers, self.auth)
|
259
|
+
if response.status_code not in (202, 429):
|
260
|
+
print(response.status_code)
|
261
|
+
print(response.text)
|
262
|
+
raise Exception(f"Error assigning workspaces by principals: {response.text}")
|
263
|
+
break
|
264
|
+
|
265
|
+
return response.status_code
|
266
|
+
|
267
|
+
# POST https://api.fabric.microsoft.com/v1/admin/domains/{domainId}/roleAssignments/bulkAssign
|
268
|
+
|
269
|
+
def role_assignments_bulk_assign(self, type, principals):
|
270
|
+
"""Method to bulk assign role assignments
|
271
|
+
|
272
|
+
Args:
|
273
|
+
type (str): The type of the role assignment
|
274
|
+
principals (list): The list of principals to assign the role
|
275
|
+
Returns:
|
276
|
+
int: The status code of the response
|
277
|
+
"""
|
278
|
+
|
279
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/roleAssignments/bulkAssign"
|
280
|
+
body = {
|
281
|
+
"type": type,
|
282
|
+
"principals": principals
|
283
|
+
}
|
284
|
+
|
285
|
+
for _ in range(10):
|
286
|
+
response = requests.post(url=url, headers=self.auth.get_headers(), json=body)
|
287
|
+
if response.status_code == 429:
|
288
|
+
print("Too many requests, waiting 10 seconds")
|
289
|
+
sleep(10)
|
290
|
+
continue
|
291
|
+
if response.status_code not in (200, 429):
|
292
|
+
print(response.status_code)
|
293
|
+
print(response.text)
|
294
|
+
raise Exception(f"Error bulk assigning role assignments: {response.text}")
|
295
|
+
break
|
296
|
+
|
297
|
+
return response.status_code
|
298
|
+
|
299
|
+
|
300
|
+
def role_assignments_bulk_unassign(self, type, principals):
|
301
|
+
"""Method to bulk unassign role assignments
|
302
|
+
|
303
|
+
Args:
|
304
|
+
type (str): The type of the role assignment
|
305
|
+
principals (list): The list of principals to unassign the role
|
306
|
+
Returns:
|
307
|
+
int: The status code of the response
|
308
|
+
"""
|
309
|
+
|
310
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/roleAssignments/bulkUnassign"
|
311
|
+
body = {
|
312
|
+
"type": type,
|
313
|
+
"principals": principals
|
314
|
+
}
|
315
|
+
|
316
|
+
for _ in range(10):
|
317
|
+
response = requests.post(url=url, headers=self.auth.get_headers(), json=body)
|
318
|
+
if response.status_code == 429:
|
319
|
+
print("Too many requests, waiting 10 seconds")
|
320
|
+
sleep(10)
|
321
|
+
continue
|
322
|
+
if response.status_code not in (200, 429):
|
323
|
+
print(response.status_code)
|
324
|
+
print(response.text)
|
325
|
+
raise Exception(f"Error bulk unassigning role assignments: {response.text}")
|
326
|
+
break
|
327
|
+
|
328
|
+
return response.status_code
|
329
|
+
|
330
|
+
|
331
|
+
def unassign_all_workspaces(self):
|
332
|
+
"""Method to unassign all workspaces from the domain
|
333
|
+
|
334
|
+
Returns:
|
335
|
+
int: The status code of the response
|
336
|
+
"""
|
337
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/unassignAllWorkspaces"
|
338
|
+
|
339
|
+
for _ in range(10):
|
340
|
+
response = requests.post(url=url, headers=self.auth.get_headers())
|
341
|
+
if response.status_code == 429:
|
342
|
+
print("Too many requests, waiting 10 seconds")
|
343
|
+
sleep(10)
|
344
|
+
continue
|
345
|
+
if response.status_code not in (200, 429):
|
346
|
+
print(response.status_code)
|
347
|
+
print(response.text)
|
348
|
+
raise Exception(f"Error unassigning all workspaces: {response.text}")
|
349
|
+
break
|
350
|
+
|
351
|
+
return response.status_code
|
352
|
+
|
353
|
+
def unassign_workspaces_by_ids(self, workspace_ids):
|
354
|
+
"""Method to unassign workspaces from the domain
|
355
|
+
|
356
|
+
Args:
|
357
|
+
workspace_ids (list): The list of workspace ids to unassign
|
358
|
+
Returns:
|
359
|
+
int: The status code of the response
|
360
|
+
"""
|
361
|
+
url = f"https://api.fabric.microsoft.com/v1/admin/domains/{self.id}/unassignWorkspaces"
|
362
|
+
body = {
|
363
|
+
"workspacesIds": workspace_ids
|
364
|
+
}
|
365
|
+
|
366
|
+
for _ in range(10):
|
367
|
+
response = requests.post(url=url, headers=self.auth.get_headers(), json=body)
|
368
|
+
if response.status_code == 429:
|
369
|
+
print("Too many requests, waiting 10 seconds")
|
370
|
+
sleep(10)
|
371
|
+
continue
|
372
|
+
if response.status_code not in (200, 429):
|
373
|
+
print(response.status_code)
|
374
|
+
print(response.text)
|
375
|
+
raise Exception(f"Error unassigning workspaces by ids: {response.text}")
|
376
|
+
break
|
377
|
+
|
378
|
+
return response.status_code
|
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
|
|
@@ -34,8 +35,17 @@ class Item:
|
|
34
35
|
}
|
35
36
|
return json.dumps(dict_, indent=2)
|
36
37
|
|
38
|
+
def __repr__(self) -> str:
|
39
|
+
return self.__str__()
|
40
|
+
|
37
41
|
def from_dict(item_dict, auth):
|
38
42
|
"""Create Item object from dictionary"""
|
43
|
+
|
44
|
+
if item_dict['type'] == "Lakehouse":
|
45
|
+
from msfabricpysdkcore.lakehouse import Lakehouse
|
46
|
+
return Lakehouse(id=item_dict['id'], display_name=item_dict['displayName'], type=item_dict['type'], workspace_id=item_dict['workspaceId'],
|
47
|
+
properties=item_dict.get('properties', None),
|
48
|
+
definition=item_dict.get('definition', None), description=item_dict.get('description', ""), auth=auth)
|
39
49
|
return Item(id=item_dict['id'], display_name=item_dict['displayName'], type=item_dict['type'], workspace_id=item_dict['workspaceId'],
|
40
50
|
properties=item_dict.get('properties', None),
|
41
51
|
definition=item_dict.get('definition', None), description=item_dict.get('description', ""), auth=auth)
|
@@ -52,6 +62,7 @@ class Item:
|
|
52
62
|
if response.status_code not in (200, 429):
|
53
63
|
print(response.status_code)
|
54
64
|
print(response.text)
|
65
|
+
print(self)
|
55
66
|
raise Exception(f"Error deleting item: {response.text}")
|
56
67
|
break
|
57
68
|
|
@@ -243,4 +254,11 @@ class Item:
|
|
243
254
|
def cancel_item_job_instance(self, job_instance_id):
|
244
255
|
"""Cancel a job instance ofjob the item"""
|
245
256
|
return self.get_item_job_instance(job_instance_id=job_instance_id).cancel()
|
246
|
-
|
257
|
+
|
258
|
+
def list_tables(self):
|
259
|
+
raise NotImplementedError("List tables only works on Lakehouse Items")
|
260
|
+
|
261
|
+
def load_table(self, table_name, path_type, relative_path,
|
262
|
+
file_extension = None, format_options = None,
|
263
|
+
mode = None, recursive = None, wait_for_completion = True):
|
264
|
+
raise NotImplementedError("Load table only works on Lakehouse Items")
|
@@ -39,6 +39,9 @@ class JobInstance:
|
|
39
39
|
}
|
40
40
|
return json.dumps(dict_, indent=2)
|
41
41
|
|
42
|
+
def __repr__(self) -> str:
|
43
|
+
return self.__str__()
|
44
|
+
|
42
45
|
def from_dict(job_dict, auth):
|
43
46
|
"""Create JobInstance object from dictionary"""
|
44
47
|
return JobInstance(id=job_dict['id'], item_id=job_dict['itemId'], workspace_id=job_dict['workspaceId'],
|
@@ -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
|
+
|
@@ -27,6 +27,9 @@ class OneLakeShortcut:
|
|
27
27
|
}
|
28
28
|
return json.dumps(dict_, indent=2)
|
29
29
|
|
30
|
+
def __repr__(self) -> str:
|
31
|
+
return self.__str__()
|
32
|
+
|
30
33
|
def from_dict(short_dict, auth):
|
31
34
|
"""Create OneLakeShortCut object from dictionary"""
|
32
35
|
return OneLakeShortcut(name=short_dict['name'],
|
File without changes
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import unittest
|
2
|
+
from dotenv import load_dotenv
|
3
|
+
from msfabricpysdkcore import FabricClientAdmin
|
4
|
+
|
5
|
+
load_dotenv()
|
6
|
+
|
7
|
+
class TestFabricClientCore(unittest.TestCase):
|
8
|
+
|
9
|
+
def __init__(self, *args, **kwargs):
|
10
|
+
super(TestFabricClientCore, self).__init__(*args, **kwargs)
|
11
|
+
|
12
|
+
|
13
|
+
def test_domains(self):
|
14
|
+
fca = FabricClientAdmin()
|
15
|
+
|
16
|
+
user_id = 'b4f4e299-e6e1-4667-886c-57e4a8dde1c2'
|
17
|
+
|
18
|
+
# List workspaces
|
19
|
+
ws = fca.list_workspaces(name="testworkspace")[0]
|
20
|
+
|
21
|
+
self.assertEqual(ws.name, "testworkspace")
|
22
|
+
|
23
|
+
# Get workspace
|
24
|
+
ws_clone = fca.get_workspace(workspace_id=ws.id)
|
25
|
+
|
26
|
+
self.assertEqual(ws.id, ws_clone.id)
|
27
|
+
|
28
|
+
# Get workspace access details
|
29
|
+
|
30
|
+
ws_access = fca.get_workspace_access_details(ws.id)
|
31
|
+
principials = ws_access["accessDetails"]
|
32
|
+
principials_ids = [p["principal"]["id"] for p in principials]
|
33
|
+
self.assertIn(user_id, principials_ids)
|
34
|
+
|
35
|
+
# Get access entities
|
36
|
+
|
37
|
+
access_entities = fca.get_access_entities(user_id, type="Notebook")
|
38
|
+
self.assertGreater(len(access_entities), 0)
|
39
|
+
|
40
|
+
# Get tenant settings
|
41
|
+
|
42
|
+
tenant_settings = fca.get_tenant_settings()
|
43
|
+
self.assertGreater(len(tenant_settings["tenantSettings"]), 0)
|
44
|
+
|
45
|
+
# Get capacity tenant settings overrides
|
46
|
+
|
47
|
+
overrides = fca.get_capacities_tenant_settings_overrides()
|
48
|
+
self.assertGreater(len(overrides), -1)
|
49
|
+
|
50
|
+
# List items
|
51
|
+
|
52
|
+
item_list = fca.list_items(workspace_id=ws.id)
|
53
|
+
self.assertGreater(len(item_list), 0)
|
54
|
+
|
55
|
+
# Get item
|
56
|
+
|
57
|
+
item = fca.get_item(workspace_id=ws.id, item_id=item_list[0].id)
|
58
|
+
self.assertEqual(item.id, item_list[0].id)
|
59
|
+
|
60
|
+
# Get item access details
|
61
|
+
|
62
|
+
item_access = fca.get_item_access_details(workspace_id=ws.id, item_id=item_list[0].id)
|
63
|
+
principials = item_access["accessDetails"]
|
64
|
+
|
65
|
+
principials_ids = [p["principal"]["id"] for p in principials]
|
66
|
+
|
67
|
+
self.assertIn(user_id, principials_ids)
|
68
|
+
|
69
|
+
|