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