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.
Files changed (22) hide show
  1. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/PKG-INFO +197 -56
  2. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/README.md +196 -55
  3. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/__init__.py +1 -1
  4. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/pyproject.toml +1 -1
  5. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/auth.py +0 -0
  6. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/__init__.py +0 -0
  7. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/admin.py +0 -0
  8. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/capacity.py +0 -0
  9. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/database.py +0 -0
  10. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/dataflow.py +0 -0
  11. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/dataset.py +0 -0
  12. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/kql.py +0 -0
  13. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/notebook.py +0 -0
  14. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/operations.py +0 -0
  15. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/pipeline.py +0 -0
  16. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/report.py +0 -0
  17. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/utilities.py +0 -0
  18. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/fabric/workspace.py +0 -0
  19. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/graph/__init__.py +0 -0
  20. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/graph/client.py +0 -0
  21. {msdev_kit-0.1.0 → msdev_kit-0.1.1}/msdev_kit/sharepoint/__init__.py +0 -0
  22. {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.0
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
- ## 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](#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
- ### Install from GitHub
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
- ### Install for local development
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
- ## Prerequisites
75
+ ## Quick Start
60
76
 
61
- - Python >= 3.10
62
- - An Azure app registration with a client ID and client secret
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
- ## Getting Started
83
+ # authenticate
84
+ auth = Auth(tenant_id="...", client_id="...", client_secret="...")
65
85
 
66
- ### Authentication
86
+ # fabric: list workspaces
87
+ ws = Workspace(auth.get_token('fabric'))
88
+ workspaces = ws.list_workspaces_for_user()
67
89
 
68
- All classes use a shared `Auth` object. You can use different service principals for different services:
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
- auth = Auth(tenant_id="...", client_id="...", client_secret="...")
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
- 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
113
+ ### Supported scopes
80
114
 
81
- # interactive user auth
82
- token = auth.get_token_for_user('pbi')
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
- ## Sub-packages
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
- #### Workspace
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
- #### Dataset
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 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. |
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
- #### Report
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 and visual details into a DataFrame. |
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. 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. |
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
- #### Dataflow
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 in a workspace (Gen1, Gen2 standard, and Gen2 CI/CD). Results are merged and deduplicated with a `source` column. |
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 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. |
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
- #### Pipeline
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 Fabric Data Pipeline. |
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 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. |
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
- #### Other modules
276
+ <details>
277
+ <summary><strong>Example: replacing a dataflow destination and updating pipelines</strong></summary>
194
278
 
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. |
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
- ### `msdev_kit.graph` — MS Graph (Entra)
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
- ### `msdev_kit.sharepoint` — SharePoint
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.create_folder('/Reports/2026')
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 — accepts short names (`company`), FQDNs (`company.sharepoint.com`), or full URLs (`https://company.sharepoint.com`).
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
- ## 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](#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
- ### Install from GitHub
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
- ### Install for local development
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
- ## Prerequisites
45
+ ## Quick Start
30
46
 
31
- - Python >= 3.10
32
- - An Azure app registration with a client ID and client secret
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
- ## Getting Started
53
+ # authenticate
54
+ auth = Auth(tenant_id="...", client_id="...", client_secret="...")
35
55
 
36
- ### Authentication
56
+ # fabric: list workspaces
57
+ ws = Workspace(auth.get_token('fabric'))
58
+ workspaces = ws.list_workspaces_for_user()
37
59
 
38
- All classes use a shared `Auth` object. You can use different service principals for different services:
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
- auth = Auth(tenant_id="...", client_id="...", client_secret="...")
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
- 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
83
+ ### Supported scopes
50
84
 
51
- # interactive user auth
52
- token = auth.get_token_for_user('pbi')
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
- ## Sub-packages
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
- #### Workspace
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
- #### Dataset
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 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. |
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
- #### Report
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 and visual details into a DataFrame. |
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. 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. |
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
- #### Dataflow
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 in a workspace (Gen1, Gen2 standard, and Gen2 CI/CD). Results are merged and deduplicated with a `source` column. |
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 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. |
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
- #### Pipeline
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 Fabric Data Pipeline. |
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 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. |
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
- #### Other modules
246
+ <details>
247
+ <summary><strong>Example: replacing a dataflow destination and updating pipelines</strong></summary>
164
248
 
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. |
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
- ### `msdev_kit.graph` — MS Graph (Entra)
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
- ### `msdev_kit.sharepoint` — SharePoint
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.create_folder('/Reports/2026')
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 — accepts short names (`company`), FQDNs (`company.sharepoint.com`), or full URLs (`https://company.sharepoint.com`).
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
 
@@ -1,3 +1,3 @@
1
1
  from .auth import Auth
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.1"
@@ -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.1"
5
5
  requires-python = ">=3.10"
6
6
  readme = "README.md"
7
7
  license = {text = "MIT"}
File without changes