msdev-kit 0.1.0__tar.gz → 0.1.1__tar.gz
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.
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/PKG-INFO +197 -56
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/README.md +196 -55
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/__init__.py +1 -1
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/pyproject.toml +1 -1
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/auth.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/__init__.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/admin.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/capacity.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/database.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/dataflow.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/dataset.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/kql.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/notebook.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/operations.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/pipeline.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/report.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/utilities.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/workspace.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/graph/__init__.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/graph/client.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/sharepoint/__init__.py +0 -0
- {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/sharepoint/client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: msdev-kit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Microsoft developer toolkit: Fabric, MS Graph, and SharePoint
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Bernardo Rufino
|
|
@@ -32,21 +32,35 @@ Description-Content-Type: text/markdown
|
|
|
32
32
|
|
|
33
33
|
Microsoft developer toolkit for Python: Fabric/Power BI, MS Graph (Entra), and SharePoint.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
[](https://pypi.org/project/msdev-kit/)
|
|
36
|
+
[](https://pypi.org/project/msdev-kit/)
|
|
37
|
+
[](https://opensource.org/licenses/MIT)
|
|
38
|
+
|
|
39
|
+
## Table of Contents
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
- [Installation](#installation)
|
|
42
|
+
- [Quick Start](#quick-start)
|
|
43
|
+
- [Authentication](#authentication)
|
|
44
|
+
- [Fabric & Power BI](#fabric--power-bi) — workspaces, datasets, reports, dataflows, pipelines, notebooks, and more
|
|
45
|
+
- [MS Graph (Entra)](#ms-graph-entra) — users and groups via MS Graph API
|
|
46
|
+
- [SharePoint](#sharepoint) — files and folders via MS Graph API
|
|
47
|
+
- [Limitations](#limitations)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
38
52
|
|
|
39
53
|
```shell
|
|
40
54
|
pip install msdev-kit
|
|
41
55
|
```
|
|
42
56
|
|
|
43
|
-
|
|
57
|
+
Or install from GitHub:
|
|
44
58
|
|
|
45
59
|
```shell
|
|
46
60
|
pip install git+https://github.com/Bernardo-Rufino/msdev-kit.git
|
|
47
61
|
```
|
|
48
62
|
|
|
49
|
-
|
|
63
|
+
For local development:
|
|
50
64
|
|
|
51
65
|
```shell
|
|
52
66
|
git clone https://github.com/Bernardo-Rufino/msdev-kit.git
|
|
@@ -54,32 +68,64 @@ cd msdev-kit
|
|
|
54
68
|
pip install -e .
|
|
55
69
|
```
|
|
56
70
|
|
|
71
|
+
**Requirements:** Python >= 3.10, an Azure app registration with a client ID and client secret.
|
|
72
|
+
|
|
57
73
|
---
|
|
58
74
|
|
|
59
|
-
##
|
|
75
|
+
## Quick Start
|
|
60
76
|
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
```python
|
|
78
|
+
from msdev_kit import Auth
|
|
79
|
+
from msdev_kit.fabric import Workspace
|
|
80
|
+
from msdev_kit.graph import GraphClient
|
|
81
|
+
from msdev_kit.sharepoint import SharePointClient
|
|
63
82
|
|
|
64
|
-
|
|
83
|
+
# authenticate
|
|
84
|
+
auth = Auth(tenant_id="...", client_id="...", client_secret="...")
|
|
65
85
|
|
|
66
|
-
|
|
86
|
+
# fabric: list workspaces
|
|
87
|
+
ws = Workspace(auth.get_token('fabric'))
|
|
88
|
+
workspaces = ws.list_workspaces_for_user()
|
|
67
89
|
|
|
68
|
-
|
|
90
|
+
# graph: look up a user
|
|
91
|
+
graph = GraphClient(auth)
|
|
92
|
+
user_id = graph.get_user_id('user@company.com')
|
|
93
|
+
|
|
94
|
+
# sharepoint: download a file
|
|
95
|
+
sp = SharePointClient(auth, sp_hostname='company', sp_site_path='sites/DataTeam')
|
|
96
|
+
sp.download_file('/Reports/monthly.xlsx', local_dir='./downloads')
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Authentication
|
|
102
|
+
|
|
103
|
+
All classes use a shared `Auth` object. You can use different service principals for different services — instantiate one `Auth` per SPN:
|
|
69
104
|
|
|
70
105
|
```python
|
|
71
106
|
from msdev_kit import Auth
|
|
72
107
|
|
|
73
108
|
# service principal auth
|
|
74
|
-
|
|
109
|
+
fabric_auth = Auth(tenant_id="...", client_id="spn-a", client_secret="...")
|
|
110
|
+
graph_auth = Auth(tenant_id="...", client_id="spn-b", client_secret="...")
|
|
111
|
+
```
|
|
75
112
|
|
|
76
|
-
|
|
77
|
-
token = auth.get_token('fabric') # Fabric API
|
|
78
|
-
token = auth.get_token('graph') # MS Graph API
|
|
79
|
-
token = auth.get_token('azure') # Azure Management API
|
|
113
|
+
### Supported scopes
|
|
80
114
|
|
|
81
|
-
|
|
82
|
-
|
|
115
|
+
| Service | Scope | Usage |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `pbi` (default) | Power BI API | `auth.get_token()` or `auth.get_token('pbi')` |
|
|
118
|
+
| `fabric` | Fabric API | `auth.get_token('fabric')` |
|
|
119
|
+
| `graph` | MS Graph API | `auth.get_token('graph')` |
|
|
120
|
+
| `azure` | Azure Management API | `auth.get_token('azure')` |
|
|
121
|
+
|
|
122
|
+
### Interactive user auth
|
|
123
|
+
|
|
124
|
+
For scenarios requiring user context (e.g., RLS-enabled datasets):
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
token = auth.get_token_for_user('pbi') # opens browser for login
|
|
128
|
+
token = auth.get_token_for_user('fabric')
|
|
83
129
|
```
|
|
84
130
|
|
|
85
131
|
### Credentials
|
|
@@ -94,24 +140,34 @@ CLIENT_SECRET='<YOUR_CLIENT_SECRET>'
|
|
|
94
140
|
|
|
95
141
|
---
|
|
96
142
|
|
|
97
|
-
##
|
|
98
|
-
|
|
99
|
-
### `msdev_kit.fabric` — Fabric & Power BI
|
|
100
|
-
|
|
101
|
-
All existing Fabric/Power BI classes, accessed via the `fabric` sub-package:
|
|
143
|
+
## Fabric & Power BI
|
|
102
144
|
|
|
103
145
|
```python
|
|
104
|
-
from msdev_kit.fabric import Workspace, Dataset, Report, Dataflow, Pipeline
|
|
105
146
|
from msdev_kit import Auth
|
|
147
|
+
from msdev_kit.fabric import Workspace, Dataset, Report, Dataflow, Pipeline
|
|
106
148
|
|
|
107
149
|
auth = Auth(tenant_id, client_id, client_secret)
|
|
108
|
-
ws = Workspace(auth.get_token('fabric'))
|
|
109
150
|
```
|
|
110
151
|
|
|
111
|
-
|
|
152
|
+
Fabric classes take a token string — call `auth.get_token('fabric')` or `auth.get_token('pbi')` depending on the API.
|
|
153
|
+
|
|
154
|
+
- [Workspace](#workspace) — workspaces, users, permissions
|
|
155
|
+
- [Dataset](#dataset) — semantic models, DAX queries, permissions
|
|
156
|
+
- [Report](#report) — metadata, definitions, visuals, measures
|
|
157
|
+
- [Dataflow](#dataflow) — Gen1, Gen2, Gen2 CI/CD management
|
|
158
|
+
- [Pipeline](#pipeline) — Data Pipeline management
|
|
159
|
+
- [Other modules](#other-modules) — Capacity, Admin, KQL, Notebook, Database
|
|
160
|
+
|
|
161
|
+
### Workspace
|
|
112
162
|
|
|
113
163
|
Manage Power BI workspaces, users, and permissions.
|
|
114
164
|
|
|
165
|
+
```python
|
|
166
|
+
ws = Workspace(auth.get_token('pbi'))
|
|
167
|
+
workspaces = ws.list_workspaces_for_user()
|
|
168
|
+
ws.add_user('user@company.com', workspace_id, 'Member', 'User')
|
|
169
|
+
```
|
|
170
|
+
|
|
115
171
|
| Method | Description |
|
|
116
172
|
|---|---|
|
|
117
173
|
| `list_workspaces_for_user(...)` | List all workspaces the user has access to, with optional filters. |
|
|
@@ -123,16 +179,21 @@ Manage Power BI workspaces, users, and permissions.
|
|
|
123
179
|
| `remove_user(user_principal_name, workspace_id)` | Remove a user from a workspace. |
|
|
124
180
|
| `batch_update_user(user, workspaces_list)` | Batch update a user across multiple workspaces. |
|
|
125
181
|
|
|
126
|
-
|
|
182
|
+
### Dataset
|
|
127
183
|
|
|
128
184
|
Manage datasets (semantic models), permissions, and execute DAX queries.
|
|
129
185
|
|
|
186
|
+
```python
|
|
187
|
+
ds = Dataset(auth.get_token('pbi'))
|
|
188
|
+
result = ds.execute_query(workspace_id, dataset_id, "EVALUATE Sales")
|
|
189
|
+
```
|
|
190
|
+
|
|
130
191
|
| Method | Description |
|
|
131
192
|
|---|---|
|
|
132
193
|
| `list_datasets(workspace_id)` | List all datasets in a workspace. |
|
|
133
194
|
| `get_dataset_details(workspace_id, dataset_id)` | Get details of a specific dataset. |
|
|
134
|
-
| `get_dataset_name(workspace_id, dataset_id)` | Resolve the display name of a dataset. Tries
|
|
135
|
-
| `execute_query(workspace_id, dataset_id, query)` | Execute a DAX query
|
|
195
|
+
| `get_dataset_name(workspace_id, dataset_id)` | Resolve the display name of a dataset. Tries PBI API first, falls back to Fabric semantic models API. |
|
|
196
|
+
| `execute_query(workspace_id, dataset_id, query)` | Execute a DAX query. Runs a COUNTROWS pre-check to detect truncation. |
|
|
136
197
|
| `list_users(workspace_id, dataset_id)` | List users with access to a dataset. |
|
|
137
198
|
| `add_user(user_principal_name, workspace_id, dataset_id, access_right)` | Grant a user access to a dataset. |
|
|
138
199
|
| `update_user(user_principal_name, workspace_id, dataset_id, access_right)` | Update a user's access to a dataset. |
|
|
@@ -140,29 +201,46 @@ Manage datasets (semantic models), permissions, and execute DAX queries.
|
|
|
140
201
|
| `list_dataset_related_reports(workspace_id, dataset_id)` | List all reports linked to a dataset. |
|
|
141
202
|
| `export_dataset_related_reports(workspace_id, dataset_id)` | Export all reports linked to a dataset as `.pbix` files. |
|
|
142
203
|
|
|
143
|
-
|
|
204
|
+
### Report
|
|
144
205
|
|
|
145
206
|
Retrieve report metadata, definitions, visuals, and report-level measures.
|
|
146
207
|
|
|
208
|
+
```python
|
|
209
|
+
rpt = Report(auth.get_token('pbi'))
|
|
210
|
+
pages = rpt.list_report_pages(workspace_id, report_id)
|
|
211
|
+
```
|
|
212
|
+
|
|
147
213
|
| Method | Description |
|
|
148
214
|
|---|---|
|
|
149
215
|
| `list_reports(workspace_id)` | List all reports in a workspace. |
|
|
150
216
|
| `get_report_metadata(workspace_id, report_id)` | Get metadata for a specific report. |
|
|
151
217
|
| `get_report_name(workspace_id, report_id)` | Get a report's display name. |
|
|
152
218
|
| `list_report_pages(workspace_id, report_id)` | List all pages in a report. |
|
|
153
|
-
| `get_report_json_pages_and_visuals(json_data, workspace_id, report_id)` | Parse a PBIR-Legacy report JSON and extract pages
|
|
219
|
+
| `get_report_json_pages_and_visuals(json_data, workspace_id, report_id)` | Parse a PBIR-Legacy report JSON and extract pages/visuals into a DataFrame. |
|
|
154
220
|
| `get_legacy_report_json(workspace_id, report_id, operations)` | Get and decode the full report definition for PBIR-Legacy reports. |
|
|
155
221
|
| `export_report(workspace_id, report_id, ...)` | Export a report as a `.pbix` file. |
|
|
156
|
-
| `get_report_measures(workspace_id, report_id, operations)` | Extract report-level measures and generate a DAX Query View script.
|
|
157
|
-
| `rebind_report(workspace_id, report_id, new_dataset_id,
|
|
222
|
+
| `get_report_measures(workspace_id, report_id, operations)` | Extract report-level measures and generate a DAX Query View script. |
|
|
223
|
+
| `rebind_report(workspace_id, report_id, new_dataset_id, ...)` | Rebind a report to a new dataset and migrate Read access. |
|
|
158
224
|
|
|
159
|
-
|
|
225
|
+
### Dataflow
|
|
160
226
|
|
|
161
227
|
Manage Power BI and Fabric dataflows, including Gen1, Gen2, and Gen2 CI/CD.
|
|
162
228
|
|
|
229
|
+
```python
|
|
230
|
+
df = Dataflow(auth.get_token('fabric'))
|
|
231
|
+
|
|
232
|
+
# upgrade Gen1 to Gen2 CI/CD
|
|
233
|
+
result = df.upgrade_to_gen2_cicd(
|
|
234
|
+
workspace_id='<workspace_id>',
|
|
235
|
+
dataflow_id='<gen1_dataflow_id>',
|
|
236
|
+
display_name='my_dataflow_cicd',
|
|
237
|
+
source_type='gen1'
|
|
238
|
+
)
|
|
239
|
+
```
|
|
240
|
+
|
|
163
241
|
| Method | Description |
|
|
164
242
|
|---|---|
|
|
165
|
-
| `list_dataflows(workspace_id)` | List all dataflows
|
|
243
|
+
| `list_dataflows(workspace_id)` | List all dataflows (Gen1, Gen2, Gen2 CI/CD), merged and deduplicated. |
|
|
166
244
|
| `get_dataflow_details(workspace_id, dataflow_id)` | Get details of a specific dataflow. |
|
|
167
245
|
| `get_dataflow_name(workspace_id, dataflow_id)` | Resolve the display name of a dataflow. |
|
|
168
246
|
| `create_dataflow(workspace_id, dataflow_content)` | Create a new Power BI dataflow. |
|
|
@@ -171,39 +249,83 @@ Manage Power BI and Fabric dataflows, including Gen1, Gen2, and Gen2 CI/CD.
|
|
|
171
249
|
| `get_dataflow_gen2_definition(workspace_id, dataflow_id)` | Get the definition of a Dataflow Gen2 CI/CD item. |
|
|
172
250
|
| `create_dataflow_gen2_from_definition(workspace_id, display_name, definition)` | Create a Dataflow Gen2 CI/CD from a definition. |
|
|
173
251
|
| `update_dataflow_gen2_from_definition(workspace_id, dataflow_id, display_name, definition)` | Update an existing Dataflow Gen2 CI/CD definition. |
|
|
174
|
-
| `get_data_destinations(workspace_id, dataflow_id)` | Get
|
|
175
|
-
| `change_data_destination(workspace_id, dataflow_id, destination_type, ...)` | Change
|
|
176
|
-
| `create_dataflow_with_new_destination(workspace_id, dataflow_id, ...)` | Create a new Gen2 CI/CD dataflow
|
|
252
|
+
| `get_data_destinations(workspace_id, dataflow_id)` | Get data destination details for each table in a dataflow. |
|
|
253
|
+
| `change_data_destination(workspace_id, dataflow_id, destination_type, ...)` | Change data destination (Lakehouse/Warehouse). Modes: `preview`, `replace`, `create`. |
|
|
254
|
+
| `create_dataflow_with_new_destination(workspace_id, dataflow_id, ...)` | Create a new Gen2 CI/CD dataflow with a different data destination. |
|
|
177
255
|
| `upgrade_to_gen2_cicd(...)` | Upgrade a Gen1 or Gen2 (standard) dataflow to Gen2 CI/CD. |
|
|
178
256
|
|
|
179
|
-
|
|
257
|
+
### Pipeline
|
|
180
258
|
|
|
181
259
|
Manage Fabric Data Pipelines.
|
|
182
260
|
|
|
261
|
+
```python
|
|
262
|
+
pipe = Pipeline(auth.get_token('fabric'))
|
|
263
|
+
activities = pipe.get_pipeline_activities(workspace_id, 'My Pipeline')
|
|
264
|
+
```
|
|
265
|
+
|
|
183
266
|
| Method | Description |
|
|
184
267
|
|---|---|
|
|
185
268
|
| `list_pipelines(workspace_id)` | List all Fabric Data Pipelines in a workspace. |
|
|
186
269
|
| `get_pipeline(workspace_id, pipeline_id)` | Get the metadata of a specific pipeline. |
|
|
187
|
-
| `get_pipeline_definition(workspace_id, pipeline_id)` | Get the full definition of a
|
|
270
|
+
| `get_pipeline_definition(workspace_id, pipeline_id)` | Get the full definition of a pipeline. |
|
|
188
271
|
| `update_pipeline_definition(workspace_id, pipeline_id, definition)` | Update an existing pipeline definition. |
|
|
189
|
-
| `get_pipeline_activities(workspace_id, pipeline_id_or_name)` | Get
|
|
190
|
-
| `find_pipelines_by_dataflow(workspace_id, dataflow_id_or_name)` | Find
|
|
191
|
-
| `replace_dataflow_id_in_pipeline(workspace_id, pipeline_id,
|
|
272
|
+
| `get_pipeline_activities(workspace_id, pipeline_id_or_name)` | Get activities from a pipeline. Accepts ID or display name. |
|
|
273
|
+
| `find_pipelines_by_dataflow(workspace_id, dataflow_id_or_name)` | Find pipelines that reference a specific dataflow. |
|
|
274
|
+
| `replace_dataflow_id_in_pipeline(workspace_id, pipeline_id, old_id, new_id)` | Replace a dataflow ID in all RefreshDataflow activities. |
|
|
192
275
|
|
|
193
|
-
|
|
276
|
+
<details>
|
|
277
|
+
<summary><strong>Example: replacing a dataflow destination and updating pipelines</strong></summary>
|
|
194
278
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
279
|
+
When `change_data_destination(mode='replace')` is used on a standard Gen2 dataflow, the original is deleted and a new CI/CD dataflow is created with a new ID. Pipelines referencing the old ID must be updated:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
from msdev_kit import Auth
|
|
283
|
+
from msdev_kit.fabric import Dataflow, Pipeline
|
|
284
|
+
|
|
285
|
+
auth = Auth(tenant_id, client_id, client_secret)
|
|
286
|
+
dataflow = Dataflow(auth.get_token('pbi'))
|
|
287
|
+
pipeline = Pipeline(auth.get_token('fabric'))
|
|
288
|
+
|
|
289
|
+
workspace_id = '<workspace_id>'
|
|
290
|
+
old_dataflow_id = '<dataflow_id>'
|
|
291
|
+
|
|
292
|
+
# 1. replace dataflow destination (creates new CI/CD, deletes original)
|
|
293
|
+
result = dataflow.change_data_destination(
|
|
294
|
+
workspace_id=workspace_id,
|
|
295
|
+
dataflow_id=old_dataflow_id,
|
|
296
|
+
destination_type='Warehouse',
|
|
297
|
+
destination_workspace_id=workspace_id,
|
|
298
|
+
destination_item_id='<warehouse_id>',
|
|
299
|
+
mode='replace'
|
|
300
|
+
)
|
|
301
|
+
new_dataflow_id = result['content']['id']
|
|
302
|
+
|
|
303
|
+
# 2. find pipelines referencing the old dataflow ID
|
|
304
|
+
matches = pipeline.find_pipelines_by_dataflow(workspace_id, old_dataflow_id)
|
|
305
|
+
|
|
306
|
+
# 3. update each pipeline to use the new ID
|
|
307
|
+
for m in matches['content']:
|
|
308
|
+
pipeline.replace_dataflow_id_in_pipeline(
|
|
309
|
+
workspace_id, m['pipeline_id'], old_dataflow_id, new_dataflow_id
|
|
310
|
+
)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
</details>
|
|
314
|
+
|
|
315
|
+
### Other modules
|
|
316
|
+
|
|
317
|
+
| Module | Class | Description |
|
|
318
|
+
|---|---|---|
|
|
319
|
+
| Capacity | `Capacity` | Monitor and manage Power BI and Fabric capacities. |
|
|
320
|
+
| Operations | `Operations` | Track long-running Fabric API operations. |
|
|
321
|
+
| Admin | `Admin` | Power BI Admin API operations. |
|
|
322
|
+
| KQL | `KQLDatabase` | Query Kusto (KQL) databases in Microsoft Fabric. |
|
|
323
|
+
| Notebook | `Notebook` | Manage Fabric notebooks (list, get metadata). |
|
|
324
|
+
| Database | `Database` | Query and write to SQL databases (Lakehouse, Warehouse) via ODBC. |
|
|
203
325
|
|
|
204
326
|
---
|
|
205
327
|
|
|
206
|
-
|
|
328
|
+
## MS Graph (Entra)
|
|
207
329
|
|
|
208
330
|
Manage Entra ID (Azure AD) users and groups via the MS Graph API.
|
|
209
331
|
|
|
@@ -214,8 +336,12 @@ from msdev_kit.graph import GraphClient
|
|
|
214
336
|
auth = Auth(tenant_id, client_id, client_secret)
|
|
215
337
|
graph = GraphClient(auth)
|
|
216
338
|
|
|
339
|
+
# look up a user and add them to a group
|
|
217
340
|
user_id = graph.get_user_id('user@company.com')
|
|
218
341
|
group_id = graph.get_group_id('Data Team')
|
|
342
|
+
graph.add_group_member(group_id, user_id)
|
|
343
|
+
|
|
344
|
+
# list all members of a group
|
|
219
345
|
members = graph.list_group_members(group_id)
|
|
220
346
|
```
|
|
221
347
|
|
|
@@ -229,7 +355,7 @@ members = graph.list_group_members(group_id)
|
|
|
229
355
|
|
|
230
356
|
---
|
|
231
357
|
|
|
232
|
-
|
|
358
|
+
## SharePoint
|
|
233
359
|
|
|
234
360
|
Manage SharePoint files and folders via MS Graph API (no ACS/Office365 dependency).
|
|
235
361
|
|
|
@@ -240,9 +366,15 @@ from msdev_kit.sharepoint import SharePointClient
|
|
|
240
366
|
auth = Auth(tenant_id, client_id, client_secret)
|
|
241
367
|
sp = SharePointClient(auth, sp_hostname='company', sp_site_path='sites/DataTeam')
|
|
242
368
|
|
|
369
|
+
# download a file
|
|
243
370
|
sp.download_file('/Reports/monthly.xlsx', local_dir='./downloads')
|
|
371
|
+
|
|
372
|
+
# upload a file (from path or bytes)
|
|
244
373
|
sp.upload_file('/Reports/updated.xlsx', source='./local/updated.xlsx')
|
|
245
|
-
sp.
|
|
374
|
+
sp.upload_file('/Reports/data.csv', source=csv_bytes, content_type='text/csv')
|
|
375
|
+
|
|
376
|
+
# create nested folders
|
|
377
|
+
sp.create_folder('/Reports/2026/Q1')
|
|
246
378
|
```
|
|
247
379
|
|
|
248
380
|
| Method | Description |
|
|
@@ -251,7 +383,16 @@ sp.create_folder('/Reports/2026')
|
|
|
251
383
|
| `upload_file(remote_path, source, content_type?)` | Upload/overwrite a file. `source` is a local file path (str) or raw bytes. |
|
|
252
384
|
| `create_folder(folder_path)` | Create a folder and all intermediate folders. |
|
|
253
385
|
|
|
254
|
-
Hostname and site path inputs are normalized automatically
|
|
386
|
+
Hostname and site path inputs are normalized automatically:
|
|
387
|
+
|
|
388
|
+
| Input | Normalized to |
|
|
389
|
+
|---|---|
|
|
390
|
+
| `company` | `company.sharepoint.com` |
|
|
391
|
+
| `company.sharepoint.com` | `company.sharepoint.com` |
|
|
392
|
+
| `https://company.sharepoint.com` | `company.sharepoint.com` |
|
|
393
|
+
| `DataTeam` | `sites/DataTeam` |
|
|
394
|
+
| `sites/DataTeam` | `sites/DataTeam` |
|
|
395
|
+
| `/sites/DataTeam` | `sites/DataTeam` |
|
|
255
396
|
|
|
256
397
|
---
|
|
257
398
|
|
|
@@ -2,21 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
Microsoft developer toolkit for Python: Fabric/Power BI, MS Graph (Entra), and SharePoint.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://pypi.org/project/msdev-kit/)
|
|
6
|
+
[](https://pypi.org/project/msdev-kit/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [Authentication](#authentication)
|
|
14
|
+
- [Fabric & Power BI](#fabric--power-bi) — workspaces, datasets, reports, dataflows, pipelines, notebooks, and more
|
|
15
|
+
- [MS Graph (Entra)](#ms-graph-entra) — users and groups via MS Graph API
|
|
16
|
+
- [SharePoint](#sharepoint) — files and folders via MS Graph API
|
|
17
|
+
- [Limitations](#limitations)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
8
22
|
|
|
9
23
|
```shell
|
|
10
24
|
pip install msdev-kit
|
|
11
25
|
```
|
|
12
26
|
|
|
13
|
-
|
|
27
|
+
Or install from GitHub:
|
|
14
28
|
|
|
15
29
|
```shell
|
|
16
30
|
pip install git+https://github.com/Bernardo-Rufino/msdev-kit.git
|
|
17
31
|
```
|
|
18
32
|
|
|
19
|
-
|
|
33
|
+
For local development:
|
|
20
34
|
|
|
21
35
|
```shell
|
|
22
36
|
git clone https://github.com/Bernardo-Rufino/msdev-kit.git
|
|
@@ -24,32 +38,64 @@ cd msdev-kit
|
|
|
24
38
|
pip install -e .
|
|
25
39
|
```
|
|
26
40
|
|
|
41
|
+
**Requirements:** Python >= 3.10, an Azure app registration with a client ID and client secret.
|
|
42
|
+
|
|
27
43
|
---
|
|
28
44
|
|
|
29
|
-
##
|
|
45
|
+
## Quick Start
|
|
30
46
|
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
```python
|
|
48
|
+
from msdev_kit import Auth
|
|
49
|
+
from msdev_kit.fabric import Workspace
|
|
50
|
+
from msdev_kit.graph import GraphClient
|
|
51
|
+
from msdev_kit.sharepoint import SharePointClient
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
# authenticate
|
|
54
|
+
auth = Auth(tenant_id="...", client_id="...", client_secret="...")
|
|
35
55
|
|
|
36
|
-
|
|
56
|
+
# fabric: list workspaces
|
|
57
|
+
ws = Workspace(auth.get_token('fabric'))
|
|
58
|
+
workspaces = ws.list_workspaces_for_user()
|
|
37
59
|
|
|
38
|
-
|
|
60
|
+
# graph: look up a user
|
|
61
|
+
graph = GraphClient(auth)
|
|
62
|
+
user_id = graph.get_user_id('user@company.com')
|
|
63
|
+
|
|
64
|
+
# sharepoint: download a file
|
|
65
|
+
sp = SharePointClient(auth, sp_hostname='company', sp_site_path='sites/DataTeam')
|
|
66
|
+
sp.download_file('/Reports/monthly.xlsx', local_dir='./downloads')
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Authentication
|
|
72
|
+
|
|
73
|
+
All classes use a shared `Auth` object. You can use different service principals for different services — instantiate one `Auth` per SPN:
|
|
39
74
|
|
|
40
75
|
```python
|
|
41
76
|
from msdev_kit import Auth
|
|
42
77
|
|
|
43
78
|
# service principal auth
|
|
44
|
-
|
|
79
|
+
fabric_auth = Auth(tenant_id="...", client_id="spn-a", client_secret="...")
|
|
80
|
+
graph_auth = Auth(tenant_id="...", client_id="spn-b", client_secret="...")
|
|
81
|
+
```
|
|
45
82
|
|
|
46
|
-
|
|
47
|
-
token = auth.get_token('fabric') # Fabric API
|
|
48
|
-
token = auth.get_token('graph') # MS Graph API
|
|
49
|
-
token = auth.get_token('azure') # Azure Management API
|
|
83
|
+
### Supported scopes
|
|
50
84
|
|
|
51
|
-
|
|
52
|
-
|
|
85
|
+
| Service | Scope | Usage |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `pbi` (default) | Power BI API | `auth.get_token()` or `auth.get_token('pbi')` |
|
|
88
|
+
| `fabric` | Fabric API | `auth.get_token('fabric')` |
|
|
89
|
+
| `graph` | MS Graph API | `auth.get_token('graph')` |
|
|
90
|
+
| `azure` | Azure Management API | `auth.get_token('azure')` |
|
|
91
|
+
|
|
92
|
+
### Interactive user auth
|
|
93
|
+
|
|
94
|
+
For scenarios requiring user context (e.g., RLS-enabled datasets):
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
token = auth.get_token_for_user('pbi') # opens browser for login
|
|
98
|
+
token = auth.get_token_for_user('fabric')
|
|
53
99
|
```
|
|
54
100
|
|
|
55
101
|
### Credentials
|
|
@@ -64,24 +110,34 @@ CLIENT_SECRET='<YOUR_CLIENT_SECRET>'
|
|
|
64
110
|
|
|
65
111
|
---
|
|
66
112
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
### `msdev_kit.fabric` — Fabric & Power BI
|
|
70
|
-
|
|
71
|
-
All existing Fabric/Power BI classes, accessed via the `fabric` sub-package:
|
|
113
|
+
## Fabric & Power BI
|
|
72
114
|
|
|
73
115
|
```python
|
|
74
|
-
from msdev_kit.fabric import Workspace, Dataset, Report, Dataflow, Pipeline
|
|
75
116
|
from msdev_kit import Auth
|
|
117
|
+
from msdev_kit.fabric import Workspace, Dataset, Report, Dataflow, Pipeline
|
|
76
118
|
|
|
77
119
|
auth = Auth(tenant_id, client_id, client_secret)
|
|
78
|
-
ws = Workspace(auth.get_token('fabric'))
|
|
79
120
|
```
|
|
80
121
|
|
|
81
|
-
|
|
122
|
+
Fabric classes take a token string — call `auth.get_token('fabric')` or `auth.get_token('pbi')` depending on the API.
|
|
123
|
+
|
|
124
|
+
- [Workspace](#workspace) — workspaces, users, permissions
|
|
125
|
+
- [Dataset](#dataset) — semantic models, DAX queries, permissions
|
|
126
|
+
- [Report](#report) — metadata, definitions, visuals, measures
|
|
127
|
+
- [Dataflow](#dataflow) — Gen1, Gen2, Gen2 CI/CD management
|
|
128
|
+
- [Pipeline](#pipeline) — Data Pipeline management
|
|
129
|
+
- [Other modules](#other-modules) — Capacity, Admin, KQL, Notebook, Database
|
|
130
|
+
|
|
131
|
+
### Workspace
|
|
82
132
|
|
|
83
133
|
Manage Power BI workspaces, users, and permissions.
|
|
84
134
|
|
|
135
|
+
```python
|
|
136
|
+
ws = Workspace(auth.get_token('pbi'))
|
|
137
|
+
workspaces = ws.list_workspaces_for_user()
|
|
138
|
+
ws.add_user('user@company.com', workspace_id, 'Member', 'User')
|
|
139
|
+
```
|
|
140
|
+
|
|
85
141
|
| Method | Description |
|
|
86
142
|
|---|---|
|
|
87
143
|
| `list_workspaces_for_user(...)` | List all workspaces the user has access to, with optional filters. |
|
|
@@ -93,16 +149,21 @@ Manage Power BI workspaces, users, and permissions.
|
|
|
93
149
|
| `remove_user(user_principal_name, workspace_id)` | Remove a user from a workspace. |
|
|
94
150
|
| `batch_update_user(user, workspaces_list)` | Batch update a user across multiple workspaces. |
|
|
95
151
|
|
|
96
|
-
|
|
152
|
+
### Dataset
|
|
97
153
|
|
|
98
154
|
Manage datasets (semantic models), permissions, and execute DAX queries.
|
|
99
155
|
|
|
156
|
+
```python
|
|
157
|
+
ds = Dataset(auth.get_token('pbi'))
|
|
158
|
+
result = ds.execute_query(workspace_id, dataset_id, "EVALUATE Sales")
|
|
159
|
+
```
|
|
160
|
+
|
|
100
161
|
| Method | Description |
|
|
101
162
|
|---|---|
|
|
102
163
|
| `list_datasets(workspace_id)` | List all datasets in a workspace. |
|
|
103
164
|
| `get_dataset_details(workspace_id, dataset_id)` | Get details of a specific dataset. |
|
|
104
|
-
| `get_dataset_name(workspace_id, dataset_id)` | Resolve the display name of a dataset. Tries
|
|
105
|
-
| `execute_query(workspace_id, dataset_id, query)` | Execute a DAX query
|
|
165
|
+
| `get_dataset_name(workspace_id, dataset_id)` | Resolve the display name of a dataset. Tries PBI API first, falls back to Fabric semantic models API. |
|
|
166
|
+
| `execute_query(workspace_id, dataset_id, query)` | Execute a DAX query. Runs a COUNTROWS pre-check to detect truncation. |
|
|
106
167
|
| `list_users(workspace_id, dataset_id)` | List users with access to a dataset. |
|
|
107
168
|
| `add_user(user_principal_name, workspace_id, dataset_id, access_right)` | Grant a user access to a dataset. |
|
|
108
169
|
| `update_user(user_principal_name, workspace_id, dataset_id, access_right)` | Update a user's access to a dataset. |
|
|
@@ -110,29 +171,46 @@ Manage datasets (semantic models), permissions, and execute DAX queries.
|
|
|
110
171
|
| `list_dataset_related_reports(workspace_id, dataset_id)` | List all reports linked to a dataset. |
|
|
111
172
|
| `export_dataset_related_reports(workspace_id, dataset_id)` | Export all reports linked to a dataset as `.pbix` files. |
|
|
112
173
|
|
|
113
|
-
|
|
174
|
+
### Report
|
|
114
175
|
|
|
115
176
|
Retrieve report metadata, definitions, visuals, and report-level measures.
|
|
116
177
|
|
|
178
|
+
```python
|
|
179
|
+
rpt = Report(auth.get_token('pbi'))
|
|
180
|
+
pages = rpt.list_report_pages(workspace_id, report_id)
|
|
181
|
+
```
|
|
182
|
+
|
|
117
183
|
| Method | Description |
|
|
118
184
|
|---|---|
|
|
119
185
|
| `list_reports(workspace_id)` | List all reports in a workspace. |
|
|
120
186
|
| `get_report_metadata(workspace_id, report_id)` | Get metadata for a specific report. |
|
|
121
187
|
| `get_report_name(workspace_id, report_id)` | Get a report's display name. |
|
|
122
188
|
| `list_report_pages(workspace_id, report_id)` | List all pages in a report. |
|
|
123
|
-
| `get_report_json_pages_and_visuals(json_data, workspace_id, report_id)` | Parse a PBIR-Legacy report JSON and extract pages
|
|
189
|
+
| `get_report_json_pages_and_visuals(json_data, workspace_id, report_id)` | Parse a PBIR-Legacy report JSON and extract pages/visuals into a DataFrame. |
|
|
124
190
|
| `get_legacy_report_json(workspace_id, report_id, operations)` | Get and decode the full report definition for PBIR-Legacy reports. |
|
|
125
191
|
| `export_report(workspace_id, report_id, ...)` | Export a report as a `.pbix` file. |
|
|
126
|
-
| `get_report_measures(workspace_id, report_id, operations)` | Extract report-level measures and generate a DAX Query View script.
|
|
127
|
-
| `rebind_report(workspace_id, report_id, new_dataset_id,
|
|
192
|
+
| `get_report_measures(workspace_id, report_id, operations)` | Extract report-level measures and generate a DAX Query View script. |
|
|
193
|
+
| `rebind_report(workspace_id, report_id, new_dataset_id, ...)` | Rebind a report to a new dataset and migrate Read access. |
|
|
128
194
|
|
|
129
|
-
|
|
195
|
+
### Dataflow
|
|
130
196
|
|
|
131
197
|
Manage Power BI and Fabric dataflows, including Gen1, Gen2, and Gen2 CI/CD.
|
|
132
198
|
|
|
199
|
+
```python
|
|
200
|
+
df = Dataflow(auth.get_token('fabric'))
|
|
201
|
+
|
|
202
|
+
# upgrade Gen1 to Gen2 CI/CD
|
|
203
|
+
result = df.upgrade_to_gen2_cicd(
|
|
204
|
+
workspace_id='<workspace_id>',
|
|
205
|
+
dataflow_id='<gen1_dataflow_id>',
|
|
206
|
+
display_name='my_dataflow_cicd',
|
|
207
|
+
source_type='gen1'
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
133
211
|
| Method | Description |
|
|
134
212
|
|---|---|
|
|
135
|
-
| `list_dataflows(workspace_id)` | List all dataflows
|
|
213
|
+
| `list_dataflows(workspace_id)` | List all dataflows (Gen1, Gen2, Gen2 CI/CD), merged and deduplicated. |
|
|
136
214
|
| `get_dataflow_details(workspace_id, dataflow_id)` | Get details of a specific dataflow. |
|
|
137
215
|
| `get_dataflow_name(workspace_id, dataflow_id)` | Resolve the display name of a dataflow. |
|
|
138
216
|
| `create_dataflow(workspace_id, dataflow_content)` | Create a new Power BI dataflow. |
|
|
@@ -141,39 +219,83 @@ Manage Power BI and Fabric dataflows, including Gen1, Gen2, and Gen2 CI/CD.
|
|
|
141
219
|
| `get_dataflow_gen2_definition(workspace_id, dataflow_id)` | Get the definition of a Dataflow Gen2 CI/CD item. |
|
|
142
220
|
| `create_dataflow_gen2_from_definition(workspace_id, display_name, definition)` | Create a Dataflow Gen2 CI/CD from a definition. |
|
|
143
221
|
| `update_dataflow_gen2_from_definition(workspace_id, dataflow_id, display_name, definition)` | Update an existing Dataflow Gen2 CI/CD definition. |
|
|
144
|
-
| `get_data_destinations(workspace_id, dataflow_id)` | Get
|
|
145
|
-
| `change_data_destination(workspace_id, dataflow_id, destination_type, ...)` | Change
|
|
146
|
-
| `create_dataflow_with_new_destination(workspace_id, dataflow_id, ...)` | Create a new Gen2 CI/CD dataflow
|
|
222
|
+
| `get_data_destinations(workspace_id, dataflow_id)` | Get data destination details for each table in a dataflow. |
|
|
223
|
+
| `change_data_destination(workspace_id, dataflow_id, destination_type, ...)` | Change data destination (Lakehouse/Warehouse). Modes: `preview`, `replace`, `create`. |
|
|
224
|
+
| `create_dataflow_with_new_destination(workspace_id, dataflow_id, ...)` | Create a new Gen2 CI/CD dataflow with a different data destination. |
|
|
147
225
|
| `upgrade_to_gen2_cicd(...)` | Upgrade a Gen1 or Gen2 (standard) dataflow to Gen2 CI/CD. |
|
|
148
226
|
|
|
149
|
-
|
|
227
|
+
### Pipeline
|
|
150
228
|
|
|
151
229
|
Manage Fabric Data Pipelines.
|
|
152
230
|
|
|
231
|
+
```python
|
|
232
|
+
pipe = Pipeline(auth.get_token('fabric'))
|
|
233
|
+
activities = pipe.get_pipeline_activities(workspace_id, 'My Pipeline')
|
|
234
|
+
```
|
|
235
|
+
|
|
153
236
|
| Method | Description |
|
|
154
237
|
|---|---|
|
|
155
238
|
| `list_pipelines(workspace_id)` | List all Fabric Data Pipelines in a workspace. |
|
|
156
239
|
| `get_pipeline(workspace_id, pipeline_id)` | Get the metadata of a specific pipeline. |
|
|
157
|
-
| `get_pipeline_definition(workspace_id, pipeline_id)` | Get the full definition of a
|
|
240
|
+
| `get_pipeline_definition(workspace_id, pipeline_id)` | Get the full definition of a pipeline. |
|
|
158
241
|
| `update_pipeline_definition(workspace_id, pipeline_id, definition)` | Update an existing pipeline definition. |
|
|
159
|
-
| `get_pipeline_activities(workspace_id, pipeline_id_or_name)` | Get
|
|
160
|
-
| `find_pipelines_by_dataflow(workspace_id, dataflow_id_or_name)` | Find
|
|
161
|
-
| `replace_dataflow_id_in_pipeline(workspace_id, pipeline_id,
|
|
242
|
+
| `get_pipeline_activities(workspace_id, pipeline_id_or_name)` | Get activities from a pipeline. Accepts ID or display name. |
|
|
243
|
+
| `find_pipelines_by_dataflow(workspace_id, dataflow_id_or_name)` | Find pipelines that reference a specific dataflow. |
|
|
244
|
+
| `replace_dataflow_id_in_pipeline(workspace_id, pipeline_id, old_id, new_id)` | Replace a dataflow ID in all RefreshDataflow activities. |
|
|
162
245
|
|
|
163
|
-
|
|
246
|
+
<details>
|
|
247
|
+
<summary><strong>Example: replacing a dataflow destination and updating pipelines</strong></summary>
|
|
164
248
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
249
|
+
When `change_data_destination(mode='replace')` is used on a standard Gen2 dataflow, the original is deleted and a new CI/CD dataflow is created with a new ID. Pipelines referencing the old ID must be updated:
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
from msdev_kit import Auth
|
|
253
|
+
from msdev_kit.fabric import Dataflow, Pipeline
|
|
254
|
+
|
|
255
|
+
auth = Auth(tenant_id, client_id, client_secret)
|
|
256
|
+
dataflow = Dataflow(auth.get_token('pbi'))
|
|
257
|
+
pipeline = Pipeline(auth.get_token('fabric'))
|
|
258
|
+
|
|
259
|
+
workspace_id = '<workspace_id>'
|
|
260
|
+
old_dataflow_id = '<dataflow_id>'
|
|
261
|
+
|
|
262
|
+
# 1. replace dataflow destination (creates new CI/CD, deletes original)
|
|
263
|
+
result = dataflow.change_data_destination(
|
|
264
|
+
workspace_id=workspace_id,
|
|
265
|
+
dataflow_id=old_dataflow_id,
|
|
266
|
+
destination_type='Warehouse',
|
|
267
|
+
destination_workspace_id=workspace_id,
|
|
268
|
+
destination_item_id='<warehouse_id>',
|
|
269
|
+
mode='replace'
|
|
270
|
+
)
|
|
271
|
+
new_dataflow_id = result['content']['id']
|
|
272
|
+
|
|
273
|
+
# 2. find pipelines referencing the old dataflow ID
|
|
274
|
+
matches = pipeline.find_pipelines_by_dataflow(workspace_id, old_dataflow_id)
|
|
275
|
+
|
|
276
|
+
# 3. update each pipeline to use the new ID
|
|
277
|
+
for m in matches['content']:
|
|
278
|
+
pipeline.replace_dataflow_id_in_pipeline(
|
|
279
|
+
workspace_id, m['pipeline_id'], old_dataflow_id, new_dataflow_id
|
|
280
|
+
)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
</details>
|
|
284
|
+
|
|
285
|
+
### Other modules
|
|
286
|
+
|
|
287
|
+
| Module | Class | Description |
|
|
288
|
+
|---|---|---|
|
|
289
|
+
| Capacity | `Capacity` | Monitor and manage Power BI and Fabric capacities. |
|
|
290
|
+
| Operations | `Operations` | Track long-running Fabric API operations. |
|
|
291
|
+
| Admin | `Admin` | Power BI Admin API operations. |
|
|
292
|
+
| KQL | `KQLDatabase` | Query Kusto (KQL) databases in Microsoft Fabric. |
|
|
293
|
+
| Notebook | `Notebook` | Manage Fabric notebooks (list, get metadata). |
|
|
294
|
+
| Database | `Database` | Query and write to SQL databases (Lakehouse, Warehouse) via ODBC. |
|
|
173
295
|
|
|
174
296
|
---
|
|
175
297
|
|
|
176
|
-
|
|
298
|
+
## MS Graph (Entra)
|
|
177
299
|
|
|
178
300
|
Manage Entra ID (Azure AD) users and groups via the MS Graph API.
|
|
179
301
|
|
|
@@ -184,8 +306,12 @@ from msdev_kit.graph import GraphClient
|
|
|
184
306
|
auth = Auth(tenant_id, client_id, client_secret)
|
|
185
307
|
graph = GraphClient(auth)
|
|
186
308
|
|
|
309
|
+
# look up a user and add them to a group
|
|
187
310
|
user_id = graph.get_user_id('user@company.com')
|
|
188
311
|
group_id = graph.get_group_id('Data Team')
|
|
312
|
+
graph.add_group_member(group_id, user_id)
|
|
313
|
+
|
|
314
|
+
# list all members of a group
|
|
189
315
|
members = graph.list_group_members(group_id)
|
|
190
316
|
```
|
|
191
317
|
|
|
@@ -199,7 +325,7 @@ members = graph.list_group_members(group_id)
|
|
|
199
325
|
|
|
200
326
|
---
|
|
201
327
|
|
|
202
|
-
|
|
328
|
+
## SharePoint
|
|
203
329
|
|
|
204
330
|
Manage SharePoint files and folders via MS Graph API (no ACS/Office365 dependency).
|
|
205
331
|
|
|
@@ -210,9 +336,15 @@ from msdev_kit.sharepoint import SharePointClient
|
|
|
210
336
|
auth = Auth(tenant_id, client_id, client_secret)
|
|
211
337
|
sp = SharePointClient(auth, sp_hostname='company', sp_site_path='sites/DataTeam')
|
|
212
338
|
|
|
339
|
+
# download a file
|
|
213
340
|
sp.download_file('/Reports/monthly.xlsx', local_dir='./downloads')
|
|
341
|
+
|
|
342
|
+
# upload a file (from path or bytes)
|
|
214
343
|
sp.upload_file('/Reports/updated.xlsx', source='./local/updated.xlsx')
|
|
215
|
-
sp.
|
|
344
|
+
sp.upload_file('/Reports/data.csv', source=csv_bytes, content_type='text/csv')
|
|
345
|
+
|
|
346
|
+
# create nested folders
|
|
347
|
+
sp.create_folder('/Reports/2026/Q1')
|
|
216
348
|
```
|
|
217
349
|
|
|
218
350
|
| Method | Description |
|
|
@@ -221,7 +353,16 @@ sp.create_folder('/Reports/2026')
|
|
|
221
353
|
| `upload_file(remote_path, source, content_type?)` | Upload/overwrite a file. `source` is a local file path (str) or raw bytes. |
|
|
222
354
|
| `create_folder(folder_path)` | Create a folder and all intermediate folders. |
|
|
223
355
|
|
|
224
|
-
Hostname and site path inputs are normalized automatically
|
|
356
|
+
Hostname and site path inputs are normalized automatically:
|
|
357
|
+
|
|
358
|
+
| Input | Normalized to |
|
|
359
|
+
|---|---|
|
|
360
|
+
| `company` | `company.sharepoint.com` |
|
|
361
|
+
| `company.sharepoint.com` | `company.sharepoint.com` |
|
|
362
|
+
| `https://company.sharepoint.com` | `company.sharepoint.com` |
|
|
363
|
+
| `DataTeam` | `sites/DataTeam` |
|
|
364
|
+
| `sites/DataTeam` | `sites/DataTeam` |
|
|
365
|
+
| `/sites/DataTeam` | `sites/DataTeam` |
|
|
225
366
|
|
|
226
367
|
---
|
|
227
368
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|