Skip to content

Fabric API Reference

Module Functions

fabias.client = _create_client module-attribute

fabias.workspace

Microsoft Fabric Workspace model.

Represents a Fabric workspace and provides access to workspace resources.

Classes

Workspace

Represents a Microsoft Fabric workspace.

Provides access to workspace resources including pipelines, lakehouses, notebooks, environments, and Git integration. Resolves workspace identifiers from display names or GUIDs.

Attributes:

Name Type Description
id str

Workspace unique identifier (GUID)

name str

Workspace display name

capacityId str

Capacity GUID hosting this workspace

description str

Workspace description

Examples:

Access via FabricClient:

>>> client = FabricClient()
>>> workspace = client.workspace("GENESIS_EXT")
>>> print(f"Workspace: {workspace.name} ({workspace.id})")

Access workspace resources:

>>> pipeline = workspace.pipeline("Daily ETL")
>>> lakehouse = workspace.lakehouse("Analytics")
>>> git = workspace.git
Source code in src/fabias/_fabric/workspace.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
class Workspace:
    """
    Represents a Microsoft Fabric workspace.

    Provides access to workspace resources including pipelines, lakehouses,
    notebooks, environments, and Git integration. Resolves workspace
    identifiers from display names or GUIDs.

    Attributes:
        id (str): Workspace unique identifier (GUID)
        name (str): Workspace display name
        capacityId (str): Capacity GUID hosting this workspace
        description (str): Workspace description

    Examples:
        Access via FabricClient:

        >>> client = FabricClient()
        >>> workspace = client.workspace("GENESIS_EXT")
        >>> print(f"Workspace: {workspace.name} ({workspace.id})")

        Access workspace resources:

        >>> pipeline = workspace.pipeline("Daily ETL")
        >>> lakehouse = workspace.lakehouse("Analytics")
        >>> git = workspace.git
    """

    def __init__(
        self,
        client: "FabricClient",
        workspace_id: Optional[str] = None,
        workspace_data: Optional[dict] = None,
    ):
        """
        Initialize workspace.

        Args:
            client: Authenticated FabricClient instance
            workspace_id: Workspace identifier (name, GUID, or None for current)
            workspace_data: Pre-populated workspace data from API (skips resolution)

        Behavior:
            - If workspace_data provided: Pre-populate attributes (no API call)
            - If GUID provided: Store it without API call (lazy load properties)
            - If name provided: Resolve immediately to get GUID (required for operations)
            - If None or 'default': Resolve to current workspace (Fabric only)

        Raises:
            NotFoundError: If workspace name cannot be found
            FabiasError: If not in Fabric and no workspace_id provided
        """
        self._client = client
        self._workspace_id = workspace_id
        self._data_fetched = False

        # Data attributes (lazy loaded unless resolved by name or pre-populated)
        self._id: Optional[str] = None
        self._name: Optional[str] = None
        self._capacity_id: Optional[str] = None
        self._description: Optional[str] = None

        # Cached sub-objects
        self._git: Optional["Git"] = None
        self._spark: Optional["Spark"] = None
        self._items_cache: dict = {}

        # Pre-populate from provided data
        if workspace_data:
            self._id = workspace_data.get("id")
            self._name = workspace_data.get("displayName") or workspace_data.get("name")
            self._capacity_id = workspace_data.get("capacityId")
            self._description = workspace_data.get("description")
            self._data_fetched = True
            return

        # If GUID provided, store it without API call
        if workspace_id and GUID.valid(workspace_id):
            self._id = workspace_id
            return

        # If name provided (or None/default), resolve immediately to get GUID
        # We need GUID for all subsequent operations
        self._resolve_identifier(workspace_id)

    def _resolve_identifier(self, workspace_id: Optional[str]) -> None:
        """
        Resolve workspace identifier to get the GUID.

        Called when a name is provided (or None/default).
        Populates all data since we're already making an API call.
        Filters out deleted workspaces when searching by name.

        Args:
            workspace_id: Identifier to resolve
        """
        # Handle None or "default" - use current workspace
        if not workspace_id or workspace_id.strip().lower() == "default":  # pragma: no cover
            if not runtime.isFabric:
                raise FabiasError(
                    "Cannot determine current workspace outside Fabric. "
                    "Please provide a workspace ID or name."
                )

            # Get from runtime context
            workspace_id = runtime.context.get("workspace_id")

        if not workspace_id:
            raise FabiasError("Could not determine workspace ID")

        # If it happens to be a GUID at this point, just store it
        if GUID.valid(workspace_id):
            self._id = workspace_id
            return

        # Name-based resolution: try admin API first (if credentials available)
        # Admin API has state field to filter deleted workspaces
        if self._client.hasExplicitCredentials:
            try:
                response = self._client.get(f"/admin/workspaces?name={workspace_id}")
                data = response.json()
                workspaces = data.get("workspaces", [])

                for workspace in workspaces:
                    if workspace.get("name") == workspace_id:
                        state = workspace.get("state", "Active")

                        # Filter out deleted workspaces
                        if state == "Deleted":
                            workspace_guid = workspace.get("id")
                            raise NotFoundError(
                                f"Workspace '{workspace_id}' exists but is in 'Deleted' state. "
                                f"Restore it using: fabric.workspaces.restore('{workspace_guid}')",
                                resource_type="Workspace",
                                resource_id=workspace_id,
                            )

                        self._id = workspace.get("id")
                        self._name = workspace.get("name")
                        self._capacity_id = workspace.get("capacityId")
                        self._description = workspace.get("description")
                        self._data_fetched = True
                        return
            except NotFoundError:
                raise  # Re-raise if workspace is deleted
            except Exception:
                pass  # Fall through to list search

        # Fallback: use client.workspaces() which handles sempy vs API internally
        workspaces = self._client.workspaces()

        matches = [w for w in workspaces if w.name == workspace_id or w.id == workspace_id]

        if not matches:
            raise NotFoundError(
                f"Workspace '{workspace_id}' not found",
                resource_type="Workspace",
                resource_id=workspace_id,
            )

        if len(matches) > 1:
            raise FabiasError(f"Multiple workspaces found with name '{workspace_id}'")

        matched = matches[0]

        # Verify the workspace is accessible (not deleted)
        # If the regular API returned it, try to access it
        try:
            self._client.get(f"/workspaces/{matched.id}")
        except Exception:
            raise NotFoundError(
                f"Workspace '{workspace_id}' exists but is not accessible (may be deleted)",
                resource_type="Workspace",
                resource_id=workspace_id,
            )

        self._id = matched.id
        self._name = matched.name
        self._capacity_id = matched.capacityId
        self._description = matched.description
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load workspace data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/workspaces/{self._id}")
        data = response.json()
        self._name = data.get("displayName")
        self._capacity_id = data.get("capacityId")
        self._description = data.get("description")
        self._data_fetched = True

    @property
    def id(self) -> Optional[str]:
        """Workspace GUID."""
        return self._id

    def _get_id(self) -> str:
        """Get workspace ID with assertion (for internal use)."""
        if self._id is None:
            raise FabiasError("Workspace ID not set - workspace not properly initialized")
        return self._id

    @property
    def name(self) -> Optional[str]:
        """Workspace display name (lazy loaded)."""
        self._fetch()
        return self._name

    @property
    def capacityId(self) -> Optional[str]:
        """Capacity GUID hosting this workspace (lazy loaded)."""
        self._fetch()
        return self._capacity_id

    @property
    def description(self) -> Optional[str]:
        """Workspace description (lazy loaded)."""
        self._fetch()
        return self._description

    def delete(self) -> None:  # pragma: no cover
        """
        Delete this workspace (soft delete).

        The workspace enters a retention period (default 7 days, configurable up to 90 days)
        during which Fabric administrators can restore it using the restore() method.

        Example:
            >>> ws = fabric.workspace("Old Workspace")
            >>> ws.delete()
            >>> # Later, restore it:
            >>> ws.restore()
        """
        if not self._id:
            raise FabiasError("Cannot delete workspace: ID not resolved")

        self._client.delete(f"/workspaces/{self._id}")

        # Clear local state since workspace no longer exists
        self._id = None
        self._name = None
        self._capacity_id = None
        self._description = None
        self._data_fetched = False

    def restore(
        self,
        new_admin_principal_id: Optional[str] = None,
        new_name: Optional[str] = None,
    ) -> "Workspace":  # pragma: no cover
        """
        Restore a deleted workspace.

        Requires Fabric Administrator privileges. The workspace must be in "Deleted" state
        within the retention period (7-90 days, default 7).

        Args:
            new_admin_principal_id: Optional principal ID to assign as workspace admin
            new_name: Optional new name for the workspace (required for My workspaces)

        Returns:
            Workspace: The restored workspace

        Raises:
            FabiasError: If workspace ID not set or restore fails

        Examples:
            >>> # Restore with original settings
            >>> deleted_ws = fabric.workspace(workspace_id)  # By GUID
            >>> deleted_ws.restore()

            >>> # Restore with new admin
            >>> deleted_ws.restore(new_admin_principal_id="user-guid")

            >>> # Restore My workspace with new name
            >>> deleted_ws.restore(new_name="Restored Workspace")
        """
        if not self._id:
            raise FabiasError("Cannot restore workspace: ID not resolved")

        payload: dict[str, Any] = {}
        if new_admin_principal_id:
            payload["newWorkspaceAdminPrincipal"] = {
                "id": new_admin_principal_id,
                "type": "User",  # Could also be ServicePrincipal, Group, etc.
            }
        if new_name:
            payload["newWorkspaceName"] = new_name

        # Call admin restore API
        self._client.post(f"/admin/workspaces/{self._id}/restore", data=payload)

        # Refresh workspace data
        self._data_fetched = False
        self._fetch()

        return self

    @property
    def spark(self) -> "Spark":
        """
        Get Spark settings handler for this workspace.

        Returns:
            Spark: Spark settings handler

        Examples:
            Get current Spark settings:

            >>> settings = workspace.spark.settings()
            >>> print(f"Auto log: {settings.automatic_log}")
            >>> print(f"Pool max nodes: {settings.pool_max_nodes}")

            Update Spark settings:

            >>> workspace.spark.settings.update(
            ...     automatic_log=True,
            ...     pool={"starterPool": {"maxNodeCount": 10, "maxExecutors": 5}},
            ... )
        """
        if self._spark is None:
            from .spark import Spark

            self._spark = Spark(self._client, self._get_id())
        return self._spark

    @property
    def git(self) -> "Git":
        """
        Get Git integration handler for this workspace.

        Returns:
            Git: Git operations handler

        Examples:
            >>> git = workspace.git
            >>> status = git.status()
            >>> if status.has_changes:
            ...     git.pull()
        """
        if self._git is None:
            from .git import Git

            self._git = Git(self._client, self._get_id())
        return self._git

    def roleAssignment(self, principal_id: str) -> "WorkspaceRoleAssignment":
        """
        Get a specific role assignment by principal ID.

        This method accepts a principal ID and looks up the corresponding
        role assignment. If multiple or no assignments are found, raises an error.

        Args:
            principal_id: Principal GUID (user, group, or service principal)

        Returns:
            WorkspaceRoleAssignment: The role assignment

        Raises:
            NotFoundError: If no role assignment found for the principal
            FabiasError: If multiple role assignments found (shouldn't happen)

        Examples:
            >>> ws = fabric.workspace("GENESIS")
            >>> assignment = ws.roleAssignment("user-guid")
            >>> print(assignment.role)
        """
        # List all assignments to find the one with matching principal_id
        assignments = self.roleAssignments()

        # Find assignment with matching principal ID
        matches = [a for a in assignments if a.principalId == principal_id]

        if not matches:
            from .._shared.exceptions import NotFoundError

            raise NotFoundError(
                f"No role assignment found for principal {principal_id}",
                resource_type="WorkspaceRoleAssignment",
                resource_id=principal_id,
            )

        if len(matches) > 1:
            from .._shared.exceptions import FabiasError

            raise FabiasError(
                f"Multiple role assignments found for principal {principal_id}. "
                "This should not happen."
            )

        return cast("WorkspaceRoleAssignment", matches[0])

    @property
    def roleAssignments(self) -> "WorkspaceRoleAssignmentAccessor":
        """
        Access role assignments for this workspace.

        Returns:
            WorkspaceRoleAssignmentAccessor: Accessor for listing and managing role assignments

        Examples:
            List role assignments:

            >>> ws = fabric.workspace("GENESIS")
            >>> for assignment in ws.roleAssignments():
            ...     print(f"{assignment.principalName}: {assignment.role}")

            Add role assignment:

            >>> ws.roleAssignments.add(
            ...     principal_id="user-guid",
            ...     principal_type="User",
            ...     role="Admin"
            ... )
        """
        return WorkspaceRoleAssignmentAccessor(self._client, self._get_id())

    # =============================================================================
    # Items
    # =============================================================================

    def pipeline(self, identifier: str) -> "Pipeline":
        """
        Get a specific pipeline by name or ID.

        Args:
            identifier: Pipeline name or GUID

        Returns:
            Pipeline: The pipeline object

        Examples:
            >>> pipeline = workspace.pipeline("Daily ETL")
            >>> job = pipeline.run()
        """
        from .items.pipeline import Pipeline

        return Pipeline(self._client, self._get_id(), identifier)

    @property
    def pipelines(self) -> "PipelineAccessor":
        """
        Access pipeline list and creation operations.

        Returns:
            PipelineAccessor: Accessor for listing and creating pipelines

        Examples:
            List all pipelines:

            >>> for pipeline in workspace.pipelines():
            ...     print(pipeline.name)

            Create new pipeline:

            >>> pipeline = workspace.pipelines.add("New ETL")
        """
        from .items.pipeline import PipelineAccessor

        return PipelineAccessor(self._client, self._get_id())

    def lakehouse(self, identifier: str) -> "Lakehouse":
        """
        Get a specific lakehouse by name or ID.

        Args:
            identifier: Lakehouse name or GUID

        Returns:
            Lakehouse: The lakehouse object

        Examples:
            >>> lakehouse = workspace.lakehouse("Analytics")
        """
        from .items.lakehouse import Lakehouse

        return Lakehouse(self._client, self._get_id(), identifier)

    @property
    def lakehouses(self) -> "LakehouseAccessor":
        """
        Access lakehouse list and creation operations.

        Returns:
            LakehouseAccessor: Accessor for listing and creating lakehouses

        Examples:
            List all lakehouses:

            >>> for lh in workspace.lakehouses():
            ...     print(lh.name)

            Create new lakehouse:

            >>> lakehouse = workspace.lakehouses.add("Data Lake")
        """
        from .items.lakehouse import LakehouseAccessor

        return LakehouseAccessor(self._client, self._get_id())

    def notebook(self, identifier: str) -> "Notebook":
        """
        Get a specific notebook by name or ID.

        Args:
            identifier: Notebook name or GUID

        Returns:
            Notebook: The notebook object

        Examples:
            >>> notebook = workspace.notebook("ETL Script")
        """
        from .items.notebook import Notebook

        return Notebook(self._client, self._get_id(), identifier)

    @property
    def notebooks(self) -> "NotebookAccessor":
        """
        Access notebook list and creation operations.

        Returns:
            NotebookAccessor: Accessor for listing and creating notebooks

        Examples:
            List all notebooks:

            >>> for nb in workspace.notebooks():
            ...     print(nb.name)

            Create new notebook:

            >>> notebook = workspace.notebooks.add("New Analysis")
        """
        from .items.notebook import NotebookAccessor

        return NotebookAccessor(self._client, self._get_id())

    def environment(self, identifier: str) -> "Environment":
        """
        Get a specific environment by name or ID.

        Args:
            identifier: Environment name or GUID

        Returns:
            Environment: The environment object

        Examples:
            >>> env = workspace.environment("ML Environment")
        """
        from .items.environment import Environment

        return Environment(self._client, self._get_id(), identifier)

    @property
    def environments(self) -> "EnvironmentAccessor":
        """
        Access environment list and creation operations.

        Returns:
            EnvironmentAccessor: Accessor for listing and creating environments

        Examples:
            List all environments:

            >>> for env in workspace.environments():
            ...     print(env.name)

            Create new environment:

            >>> env = workspace.environments.add("Data Science")
        """
        from .items.environment import EnvironmentAccessor

        return EnvironmentAccessor(self._client, self._get_id())

    def variableLibrary(self, identifier: str) -> "VariableLibrary":
        """
        Get a specific variable library by name or ID.

        Args:
            identifier: Variable library name or GUID

        Returns:
            VariableLibrary: The variable library object

        Examples:
            >>> lib = workspace.variableLibrary("Deployment Config")
            >>> print(f"Active value set: {lib.active_value_set}")
        """
        from .items.variablelibrary import VariableLibrary

        return VariableLibrary(self._client, self._get_id(), identifier)

    @property
    def variableLibraries(self) -> "VariableLibraryAccessor":
        """
        Access variable library list and creation operations.

        Returns:
            VariableLibraryAccessor: Accessor for listing and creating variable libraries

        Examples:
            List all variable libraries:

            >>> for lib in workspace.variableLibraries():
            ...     print(f"{lib.name}: active={lib.active_value_set}")

            Create new variable library:

            >>> lib = workspace.variableLibraries.add("Deployment Config")

            Create with initial variables:

            >>> import base64, json
            >>> variables_json = {
            ...     "$schema": "...",
            ...     "variables": [{"name": "env", "type": "String", "value": "dev"}]
            ... }
            >>> lib = workspace.variableLibraries.add(
            ...     "Deployment Config",
            ...     definition={
            ...         "format": "VariableLibraryV1",
            ...         "parts": [{
            ...             "path": "variables.json",
            ...             "payload": base64.b64encode(
            ...                 json.dumps(variables_json).encode()
            ...             ).decode(),
            ...             "payloadType": "InlineBase64"
            ...         }]
            ...     }
            ... )
        """
        from .items.variablelibrary import VariableLibraryAccessor

        return VariableLibraryAccessor(self._client, self._get_id())

    # =============================================================================
    # Folders
    # =============================================================================

    def folder(self, identifier: str) -> "Folder":
        """
        Get a specific folder by name or ID.

        Args:
            identifier: Folder display name or GUID

        Returns:
            Folder: The folder object

        Examples:
            >>> folder = workspace.folder("Sales")
            >>> print(f"{folder.name} (parent: {folder.parent})")
        """
        from .folders import Folder

        return Folder(self._client, self._get_id(), identifier)

    @property
    def folders(self) -> "FolderAccessor":
        """
        Access folder list and creation operations.

        Returns:
            FolderAccessor: Accessor for listing and creating folders

        Examples:
            List all folders:

            >>> for f in workspace.folders():
            ...     print(f"{f.name} (parent: {f.parent})")

            List direct children of a folder:

            >>> children = workspace.folders(root_folder_id=parent.id, recursive=False)

            Create new folder:

            >>> folder = workspace.folders.add("Reports")

            Create nested folder:

            >>> child = workspace.folders.add("Q1", parent=parent.id)
        """
        from .folders import FolderAccessor

        return FolderAccessor(self._client, self._get_id())

    # =============================================================================
    # Items
    # =============================================================================

    def items(self, item_type: Optional[Union[ItemType, str]] = None) -> list:
        """
        List items in this workspace, converted to specific types.

        Returns typed item objects (Pipeline, Notebook, etc.) based on the
        'type' field in the API response. Unknown types return base Item.

        Args:
            item_type: Optional filter by type. Can be ItemType enum or string
                (e.g., ItemType.DATA_PIPELINE, "DataPipeline", ItemType.NOTEBOOK, "Notebook")

        Returns:
            list: List of Item objects (Pipeline, Notebook, etc.)

        Examples:
            All items:

            >>> items = workspace.items()
            >>> for item in items:
            ...     print(f"{item.name}: {type(item).__name__}")

            Filtered by type:

            >>> pipelines = workspace.items(item_type="DataPipeline")
            >>> notebooks = workspace.items(item_type="Notebook")
        """
        from .items import fromJson

        # Build endpoint with optional type filter
        endpoint = f"/workspaces/{self.id}/items"
        if item_type:
            # Convert enum to string if needed
            type_str = item_type.value if isinstance(item_type, ItemType) else item_type
            endpoint = f"{endpoint}?type={type_str}"

        # Get all items (pagination handled automatically by client)
        response = self._client.get(endpoint)
        data = response.json()

        # Convert JSON to typed objects
        return [
            fromJson(self._client, self._get_id(), item_data) for item_data in data.get("value", [])
        ]

    @classmethod
    @classmethod
    def create(  # pragma: no cover
        cls, client: "FabricClient", name: str, capacity: str, description: Optional[str] = None
    ) -> "Workspace":
        """
        Create a new workspace.

        If a workspace with the same name already exists and is in "Active" state,
        returns the existing workspace. If the workspace exists but is in "Deleted"
        state, raises an error indicating it must be permanently deleted first.

        Args:
            client: Authenticated FabricClient instance
            name: Display name for the new workspace
            capacity: Capacity GUID to host the workspace
            description: Optional workspace description

        Returns:
            Workspace: The newly created or existing active workspace

        Raises:
            FabiasError: If workspace exists in "Deleted" state

        Examples:
            >>> from fabias.fabric import FabricClient, Workspace
            >>> client = FabricClient(auth=auth)
            >>> ws = Workspace.create(client, "New Workspace", capacity="...")
        """
        payload = {"displayName": name, "capacityId": capacity}
        if description is not None:
            payload["description"] = description

        try:
            response = client.post("/workspaces", data=payload)

            if response.status_code == 202:
                operation_id = response.headers.get("x-ms-operation-id")
                raise FabiasError(
                    f"Workspace creation started asynchronously (operation: {operation_id}). "
                    "Polling not yet implemented."
                )

            workspace_data = response.json()
            workspace_id = workspace_data.get("id")

            # Return new Workspace instance with resolved ID
            return cls(client, workspace_id=workspace_id)
        except Exception as e:
            # If workspace already exists (409), fetch and return it if Active
            from .._shared.exceptions import ApiError

            if (
                (isinstance(e, ApiError) and e.status_code == 409)
                or "409" in str(e)
                or "already exists" in str(e).lower()
            ):
                # Use admin API to get state information
                if client.hasExplicitCredentials:
                    try:
                        response = client.get(f"/admin/workspaces?name={name}")
                        data = response.json()
                        workspaces_data = data.get("workspaces", [])

                        for ws_data in workspaces_data:
                            if ws_data.get("name") == name:
                                state = ws_data.get("state", "Active")

                                if state == "Deleted":
                                    raise FabiasError(
                                        f"Workspace '{name}' exists in 'Deleted' state. "
                                        "It must be permanently deleted by a Fabric admin "
                                        "before creating a new workspace with this name."
                                    )

                                # Active or other state - return the workspace
                                return cls(client, workspace_data=ws_data)
                    except FabiasError:
                        raise  # Re-raise our custom error
                    except Exception:
                        pass  # Fall through to regular workspaces API

                # Fallback: regular workspaces API (may not have state field)
                workspaces = client.workspaces()
                matches = [w for w in workspaces if w.name == name]

                if matches:
                    # Without state field, we can't distinguish deleted workspaces
                    # Try to access the workspace to verify it's active
                    ws = matches[0]
                    try:
                        # Attempt to fetch workspace details - will fail if deleted
                        client.get(f"/workspaces/{ws.id}")
                        return ws
                    except Exception:
                        raise FabiasError(
                            f"Workspace '{name}' exists but is not accessible. "
                            "It may be in 'Deleted' state. Use admin API or wait for "
                            "permanent deletion before creating a workspace with this name."
                        )
            raise

    def __repr__(self) -> str:
        """Return string representation."""
        return f"Workspace(id={self.id}, name={self.name!r})"

    def __getattr__(self, name: str) -> Callable:
        """
        Dynamic attribute access for undefined endpoints.

        Allows accessing Fabric API endpoints that don't have explicit methods.
        Converts attribute name to API endpoint and returns a callable or data.

        Args:
            name: Attribute name (will be converted to API endpoint)

        Returns:
            Callable: A function that accepts an optional item_id parameter

        Examples:
            # List eventhouses (no explicit method defined)
            >>> eventhouses = workspace.eventhouses()

            # Get specific eventhouse
            >>> eh = workspace.eventhouse("some-id")

            # Access any Fabric item type
            >>> warehouses = workspace.warehouses()
            >>> kqlDatabases = workspace.kqlDatabases()
        """
        # Don't intercept private attributes or known properties
        if name.startswith("_"):
            raise AttributeError(name)

        def dynamic_item_accessor(item_id: Optional[str] = None):
            """
            Access items of a given type.

            If item_id is provided, gets that specific item.
            Otherwise, lists all items of this type.
            """
            # Handle singular vs plural naming
            # e.g., "eventhouse" -> get one, "eventhouses" -> list all
            if item_id is not None:
                # Get specific item: /workspaces/{id}/{type}/{item_id}
                endpoint = f"/workspaces/{self.id}/{name}/{item_id}"
                response = self._client.get(endpoint)
                return response.json()
            else:
                # List items: /workspaces/{id}/{type}
                endpoint = f"/workspaces/{self.id}/{name}"
                response = self._client.get(endpoint)
                data = response.json()
                # Most Fabric list endpoints return {"value": [...]}
                if "value" in data:
                    return data["value"]
                return data

        return dynamic_item_accessor
Attributes
capacityId property

Capacity GUID hosting this workspace (lazy loaded).

description property

Workspace description (lazy loaded).

environments property

Access environment list and creation operations.

Returns:

Name Type Description
EnvironmentAccessor EnvironmentAccessor

Accessor for listing and creating environments

Examples:

List all environments:

>>> for env in workspace.environments():
...     print(env.name)

Create new environment:

>>> env = workspace.environments.add("Data Science")
folders property

Access folder list and creation operations.

Returns:

Name Type Description
FolderAccessor FolderAccessor

Accessor for listing and creating folders

Examples:

List all folders:

>>> for f in workspace.folders():
...     print(f"{f.name} (parent: {f.parent})")

List direct children of a folder:

>>> children = workspace.folders(root_folder_id=parent.id, recursive=False)

Create new folder:

>>> folder = workspace.folders.add("Reports")

Create nested folder:

>>> child = workspace.folders.add("Q1", parent=parent.id)
git property

Get Git integration handler for this workspace.

Returns:

Name Type Description
Git Git

Git operations handler

Examples:

>>> git = workspace.git
>>> status = git.status()
>>> if status.has_changes:
...     git.pull()
id property

Workspace GUID.

lakehouses property

Access lakehouse list and creation operations.

Returns:

Name Type Description
LakehouseAccessor LakehouseAccessor

Accessor for listing and creating lakehouses

Examples:

List all lakehouses:

>>> for lh in workspace.lakehouses():
...     print(lh.name)

Create new lakehouse:

>>> lakehouse = workspace.lakehouses.add("Data Lake")
name property

Workspace display name (lazy loaded).

notebooks property

Access notebook list and creation operations.

Returns:

Name Type Description
NotebookAccessor NotebookAccessor

Accessor for listing and creating notebooks

Examples:

List all notebooks:

>>> for nb in workspace.notebooks():
...     print(nb.name)

Create new notebook:

>>> notebook = workspace.notebooks.add("New Analysis")
pipelines property

Access pipeline list and creation operations.

Returns:

Name Type Description
PipelineAccessor PipelineAccessor

Accessor for listing and creating pipelines

Examples:

List all pipelines:

>>> for pipeline in workspace.pipelines():
...     print(pipeline.name)

Create new pipeline:

>>> pipeline = workspace.pipelines.add("New ETL")
roleAssignments property

Access role assignments for this workspace.

Returns:

Name Type Description
WorkspaceRoleAssignmentAccessor WorkspaceRoleAssignmentAccessor

Accessor for listing and managing role assignments

Examples:

List role assignments:

>>> ws = fabric.workspace("GENESIS")
>>> for assignment in ws.roleAssignments():
...     print(f"{assignment.principalName}: {assignment.role}")

Add role assignment:

>>> ws.roleAssignments.add(
...     principal_id="user-guid",
...     principal_type="User",
...     role="Admin"
... )
spark property

Get Spark settings handler for this workspace.

Returns:

Name Type Description
Spark Spark

Spark settings handler

Examples:

Get current Spark settings:

>>> settings = workspace.spark.settings()
>>> print(f"Auto log: {settings.automatic_log}")
>>> print(f"Pool max nodes: {settings.pool_max_nodes}")

Update Spark settings:

>>> workspace.spark.settings.update(
...     automatic_log=True,
...     pool={"starterPool": {"maxNodeCount": 10, "maxExecutors": 5}},
... )
variableLibraries property

Access variable library list and creation operations.

Returns:

Name Type Description
VariableLibraryAccessor VariableLibraryAccessor

Accessor for listing and creating variable libraries

Examples:

List all variable libraries:

>>> for lib in workspace.variableLibraries():
...     print(f"{lib.name}: active={lib.active_value_set}")

Create new variable library:

>>> lib = workspace.variableLibraries.add("Deployment Config")

Create with initial variables:

>>> import base64, json
>>> variables_json = {
...     "$schema": "...",
...     "variables": [{"name": "env", "type": "String", "value": "dev"}]
... }
>>> lib = workspace.variableLibraries.add(
...     "Deployment Config",
...     definition={
...         "format": "VariableLibraryV1",
...         "parts": [{
...             "path": "variables.json",
...             "payload": base64.b64encode(
...                 json.dumps(variables_json).encode()
...             ).decode(),
...             "payloadType": "InlineBase64"
...         }]
...     }
... )
Functions
__getattr__(name)

Dynamic attribute access for undefined endpoints.

Allows accessing Fabric API endpoints that don't have explicit methods. Converts attribute name to API endpoint and returns a callable or data.

Parameters:

Name Type Description Default
name str

Attribute name (will be converted to API endpoint)

required

Returns:

Name Type Description
Callable Callable

A function that accepts an optional item_id parameter

Examples:

List eventhouses (no explicit method defined)
>>> eventhouses = workspace.eventhouses()
Get specific eventhouse
>>> eh = workspace.eventhouse("some-id")
Access any Fabric item type
>>> warehouses = workspace.warehouses()
>>> kqlDatabases = workspace.kqlDatabases()
Source code in src/fabias/_fabric/workspace.py
def __getattr__(self, name: str) -> Callable:
    """
    Dynamic attribute access for undefined endpoints.

    Allows accessing Fabric API endpoints that don't have explicit methods.
    Converts attribute name to API endpoint and returns a callable or data.

    Args:
        name: Attribute name (will be converted to API endpoint)

    Returns:
        Callable: A function that accepts an optional item_id parameter

    Examples:
        # List eventhouses (no explicit method defined)
        >>> eventhouses = workspace.eventhouses()

        # Get specific eventhouse
        >>> eh = workspace.eventhouse("some-id")

        # Access any Fabric item type
        >>> warehouses = workspace.warehouses()
        >>> kqlDatabases = workspace.kqlDatabases()
    """
    # Don't intercept private attributes or known properties
    if name.startswith("_"):
        raise AttributeError(name)

    def dynamic_item_accessor(item_id: Optional[str] = None):
        """
        Access items of a given type.

        If item_id is provided, gets that specific item.
        Otherwise, lists all items of this type.
        """
        # Handle singular vs plural naming
        # e.g., "eventhouse" -> get one, "eventhouses" -> list all
        if item_id is not None:
            # Get specific item: /workspaces/{id}/{type}/{item_id}
            endpoint = f"/workspaces/{self.id}/{name}/{item_id}"
            response = self._client.get(endpoint)
            return response.json()
        else:
            # List items: /workspaces/{id}/{type}
            endpoint = f"/workspaces/{self.id}/{name}"
            response = self._client.get(endpoint)
            data = response.json()
            # Most Fabric list endpoints return {"value": [...]}
            if "value" in data:
                return data["value"]
            return data

    return dynamic_item_accessor
__init__(client, workspace_id=None, workspace_data=None)

Initialize workspace.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient instance

required
workspace_id Optional[str]

Workspace identifier (name, GUID, or None for current)

None
workspace_data Optional[dict]

Pre-populated workspace data from API (skips resolution)

None
Behavior
  • If workspace_data provided: Pre-populate attributes (no API call)
  • If GUID provided: Store it without API call (lazy load properties)
  • If name provided: Resolve immediately to get GUID (required for operations)
  • If None or 'default': Resolve to current workspace (Fabric only)

Raises:

Type Description
NotFoundError

If workspace name cannot be found

FabiasError

If not in Fabric and no workspace_id provided

Source code in src/fabias/_fabric/workspace.py
def __init__(
    self,
    client: "FabricClient",
    workspace_id: Optional[str] = None,
    workspace_data: Optional[dict] = None,
):
    """
    Initialize workspace.

    Args:
        client: Authenticated FabricClient instance
        workspace_id: Workspace identifier (name, GUID, or None for current)
        workspace_data: Pre-populated workspace data from API (skips resolution)

    Behavior:
        - If workspace_data provided: Pre-populate attributes (no API call)
        - If GUID provided: Store it without API call (lazy load properties)
        - If name provided: Resolve immediately to get GUID (required for operations)
        - If None or 'default': Resolve to current workspace (Fabric only)

    Raises:
        NotFoundError: If workspace name cannot be found
        FabiasError: If not in Fabric and no workspace_id provided
    """
    self._client = client
    self._workspace_id = workspace_id
    self._data_fetched = False

    # Data attributes (lazy loaded unless resolved by name or pre-populated)
    self._id: Optional[str] = None
    self._name: Optional[str] = None
    self._capacity_id: Optional[str] = None
    self._description: Optional[str] = None

    # Cached sub-objects
    self._git: Optional["Git"] = None
    self._spark: Optional["Spark"] = None
    self._items_cache: dict = {}

    # Pre-populate from provided data
    if workspace_data:
        self._id = workspace_data.get("id")
        self._name = workspace_data.get("displayName") or workspace_data.get("name")
        self._capacity_id = workspace_data.get("capacityId")
        self._description = workspace_data.get("description")
        self._data_fetched = True
        return

    # If GUID provided, store it without API call
    if workspace_id and GUID.valid(workspace_id):
        self._id = workspace_id
        return

    # If name provided (or None/default), resolve immediately to get GUID
    # We need GUID for all subsequent operations
    self._resolve_identifier(workspace_id)
__repr__()

Return string representation.

Source code in src/fabias/_fabric/workspace.py
def __repr__(self) -> str:
    """Return string representation."""
    return f"Workspace(id={self.id}, name={self.name!r})"
create(client, name, capacity, description=None) classmethod

Create a new workspace.

If a workspace with the same name already exists and is in "Active" state, returns the existing workspace. If the workspace exists but is in "Deleted" state, raises an error indicating it must be permanently deleted first.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient instance

required
name str

Display name for the new workspace

required
capacity str

Capacity GUID to host the workspace

required
description Optional[str]

Optional workspace description

None

Returns:

Name Type Description
Workspace Workspace

The newly created or existing active workspace

Raises:

Type Description
FabiasError

If workspace exists in "Deleted" state

Examples:

>>> from fabias.fabric import FabricClient, Workspace
>>> client = FabricClient(auth=auth)
>>> ws = Workspace.create(client, "New Workspace", capacity="...")
Source code in src/fabias/_fabric/workspace.py
@classmethod
@classmethod
def create(  # pragma: no cover
    cls, client: "FabricClient", name: str, capacity: str, description: Optional[str] = None
) -> "Workspace":
    """
    Create a new workspace.

    If a workspace with the same name already exists and is in "Active" state,
    returns the existing workspace. If the workspace exists but is in "Deleted"
    state, raises an error indicating it must be permanently deleted first.

    Args:
        client: Authenticated FabricClient instance
        name: Display name for the new workspace
        capacity: Capacity GUID to host the workspace
        description: Optional workspace description

    Returns:
        Workspace: The newly created or existing active workspace

    Raises:
        FabiasError: If workspace exists in "Deleted" state

    Examples:
        >>> from fabias.fabric import FabricClient, Workspace
        >>> client = FabricClient(auth=auth)
        >>> ws = Workspace.create(client, "New Workspace", capacity="...")
    """
    payload = {"displayName": name, "capacityId": capacity}
    if description is not None:
        payload["description"] = description

    try:
        response = client.post("/workspaces", data=payload)

        if response.status_code == 202:
            operation_id = response.headers.get("x-ms-operation-id")
            raise FabiasError(
                f"Workspace creation started asynchronously (operation: {operation_id}). "
                "Polling not yet implemented."
            )

        workspace_data = response.json()
        workspace_id = workspace_data.get("id")

        # Return new Workspace instance with resolved ID
        return cls(client, workspace_id=workspace_id)
    except Exception as e:
        # If workspace already exists (409), fetch and return it if Active
        from .._shared.exceptions import ApiError

        if (
            (isinstance(e, ApiError) and e.status_code == 409)
            or "409" in str(e)
            or "already exists" in str(e).lower()
        ):
            # Use admin API to get state information
            if client.hasExplicitCredentials:
                try:
                    response = client.get(f"/admin/workspaces?name={name}")
                    data = response.json()
                    workspaces_data = data.get("workspaces", [])

                    for ws_data in workspaces_data:
                        if ws_data.get("name") == name:
                            state = ws_data.get("state", "Active")

                            if state == "Deleted":
                                raise FabiasError(
                                    f"Workspace '{name}' exists in 'Deleted' state. "
                                    "It must be permanently deleted by a Fabric admin "
                                    "before creating a new workspace with this name."
                                )

                            # Active or other state - return the workspace
                            return cls(client, workspace_data=ws_data)
                except FabiasError:
                    raise  # Re-raise our custom error
                except Exception:
                    pass  # Fall through to regular workspaces API

            # Fallback: regular workspaces API (may not have state field)
            workspaces = client.workspaces()
            matches = [w for w in workspaces if w.name == name]

            if matches:
                # Without state field, we can't distinguish deleted workspaces
                # Try to access the workspace to verify it's active
                ws = matches[0]
                try:
                    # Attempt to fetch workspace details - will fail if deleted
                    client.get(f"/workspaces/{ws.id}")
                    return ws
                except Exception:
                    raise FabiasError(
                        f"Workspace '{name}' exists but is not accessible. "
                        "It may be in 'Deleted' state. Use admin API or wait for "
                        "permanent deletion before creating a workspace with this name."
                    )
        raise
delete()

Delete this workspace (soft delete).

The workspace enters a retention period (default 7 days, configurable up to 90 days) during which Fabric administrators can restore it using the restore() method.

Example

ws = fabric.workspace("Old Workspace") ws.delete()

Later, restore it:

ws.restore()

Source code in src/fabias/_fabric/workspace.py
def delete(self) -> None:  # pragma: no cover
    """
    Delete this workspace (soft delete).

    The workspace enters a retention period (default 7 days, configurable up to 90 days)
    during which Fabric administrators can restore it using the restore() method.

    Example:
        >>> ws = fabric.workspace("Old Workspace")
        >>> ws.delete()
        >>> # Later, restore it:
        >>> ws.restore()
    """
    if not self._id:
        raise FabiasError("Cannot delete workspace: ID not resolved")

    self._client.delete(f"/workspaces/{self._id}")

    # Clear local state since workspace no longer exists
    self._id = None
    self._name = None
    self._capacity_id = None
    self._description = None
    self._data_fetched = False
environment(identifier)

Get a specific environment by name or ID.

Parameters:

Name Type Description Default
identifier str

Environment name or GUID

required

Returns:

Name Type Description
Environment Environment

The environment object

Examples:

>>> env = workspace.environment("ML Environment")
Source code in src/fabias/_fabric/workspace.py
def environment(self, identifier: str) -> "Environment":
    """
    Get a specific environment by name or ID.

    Args:
        identifier: Environment name or GUID

    Returns:
        Environment: The environment object

    Examples:
        >>> env = workspace.environment("ML Environment")
    """
    from .items.environment import Environment

    return Environment(self._client, self._get_id(), identifier)
folder(identifier)

Get a specific folder by name or ID.

Parameters:

Name Type Description Default
identifier str

Folder display name or GUID

required

Returns:

Name Type Description
Folder Folder

The folder object

Examples:

>>> folder = workspace.folder("Sales")
>>> print(f"{folder.name} (parent: {folder.parent})")
Source code in src/fabias/_fabric/workspace.py
def folder(self, identifier: str) -> "Folder":
    """
    Get a specific folder by name or ID.

    Args:
        identifier: Folder display name or GUID

    Returns:
        Folder: The folder object

    Examples:
        >>> folder = workspace.folder("Sales")
        >>> print(f"{folder.name} (parent: {folder.parent})")
    """
    from .folders import Folder

    return Folder(self._client, self._get_id(), identifier)
items(item_type=None)

List items in this workspace, converted to specific types.

Returns typed item objects (Pipeline, Notebook, etc.) based on the 'type' field in the API response. Unknown types return base Item.

Parameters:

Name Type Description Default
item_type Optional[Union[ItemType, str]]

Optional filter by type. Can be ItemType enum or string (e.g., ItemType.DATA_PIPELINE, "DataPipeline", ItemType.NOTEBOOK, "Notebook")

None

Returns:

Name Type Description
list list

List of Item objects (Pipeline, Notebook, etc.)

Examples:

All items:

>>> items = workspace.items()
>>> for item in items:
...     print(f"{item.name}: {type(item).__name__}")

Filtered by type:

>>> pipelines = workspace.items(item_type="DataPipeline")
>>> notebooks = workspace.items(item_type="Notebook")
Source code in src/fabias/_fabric/workspace.py
def items(self, item_type: Optional[Union[ItemType, str]] = None) -> list:
    """
    List items in this workspace, converted to specific types.

    Returns typed item objects (Pipeline, Notebook, etc.) based on the
    'type' field in the API response. Unknown types return base Item.

    Args:
        item_type: Optional filter by type. Can be ItemType enum or string
            (e.g., ItemType.DATA_PIPELINE, "DataPipeline", ItemType.NOTEBOOK, "Notebook")

    Returns:
        list: List of Item objects (Pipeline, Notebook, etc.)

    Examples:
        All items:

        >>> items = workspace.items()
        >>> for item in items:
        ...     print(f"{item.name}: {type(item).__name__}")

        Filtered by type:

        >>> pipelines = workspace.items(item_type="DataPipeline")
        >>> notebooks = workspace.items(item_type="Notebook")
    """
    from .items import fromJson

    # Build endpoint with optional type filter
    endpoint = f"/workspaces/{self.id}/items"
    if item_type:
        # Convert enum to string if needed
        type_str = item_type.value if isinstance(item_type, ItemType) else item_type
        endpoint = f"{endpoint}?type={type_str}"

    # Get all items (pagination handled automatically by client)
    response = self._client.get(endpoint)
    data = response.json()

    # Convert JSON to typed objects
    return [
        fromJson(self._client, self._get_id(), item_data) for item_data in data.get("value", [])
    ]
lakehouse(identifier)

Get a specific lakehouse by name or ID.

Parameters:

Name Type Description Default
identifier str

Lakehouse name or GUID

required

Returns:

Name Type Description
Lakehouse Lakehouse

The lakehouse object

Examples:

>>> lakehouse = workspace.lakehouse("Analytics")
Source code in src/fabias/_fabric/workspace.py
def lakehouse(self, identifier: str) -> "Lakehouse":
    """
    Get a specific lakehouse by name or ID.

    Args:
        identifier: Lakehouse name or GUID

    Returns:
        Lakehouse: The lakehouse object

    Examples:
        >>> lakehouse = workspace.lakehouse("Analytics")
    """
    from .items.lakehouse import Lakehouse

    return Lakehouse(self._client, self._get_id(), identifier)
notebook(identifier)

Get a specific notebook by name or ID.

Parameters:

Name Type Description Default
identifier str

Notebook name or GUID

required

Returns:

Name Type Description
Notebook Notebook

The notebook object

Examples:

>>> notebook = workspace.notebook("ETL Script")
Source code in src/fabias/_fabric/workspace.py
def notebook(self, identifier: str) -> "Notebook":
    """
    Get a specific notebook by name or ID.

    Args:
        identifier: Notebook name or GUID

    Returns:
        Notebook: The notebook object

    Examples:
        >>> notebook = workspace.notebook("ETL Script")
    """
    from .items.notebook import Notebook

    return Notebook(self._client, self._get_id(), identifier)
pipeline(identifier)

Get a specific pipeline by name or ID.

Parameters:

Name Type Description Default
identifier str

Pipeline name or GUID

required

Returns:

Name Type Description
Pipeline Pipeline

The pipeline object

Examples:

>>> pipeline = workspace.pipeline("Daily ETL")
>>> job = pipeline.run()
Source code in src/fabias/_fabric/workspace.py
def pipeline(self, identifier: str) -> "Pipeline":
    """
    Get a specific pipeline by name or ID.

    Args:
        identifier: Pipeline name or GUID

    Returns:
        Pipeline: The pipeline object

    Examples:
        >>> pipeline = workspace.pipeline("Daily ETL")
        >>> job = pipeline.run()
    """
    from .items.pipeline import Pipeline

    return Pipeline(self._client, self._get_id(), identifier)
restore(new_admin_principal_id=None, new_name=None)

Restore a deleted workspace.

Requires Fabric Administrator privileges. The workspace must be in "Deleted" state within the retention period (7-90 days, default 7).

Parameters:

Name Type Description Default
new_admin_principal_id Optional[str]

Optional principal ID to assign as workspace admin

None
new_name Optional[str]

Optional new name for the workspace (required for My workspaces)

None

Returns:

Name Type Description
Workspace Workspace

The restored workspace

Raises:

Type Description
FabiasError

If workspace ID not set or restore fails

Examples:

>>> # Restore with original settings
>>> deleted_ws = fabric.workspace(workspace_id)  # By GUID
>>> deleted_ws.restore()
>>> # Restore with new admin
>>> deleted_ws.restore(new_admin_principal_id="user-guid")
>>> # Restore My workspace with new name
>>> deleted_ws.restore(new_name="Restored Workspace")
Source code in src/fabias/_fabric/workspace.py
def restore(
    self,
    new_admin_principal_id: Optional[str] = None,
    new_name: Optional[str] = None,
) -> "Workspace":  # pragma: no cover
    """
    Restore a deleted workspace.

    Requires Fabric Administrator privileges. The workspace must be in "Deleted" state
    within the retention period (7-90 days, default 7).

    Args:
        new_admin_principal_id: Optional principal ID to assign as workspace admin
        new_name: Optional new name for the workspace (required for My workspaces)

    Returns:
        Workspace: The restored workspace

    Raises:
        FabiasError: If workspace ID not set or restore fails

    Examples:
        >>> # Restore with original settings
        >>> deleted_ws = fabric.workspace(workspace_id)  # By GUID
        >>> deleted_ws.restore()

        >>> # Restore with new admin
        >>> deleted_ws.restore(new_admin_principal_id="user-guid")

        >>> # Restore My workspace with new name
        >>> deleted_ws.restore(new_name="Restored Workspace")
    """
    if not self._id:
        raise FabiasError("Cannot restore workspace: ID not resolved")

    payload: dict[str, Any] = {}
    if new_admin_principal_id:
        payload["newWorkspaceAdminPrincipal"] = {
            "id": new_admin_principal_id,
            "type": "User",  # Could also be ServicePrincipal, Group, etc.
        }
    if new_name:
        payload["newWorkspaceName"] = new_name

    # Call admin restore API
    self._client.post(f"/admin/workspaces/{self._id}/restore", data=payload)

    # Refresh workspace data
    self._data_fetched = False
    self._fetch()

    return self
roleAssignment(principal_id)

Get a specific role assignment by principal ID.

This method accepts a principal ID and looks up the corresponding role assignment. If multiple or no assignments are found, raises an error.

Parameters:

Name Type Description Default
principal_id str

Principal GUID (user, group, or service principal)

required

Returns:

Name Type Description
WorkspaceRoleAssignment WorkspaceRoleAssignment

The role assignment

Raises:

Type Description
NotFoundError

If no role assignment found for the principal

FabiasError

If multiple role assignments found (shouldn't happen)

Examples:

>>> ws = fabric.workspace("GENESIS")
>>> assignment = ws.roleAssignment("user-guid")
>>> print(assignment.role)
Source code in src/fabias/_fabric/workspace.py
def roleAssignment(self, principal_id: str) -> "WorkspaceRoleAssignment":
    """
    Get a specific role assignment by principal ID.

    This method accepts a principal ID and looks up the corresponding
    role assignment. If multiple or no assignments are found, raises an error.

    Args:
        principal_id: Principal GUID (user, group, or service principal)

    Returns:
        WorkspaceRoleAssignment: The role assignment

    Raises:
        NotFoundError: If no role assignment found for the principal
        FabiasError: If multiple role assignments found (shouldn't happen)

    Examples:
        >>> ws = fabric.workspace("GENESIS")
        >>> assignment = ws.roleAssignment("user-guid")
        >>> print(assignment.role)
    """
    # List all assignments to find the one with matching principal_id
    assignments = self.roleAssignments()

    # Find assignment with matching principal ID
    matches = [a for a in assignments if a.principalId == principal_id]

    if not matches:
        from .._shared.exceptions import NotFoundError

        raise NotFoundError(
            f"No role assignment found for principal {principal_id}",
            resource_type="WorkspaceRoleAssignment",
            resource_id=principal_id,
        )

    if len(matches) > 1:
        from .._shared.exceptions import FabiasError

        raise FabiasError(
            f"Multiple role assignments found for principal {principal_id}. "
            "This should not happen."
        )

    return cast("WorkspaceRoleAssignment", matches[0])
variableLibrary(identifier)

Get a specific variable library by name or ID.

Parameters:

Name Type Description Default
identifier str

Variable library name or GUID

required

Returns:

Name Type Description
VariableLibrary VariableLibrary

The variable library object

Examples:

>>> lib = workspace.variableLibrary("Deployment Config")
>>> print(f"Active value set: {lib.active_value_set}")
Source code in src/fabias/_fabric/workspace.py
def variableLibrary(self, identifier: str) -> "VariableLibrary":
    """
    Get a specific variable library by name or ID.

    Args:
        identifier: Variable library name or GUID

    Returns:
        VariableLibrary: The variable library object

    Examples:
        >>> lib = workspace.variableLibrary("Deployment Config")
        >>> print(f"Active value set: {lib.active_value_set}")
    """
    from .items.variablelibrary import VariableLibrary

    return VariableLibrary(self._client, self._get_id(), identifier)

WorkspaceAccessor

Accessor for workspace-level operations.

Provides methods to list and create workspaces. Follows the same pattern as PipelineAccessor, LakehouseAccessor, etc.

Supports lazy client loading for module-level usage.

Source code in src/fabias/_fabric/workspace.py
class WorkspaceAccessor:
    """
    Accessor for workspace-level operations.

    Provides methods to list and create workspaces.
    Follows the same pattern as PipelineAccessor, LakehouseAccessor, etc.

    Supports lazy client loading for module-level usage.
    """

    def __init__(self, client_or_getter: Union["FabricClient", Callable[[], "FabricClient"]]):
        """
        Initialize workspace accessor.

        Args:
            client_or_getter: Either a FabricClient instance or a callable that returns one
        """
        if callable(client_or_getter):
            self._client_getter: Optional[Callable[[], "FabricClient"]] = client_or_getter
            self._client: Optional["FabricClient"] = None
        else:
            self._client = client_or_getter
            self._client_getter = None

    def _get_client(self) -> "FabricClient":
        """Get client, lazily loading if needed."""
        if self._client is None:
            if self._client_getter is None:
                raise RuntimeError("No client or client getter available")
            self._client = self._client_getter()
        return self._client

    def __call__(self) -> list["Workspace"]:
        """
        List all accessible workspaces.

        Returns:
            list[Workspace]: List of Workspace objects

        Examples:
            >>> import fabias.fabric as fabric
            >>> for ws in fabric.workspaces():
            ...     print(f"{ws.name}: {ws.id}")
        """
        return self._get_client().workspaces()

    def add(
        self, name: str, capacity: str, description: Optional[str] = None
    ) -> Workspace:  # pragma: no cover
        """
        Create a new workspace.

        If a workspace with the same name already exists and is in "Active" state,
        returns the existing workspace. If the workspace exists but is in "Deleted"
        state, raises an error indicating it must be permanently deleted first.

        Args:
            name: Display name for the new workspace
            capacity: Capacity GUID to host the workspace
            description: Optional workspace description

        Returns:
            Workspace: The newly created or existing active workspace

        Raises:
            FabiasError: If workspace exists in "Deleted" state

        Examples:
            >>> import fabias.fabric as fabric
            >>> new_ws = fabric.workspaces.add("Analytics WS", capacity="...")
        """
        try:
            return Workspace.create(self._get_client(), name, capacity, description)
        except Exception as e:
            # If workspace already exists (409), fetch and return it if Active
            from .._shared.exceptions import ApiError, FabiasError

            if (
                (isinstance(e, ApiError) and e.status_code == 409)
                or "409" in str(e)
                or "already exists" in str(e).lower()
            ):
                # Use admin API to get state information
                client = self._get_client()

                # Try admin API first (has state field)
                if client.hasExplicitCredentials:
                    try:
                        response = client.get(f"/admin/workspaces?name={name}")
                        data = response.json()
                        workspaces_data = data.get("workspaces", [])

                        for ws_data in workspaces_data:
                            if ws_data.get("name") == name:
                                state = ws_data.get("state", "Active")

                                if state == "Deleted":
                                    # Get workspace ID for restore instructions
                                    workspace_id = ws_data.get("id")
                                    raise FabiasError(
                                        f"Workspace '{name}' exists in 'Deleted' state. "
                                        f"Restore it using: fabric.workspaces.restore('{workspace_id}') "
                                        "or permanently delete it via Fabric admin portal."
                                    )

                                # Active or other state - return the workspace
                                return Workspace(client, workspace_data=ws_data)
                    except FabiasError:
                        raise  # Re-raise our custom error
                    except Exception:
                        pass  # Fall through to regular workspaces API

                # Fallback: regular workspaces API (may not have state field)
                workspaces = client.workspaces()
                matches = [w for w in workspaces if w.name == name]

                if matches:
                    # Without state field, we can't distinguish deleted workspaces
                    # Try to access the workspace to verify it's active
                    ws = matches[0]
                    try:
                        # Attempt to fetch workspace details - will fail if deleted
                        client.get(f"/workspaces/{ws.id}")
                        return ws
                    except Exception:
                        raise FabiasError(
                            f"Workspace '{name}' exists but is not accessible. "
                            "It may be in 'Deleted' state. Use admin API or wait for "
                            "permanent deletion before creating a workspace with this name."
                        )
            raise

    def delete(self, name_or_id: str) -> None:  # pragma: no cover
        """
        Delete a workspace by name or GUID (soft delete).

        The workspace enters a retention period (default 7 days, configurable up to 90 days)
        during which Fabric administrators can restore it.

        Args:
            name_or_id: Workspace name or GUID

        Examples:
            >>> fabric.workspaces.delete("Old Workspace")
            >>> fabric.workspaces.delete("abc-123-guid")
        """
        from . import workspace as get_workspace

        # Get the workspace (this resolves name to ID if needed)
        ws = get_workspace(name_or_id)

        if not ws.id:
            from .._shared.exceptions import FabiasError

            raise FabiasError(f"Cannot delete workspace: ID not resolved for '{name_or_id}'")

        self._get_client().delete(f"/workspaces/{ws.id}")

    def restore(
        self,
        workspace_id: str,
        new_admin_principal_id: Optional[str] = None,
        new_name: Optional[str] = None,
    ) -> Workspace:  # pragma: no cover
        """
        Restore a deleted workspace.

        Requires Fabric Administrator privileges. The workspace must be in "Deleted" state
        within the retention period (7-90 days, default 7).

        Args:
            workspace_id: GUID of the deleted workspace
            new_admin_principal_id: Optional principal ID to assign as workspace admin
            new_name: Optional new name for the workspace (required for My workspaces)

        Returns:
            Workspace: The restored workspace

        Examples:
            >>> # Restore with original settings
            >>> ws = fabric.workspaces.restore("workspace-guid")

            >>> # Restore with new admin
            >>> ws = fabric.workspaces.restore(
            ...     "workspace-guid",
            ...     new_admin_principal_id="user-guid"
            ... )

            >>> # Restore My workspace with new name
            >>> ws = fabric.workspaces.restore(
            ...     "workspace-guid",
            ...     new_name="Restored Workspace"
            ... )
        """
        payload: dict[str, Any] = {}
        if new_admin_principal_id:
            payload["newWorkspaceAdminPrincipal"] = {
                "id": new_admin_principal_id,
                "type": "User",
            }
        if new_name:
            payload["newWorkspaceName"] = new_name

        # Call admin restore API
        self._get_client().post(f"/admin/workspaces/{workspace_id}/restore", data=payload)

        # Return restored workspace
        return Workspace(self._get_client(), workspace_id=workspace_id)
Functions
__call__()

List all accessible workspaces.

Returns:

Type Description
list[Workspace]

list[Workspace]: List of Workspace objects

Examples:

>>> import fabias.fabric as fabric
>>> for ws in fabric.workspaces():
...     print(f"{ws.name}: {ws.id}")
Source code in src/fabias/_fabric/workspace.py
def __call__(self) -> list["Workspace"]:
    """
    List all accessible workspaces.

    Returns:
        list[Workspace]: List of Workspace objects

    Examples:
        >>> import fabias.fabric as fabric
        >>> for ws in fabric.workspaces():
        ...     print(f"{ws.name}: {ws.id}")
    """
    return self._get_client().workspaces()
__init__(client_or_getter)

Initialize workspace accessor.

Parameters:

Name Type Description Default
client_or_getter Union[FabricClient, Callable[[], FabricClient]]

Either a FabricClient instance or a callable that returns one

required
Source code in src/fabias/_fabric/workspace.py
def __init__(self, client_or_getter: Union["FabricClient", Callable[[], "FabricClient"]]):
    """
    Initialize workspace accessor.

    Args:
        client_or_getter: Either a FabricClient instance or a callable that returns one
    """
    if callable(client_or_getter):
        self._client_getter: Optional[Callable[[], "FabricClient"]] = client_or_getter
        self._client: Optional["FabricClient"] = None
    else:
        self._client = client_or_getter
        self._client_getter = None
add(name, capacity, description=None)

Create a new workspace.

If a workspace with the same name already exists and is in "Active" state, returns the existing workspace. If the workspace exists but is in "Deleted" state, raises an error indicating it must be permanently deleted first.

Parameters:

Name Type Description Default
name str

Display name for the new workspace

required
capacity str

Capacity GUID to host the workspace

required
description Optional[str]

Optional workspace description

None

Returns:

Name Type Description
Workspace Workspace

The newly created or existing active workspace

Raises:

Type Description
FabiasError

If workspace exists in "Deleted" state

Examples:

>>> import fabias.fabric as fabric
>>> new_ws = fabric.workspaces.add("Analytics WS", capacity="...")
Source code in src/fabias/_fabric/workspace.py
def add(
    self, name: str, capacity: str, description: Optional[str] = None
) -> Workspace:  # pragma: no cover
    """
    Create a new workspace.

    If a workspace with the same name already exists and is in "Active" state,
    returns the existing workspace. If the workspace exists but is in "Deleted"
    state, raises an error indicating it must be permanently deleted first.

    Args:
        name: Display name for the new workspace
        capacity: Capacity GUID to host the workspace
        description: Optional workspace description

    Returns:
        Workspace: The newly created or existing active workspace

    Raises:
        FabiasError: If workspace exists in "Deleted" state

    Examples:
        >>> import fabias.fabric as fabric
        >>> new_ws = fabric.workspaces.add("Analytics WS", capacity="...")
    """
    try:
        return Workspace.create(self._get_client(), name, capacity, description)
    except Exception as e:
        # If workspace already exists (409), fetch and return it if Active
        from .._shared.exceptions import ApiError, FabiasError

        if (
            (isinstance(e, ApiError) and e.status_code == 409)
            or "409" in str(e)
            or "already exists" in str(e).lower()
        ):
            # Use admin API to get state information
            client = self._get_client()

            # Try admin API first (has state field)
            if client.hasExplicitCredentials:
                try:
                    response = client.get(f"/admin/workspaces?name={name}")
                    data = response.json()
                    workspaces_data = data.get("workspaces", [])

                    for ws_data in workspaces_data:
                        if ws_data.get("name") == name:
                            state = ws_data.get("state", "Active")

                            if state == "Deleted":
                                # Get workspace ID for restore instructions
                                workspace_id = ws_data.get("id")
                                raise FabiasError(
                                    f"Workspace '{name}' exists in 'Deleted' state. "
                                    f"Restore it using: fabric.workspaces.restore('{workspace_id}') "
                                    "or permanently delete it via Fabric admin portal."
                                )

                            # Active or other state - return the workspace
                            return Workspace(client, workspace_data=ws_data)
                except FabiasError:
                    raise  # Re-raise our custom error
                except Exception:
                    pass  # Fall through to regular workspaces API

            # Fallback: regular workspaces API (may not have state field)
            workspaces = client.workspaces()
            matches = [w for w in workspaces if w.name == name]

            if matches:
                # Without state field, we can't distinguish deleted workspaces
                # Try to access the workspace to verify it's active
                ws = matches[0]
                try:
                    # Attempt to fetch workspace details - will fail if deleted
                    client.get(f"/workspaces/{ws.id}")
                    return ws
                except Exception:
                    raise FabiasError(
                        f"Workspace '{name}' exists but is not accessible. "
                        "It may be in 'Deleted' state. Use admin API or wait for "
                        "permanent deletion before creating a workspace with this name."
                    )
        raise
delete(name_or_id)

Delete a workspace by name or GUID (soft delete).

The workspace enters a retention period (default 7 days, configurable up to 90 days) during which Fabric administrators can restore it.

Parameters:

Name Type Description Default
name_or_id str

Workspace name or GUID

required

Examples:

>>> fabric.workspaces.delete("Old Workspace")
>>> fabric.workspaces.delete("abc-123-guid")
Source code in src/fabias/_fabric/workspace.py
def delete(self, name_or_id: str) -> None:  # pragma: no cover
    """
    Delete a workspace by name or GUID (soft delete).

    The workspace enters a retention period (default 7 days, configurable up to 90 days)
    during which Fabric administrators can restore it.

    Args:
        name_or_id: Workspace name or GUID

    Examples:
        >>> fabric.workspaces.delete("Old Workspace")
        >>> fabric.workspaces.delete("abc-123-guid")
    """
    from . import workspace as get_workspace

    # Get the workspace (this resolves name to ID if needed)
    ws = get_workspace(name_or_id)

    if not ws.id:
        from .._shared.exceptions import FabiasError

        raise FabiasError(f"Cannot delete workspace: ID not resolved for '{name_or_id}'")

    self._get_client().delete(f"/workspaces/{ws.id}")
restore(workspace_id, new_admin_principal_id=None, new_name=None)

Restore a deleted workspace.

Requires Fabric Administrator privileges. The workspace must be in "Deleted" state within the retention period (7-90 days, default 7).

Parameters:

Name Type Description Default
workspace_id str

GUID of the deleted workspace

required
new_admin_principal_id Optional[str]

Optional principal ID to assign as workspace admin

None
new_name Optional[str]

Optional new name for the workspace (required for My workspaces)

None

Returns:

Name Type Description
Workspace Workspace

The restored workspace

Examples:

>>> # Restore with original settings
>>> ws = fabric.workspaces.restore("workspace-guid")
>>> # Restore with new admin
>>> ws = fabric.workspaces.restore(
...     "workspace-guid",
...     new_admin_principal_id="user-guid"
... )
>>> # Restore My workspace with new name
>>> ws = fabric.workspaces.restore(
...     "workspace-guid",
...     new_name="Restored Workspace"
... )
Source code in src/fabias/_fabric/workspace.py
def restore(
    self,
    workspace_id: str,
    new_admin_principal_id: Optional[str] = None,
    new_name: Optional[str] = None,
) -> Workspace:  # pragma: no cover
    """
    Restore a deleted workspace.

    Requires Fabric Administrator privileges. The workspace must be in "Deleted" state
    within the retention period (7-90 days, default 7).

    Args:
        workspace_id: GUID of the deleted workspace
        new_admin_principal_id: Optional principal ID to assign as workspace admin
        new_name: Optional new name for the workspace (required for My workspaces)

    Returns:
        Workspace: The restored workspace

    Examples:
        >>> # Restore with original settings
        >>> ws = fabric.workspaces.restore("workspace-guid")

        >>> # Restore with new admin
        >>> ws = fabric.workspaces.restore(
        ...     "workspace-guid",
        ...     new_admin_principal_id="user-guid"
        ... )

        >>> # Restore My workspace with new name
        >>> ws = fabric.workspaces.restore(
        ...     "workspace-guid",
        ...     new_name="Restored Workspace"
        ... )
    """
    payload: dict[str, Any] = {}
    if new_admin_principal_id:
        payload["newWorkspaceAdminPrincipal"] = {
            "id": new_admin_principal_id,
            "type": "User",
        }
    if new_name:
        payload["newWorkspaceName"] = new_name

    # Call admin restore API
    self._get_client().post(f"/admin/workspaces/{workspace_id}/restore", data=payload)

    # Return restored workspace
    return Workspace(self._get_client(), workspace_id=workspace_id)

WorkspaceRoleAssignment

Bases: BaseRoleAssignment

Represents a role assignment for a workspace.

Inherits from BaseRoleAssignment to provide: - principal (dict): Principal information (user/group/service principal) - role (str): Assigned role (e.g., 'Admin', 'Member', 'Contributor', 'Viewer') - principal_id, principal_type, principal_name properties

Workspace-specific roles: - Admin: Full control over the workspace - Member: Can view, edit, and share workspace items - Contributor: Can view and edit workspace items - Viewer: Read-only access to workspace items

Source code in src/fabias/_fabric/workspace.py
class WorkspaceRoleAssignment(BaseRoleAssignment):
    """
    Represents a role assignment for a workspace.

    Inherits from BaseRoleAssignment to provide:
    - principal (dict): Principal information (user/group/service principal)
    - role (str): Assigned role (e.g., 'Admin', 'Member', 'Contributor', 'Viewer')
    - principal_id, principal_type, principal_name properties

    Workspace-specific roles:
    - Admin: Full control over the workspace
    - Member: Can view, edit, and share workspace items
    - Contributor: Can view and edit workspace items
    - Viewer: Read-only access to workspace items
    """

    pass

WorkspaceRoleAssignmentAccessor

Bases: BaseRoleAssignmentAccessor

Accessor for workspace role assignment operations.

Inherits from BaseRoleAssignmentAccessor to provide: - call(): List all role assignments - add(principal_id, principal_type, role): Add role assignment (with 409 handling) - update(principal_id, role): Update role assignment - delete(principal_id): Delete role assignment

Source code in src/fabias/_fabric/workspace.py
class WorkspaceRoleAssignmentAccessor(BaseRoleAssignmentAccessor):
    """
    Accessor for workspace role assignment operations.

    Inherits from BaseRoleAssignmentAccessor to provide:
    - __call__(): List all role assignments
    - add(principal_id, principal_type, role): Add role assignment (with 409 handling)
    - update(principal_id, role): Update role assignment
    - delete(principal_id): Delete role assignment
    """

    def __init__(self, client: "FabricClient", workspace_id: str):
        """Initialize role assignment accessor for a workspace."""
        endpoint = f"/workspaces/{workspace_id}/roleAssignments"
        super().__init__(client, endpoint, WorkspaceRoleAssignment)
Functions
__init__(client, workspace_id)

Initialize role assignment accessor for a workspace.

Source code in src/fabias/_fabric/workspace.py
def __init__(self, client: "FabricClient", workspace_id: str):
    """Initialize role assignment accessor for a workspace."""
    endpoint = f"/workspaces/{workspace_id}/roleAssignments"
    super().__init__(client, endpoint, WorkspaceRoleAssignment)

fabias.workspaces = WorkspaceAccessor(_get_client) module-attribute

fabias.connection(name_or_id)

Get a specific connection by name or ID.

Searches tenant-wide connections by name (case-insensitive partial match). If only one match is found, returns it. If multiple matches or no matches, raises an error.

Parameters:

Name Type Description Default
name_or_id str

Connection name or GUID

required

Returns:

Name Type Description
Connection Connection

The connection object

Raises:

Type Description
NotFoundError

If no connection matches the name/ID

FabiasError

If multiple connections match the name

Examples:

By name:

>>> import fabias._fabric as fabric
>>> conn = fabric.connection("SQL Server")

By GUID:

>>> conn = fabric.connection("2db03b89-1737-4dc8-ba25-ee866e70c3e9")
Source code in src/fabias/_fabric/__init__.py
def connection(name_or_id: str) -> "Connection":
    """
    Get a specific connection by name or ID.

    Searches tenant-wide connections by name (case-insensitive partial match).
    If only one match is found, returns it. If multiple matches or no matches,
    raises an error.

    Args:
        name_or_id: Connection name or GUID

    Returns:
        Connection: The connection object

    Raises:
        NotFoundError: If no connection matches the name/ID
        FabiasError: If multiple connections match the name

    Examples:
        By name:

        >>> import fabias._fabric as fabric
        >>> conn = fabric.connection("SQL Server")

        By GUID:

        >>> conn = fabric.connection("2db03b89-1737-4dc8-ba25-ee866e70c3e9")
    """
    from .._shared.exceptions import FabiasError, NotFoundError

    # Get accessor via __getattr__ (lazy loaded)
    _connections = __getattr__("connections")

    # Try getting by ID first
    try:
        return _connections.get(name_or_id)
    except Exception:
        pass

    # Search by name
    matches = _connections(name=name_or_id)

    if not matches:
        raise NotFoundError(
            f"Connection '{name_or_id}' not found",
            resource_type="Connection",
            resource_id=name_or_id,
        )

    # If exact match exists, use it
    exact_matches = [c for c in matches if c.name == name_or_id]
    if len(exact_matches) == 1:
        return exact_matches[0]

    # Otherwise, require single match
    if len(matches) > 1:
        raise FabiasError(
            f"Multiple connections found matching '{name_or_id}': "
            + ", ".join(c.name for c in matches[:5])
        )

    return matches[0]

fabias.connections

Connections management for Microsoft Fabric workspaces.

Provides functionality for managing data connections including on-premises, virtual network, and cloud connections.

Classes

Connection

Represents a Fabric connection.

Attributes:

Name Type Description
id str

Connection unique identifier

name str

Connection display name

connectionType str

Type of connection

connectivityType str

Connectivity type (OnPremises, VirtualNetwork, Cloud)

privacyLevel str

Privacy level setting

credentialDetails dict

Credential configuration details

Source code in src/fabias/_fabric/connections.py
class Connection:
    """
    Represents a Fabric connection.

    Attributes:
        id (str): Connection unique identifier
        name (str): Connection display name
        connectionType (str): Type of connection
        connectivityType (str): Connectivity type (OnPremises, VirtualNetwork, Cloud)
        privacyLevel (str): Privacy level setting
        credentialDetails (dict): Credential configuration details
    """

    def __init__(
        self,
        client: "FabricClient",
        identifier: Optional[str] = None,
        connection_data: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize Connection.

        Args:
            client: Authenticated FabricClient
            identifier: Connection name or GUID (triggers lazy loading or immediate resolution)
            connection_data: Pre-populated connection data from API (skips resolution)
        """
        self._client = client
        self._data_fetched = False

        # Attributes (lazy loaded)
        self._id: Optional[str] = None
        self._name: Optional[str] = None
        self._connection_type: Optional[str] = None
        self._connectivity_type: Optional[str] = None
        self._privacy_level: Optional[str] = None
        self._credential_details: Optional[dict] = None

        # Pre-populated from API response
        if connection_data:
            self._id = connection_data.get("id")
            self._name = connection_data.get("displayName")
            self._connection_type = connection_data.get("connectivityType")
            self._connectivity_type = connection_data.get("connectivityType")
            self._privacy_level = connection_data.get("privacyLevel")
            self._credential_details = connection_data.get("credentialDetails", {})
            self._data_fetched = True
            return

        # GUID provided - store and return (no API call)
        if identifier and GUID.valid(identifier):
            self._id = identifier
            return

        # Name provided - resolve to GUID (API call happens here)
        if identifier:
            self._resolve_identifier(identifier)

    def _resolve_identifier(self, name: str) -> None:
        """
        Resolve connection name to GUID.

        Searches all connections and populates all data since we're making an API call.

        Args:
            name: Connection display name
        """
        # Search by name using Connections handler
        from .connections import Connections

        handler = Connections(self._client)
        matches = handler.list(name=name)

        if not matches:
            from .._shared.exceptions import NotFoundError

            raise NotFoundError(
                f"Connection '{name}' not found", resource_type="Connection", resource_id=name
            )

        # Exact match preferred
        exact = [c for c in matches if c.name == name]
        if len(exact) == 1:
            matched = exact[0]
        elif len(matches) == 1:
            matched = matches[0]
        else:
            from .._shared.exceptions import FabiasError

            raise FabiasError(
                f"Multiple connections found matching '{name}': "
                + ", ".join(c.name for c in matches[:5])
            )

        # Copy all properties from matched connection
        self._id = matched.id
        self._name = matched.name
        self._connection_type = matched.connectionType
        self._connectivity_type = matched.connectivityType
        self._privacy_level = matched.privacyLevel
        self._credential_details = matched.credentialDetails
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load connection data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/connections/{self._id}")
        data = response.json()

        self._name = data.get("displayName")
        self._connection_type = data.get("connectivityType")
        self._connectivity_type = data.get("connectivityType")
        self._privacy_level = data.get("privacyLevel")
        self._credential_details = data.get("credentialDetails", {})
        self._data_fetched = True

    @property
    def id(self) -> Optional[str]:
        """Connection ID is always available (set in __init__)."""
        return self._id

    @property
    def name(self) -> Optional[str]:
        """Connection display name (lazy loaded)."""
        self._fetch()
        return self._name

    @property
    def connectionType(self) -> Optional[str]:
        """Connection type (lazy loaded)."""
        self._fetch()
        return self._connection_type

    @property
    def connectivityType(self) -> Optional[str]:
        """Connectivity type (lazy loaded)."""
        self._fetch()
        return self._connectivity_type

    @property
    def privacyLevel(self) -> Optional[str]:
        """Privacy level (lazy loaded)."""
        self._fetch()
        return self._privacy_level

    @property
    def credentialDetails(self) -> Optional[dict]:
        """Credential details (lazy loaded)."""
        self._fetch()
        return self._credential_details

    def refresh(self) -> "Connection":
        """
        Force refresh connection data from API.

        Returns:
            Connection: Self for method chaining

        Examples:
            >>> conn = fabric.connection("SQL Server")
            >>> conn.refresh()  # Get latest data from API
        """
        self._data_fetched = False
        self._fetch()
        return self

    def roleAssignment(self, principal_id: str) -> "ConnectionRoleAssignment":
        """
        Get a specific role assignment by principal ID.

        This method accepts a principal ID and looks up the corresponding
        role assignment. If multiple or no assignments are found, raises an error.

        Args:
            principal_id: Principal GUID (user, group, or service principal)

        Returns:
            ConnectionRoleAssignment: The role assignment

        Raises:
            NotFoundError: If no role assignment found for the principal
            FabiasError: If multiple role assignments found (shouldn't happen)

        Examples:
            >>> conn = fabric.connection("SQL Server")
            >>> assignment = conn.roleAssignment("user-guid")
            >>> print(assignment.role)
        """
        # List all assignments to find the one with matching principal_id
        assignments = self.roleAssignments()

        # Find assignment with matching principal ID
        matches = [a for a in assignments if a.principalId == principal_id]

        if not matches:
            from .._shared.exceptions import NotFoundError

            raise NotFoundError(
                f"No role assignment found for principal {principal_id}",
                resource_type="ConnectionRoleAssignment",
                resource_id=principal_id,
            )

        if len(matches) > 1:
            from .._shared.exceptions import FabiasError

            raise FabiasError(
                f"Multiple role assignments found for principal {principal_id}. "
                "This should not happen."
            )

        return cast("ConnectionRoleAssignment", matches[0])

    @property
    def roleAssignments(self) -> "RoleAssignmentAccessor":
        """
        Access role assignments for this connection.

        Returns:
            RoleAssignmentAccessor: Accessor for listing and managing role assignments

        Examples:
            List role assignments:

            >>> conn = fabric.connection("SQL Server")
            >>> for assignment in conn.roleAssignments():
            ...     print(f"{assignment.principalName}: {assignment.role}")

            Add role assignment:

            >>> conn.roleAssignments.add(
            ...     principal_id="user-guid",
            ...     principal_type="User",
            ...     role="Admin"
            ... )
        """
        if not self.id:
            raise ValueError("Connection ID not available")
        return RoleAssignmentAccessor(self._client, self.id)

    def __repr__(self) -> str:
        return f"Connection(id={self.id}, " f"name={self.name}, " f"type={self.connectionType})"
Attributes
connectionType property

Connection type (lazy loaded).

connectivityType property

Connectivity type (lazy loaded).

credentialDetails property

Credential details (lazy loaded).

id property

Connection ID is always available (set in init).

name property

Connection display name (lazy loaded).

privacyLevel property

Privacy level (lazy loaded).

roleAssignments property

Access role assignments for this connection.

Returns:

Name Type Description
RoleAssignmentAccessor RoleAssignmentAccessor

Accessor for listing and managing role assignments

Examples:

List role assignments:

>>> conn = fabric.connection("SQL Server")
>>> for assignment in conn.roleAssignments():
...     print(f"{assignment.principalName}: {assignment.role}")

Add role assignment:

>>> conn.roleAssignments.add(
...     principal_id="user-guid",
...     principal_type="User",
...     role="Admin"
... )
Functions
__init__(client, identifier=None, connection_data=None)

Initialize Connection.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
identifier Optional[str]

Connection name or GUID (triggers lazy loading or immediate resolution)

None
connection_data Optional[Dict[str, Any]]

Pre-populated connection data from API (skips resolution)

None
Source code in src/fabias/_fabric/connections.py
def __init__(
    self,
    client: "FabricClient",
    identifier: Optional[str] = None,
    connection_data: Optional[Dict[str, Any]] = None,
):
    """
    Initialize Connection.

    Args:
        client: Authenticated FabricClient
        identifier: Connection name or GUID (triggers lazy loading or immediate resolution)
        connection_data: Pre-populated connection data from API (skips resolution)
    """
    self._client = client
    self._data_fetched = False

    # Attributes (lazy loaded)
    self._id: Optional[str] = None
    self._name: Optional[str] = None
    self._connection_type: Optional[str] = None
    self._connectivity_type: Optional[str] = None
    self._privacy_level: Optional[str] = None
    self._credential_details: Optional[dict] = None

    # Pre-populated from API response
    if connection_data:
        self._id = connection_data.get("id")
        self._name = connection_data.get("displayName")
        self._connection_type = connection_data.get("connectivityType")
        self._connectivity_type = connection_data.get("connectivityType")
        self._privacy_level = connection_data.get("privacyLevel")
        self._credential_details = connection_data.get("credentialDetails", {})
        self._data_fetched = True
        return

    # GUID provided - store and return (no API call)
    if identifier and GUID.valid(identifier):
        self._id = identifier
        return

    # Name provided - resolve to GUID (API call happens here)
    if identifier:
        self._resolve_identifier(identifier)
refresh()

Force refresh connection data from API.

Returns:

Name Type Description
Connection Connection

Self for method chaining

Examples:

>>> conn = fabric.connection("SQL Server")
>>> conn.refresh()  # Get latest data from API
Source code in src/fabias/_fabric/connections.py
def refresh(self) -> "Connection":
    """
    Force refresh connection data from API.

    Returns:
        Connection: Self for method chaining

    Examples:
        >>> conn = fabric.connection("SQL Server")
        >>> conn.refresh()  # Get latest data from API
    """
    self._data_fetched = False
    self._fetch()
    return self
roleAssignment(principal_id)

Get a specific role assignment by principal ID.

This method accepts a principal ID and looks up the corresponding role assignment. If multiple or no assignments are found, raises an error.

Parameters:

Name Type Description Default
principal_id str

Principal GUID (user, group, or service principal)

required

Returns:

Name Type Description
ConnectionRoleAssignment ConnectionRoleAssignment

The role assignment

Raises:

Type Description
NotFoundError

If no role assignment found for the principal

FabiasError

If multiple role assignments found (shouldn't happen)

Examples:

>>> conn = fabric.connection("SQL Server")
>>> assignment = conn.roleAssignment("user-guid")
>>> print(assignment.role)
Source code in src/fabias/_fabric/connections.py
def roleAssignment(self, principal_id: str) -> "ConnectionRoleAssignment":
    """
    Get a specific role assignment by principal ID.

    This method accepts a principal ID and looks up the corresponding
    role assignment. If multiple or no assignments are found, raises an error.

    Args:
        principal_id: Principal GUID (user, group, or service principal)

    Returns:
        ConnectionRoleAssignment: The role assignment

    Raises:
        NotFoundError: If no role assignment found for the principal
        FabiasError: If multiple role assignments found (shouldn't happen)

    Examples:
        >>> conn = fabric.connection("SQL Server")
        >>> assignment = conn.roleAssignment("user-guid")
        >>> print(assignment.role)
    """
    # List all assignments to find the one with matching principal_id
    assignments = self.roleAssignments()

    # Find assignment with matching principal ID
    matches = [a for a in assignments if a.principalId == principal_id]

    if not matches:
        from .._shared.exceptions import NotFoundError

        raise NotFoundError(
            f"No role assignment found for principal {principal_id}",
            resource_type="ConnectionRoleAssignment",
            resource_id=principal_id,
        )

    if len(matches) > 1:
        from .._shared.exceptions import FabiasError

        raise FabiasError(
            f"Multiple role assignments found for principal {principal_id}. "
            "This should not happen."
        )

    return cast("ConnectionRoleAssignment", matches[0])

ConnectionRoleAssignment

Bases: BaseRoleAssignment

Represents a role assignment for a connection.

Inherits from BaseRoleAssignment to provide: - principal (dict): Principal information (user/group/service principal) - role (str): Assigned role (e.g., 'User', 'Admin', 'Owner') - principal_id, principal_type, principal_name properties

Connection-specific roles: - User: Can use the connection - UserWithReshare: Can use and share the connection - Owner: Full control over the connection

Source code in src/fabias/_fabric/connections.py
class ConnectionRoleAssignment(BaseRoleAssignment):
    """
    Represents a role assignment for a connection.

    Inherits from BaseRoleAssignment to provide:
    - principal (dict): Principal information (user/group/service principal)
    - role (str): Assigned role (e.g., 'User', 'Admin', 'Owner')
    - principal_id, principal_type, principal_name properties

    Connection-specific roles:
    - User: Can use the connection
    - UserWithReshare: Can use and share the connection
    - Owner: Full control over the connection
    """

    pass

Connections

Manages connections for Microsoft Fabric (tenant-wide).

Provides methods to: - List, create, get, update, and delete connections - Manage connection role assignments - Get supported connection types

Note: Connections are tenant-wide resources, not workspace-scoped.

Examples:

>>> import fabias.fabric as fabric
>>> connections = Connections(fabric.client())
>>>
>>> # List all connections
>>> for conn in connections.list():
...     print(f"{conn.name}: {conn.connectionType}")
>>>
>>> # Create a connection
>>> conn = connections.create(
...     name="MyDataSource",
...     connection_type="Sql",
...     connectivity_type="OnPremises",
...     privacy_level="Organizational"
... )
>>>
>>> # Get a connection
>>> conn = connections.get("connection-id")
>>>
>>> # Update a connection
>>> connections.update("connection-id", display_name="NewName")
>>>
>>> # Delete a connection
>>> connections.delete("connection-id")
Source code in src/fabias/_fabric/connections.py
class Connections:
    """
    Manages connections for Microsoft Fabric (tenant-wide).

    Provides methods to:
    - List, create, get, update, and delete connections
    - Manage connection role assignments
    - Get supported connection types

    Note: Connections are tenant-wide resources, not workspace-scoped.

    Examples:
        >>> import fabias.fabric as fabric
        >>> connections = Connections(fabric.client())
        >>>
        >>> # List all connections
        >>> for conn in connections.list():
        ...     print(f"{conn.name}: {conn.connectionType}")
        >>>
        >>> # Create a connection
        >>> conn = connections.create(
        ...     name="MyDataSource",
        ...     connection_type="Sql",
        ...     connectivity_type="OnPremises",
        ...     privacy_level="Organizational"
        ... )
        >>>
        >>> # Get a connection
        >>> conn = connections.get("connection-id")
        >>>
        >>> # Update a connection
        >>> connections.update("connection-id", display_name="NewName")
        >>>
        >>> # Delete a connection
        >>> connections.delete("connection-id")
    """

    def __init__(self, client: "FabricClient"):
        """
        Initialize Connections handler.

        Args:
            client: Authenticated FabricClient
        """
        self._client = client
        self._endpoint = "/connections"

    def list(self, name: Optional[str] = None) -> List[Connection]:
        """
        List all connections (tenant-wide).

        Args:
            name: Optional filter by connection name (case-insensitive partial match)

        Returns:
            List[Connection]: List of connection objects

        Examples:
            >>> connections = Connections(client)
            >>> for conn in connections.list():
            ...     print(conn.name)

            >>> # Search by name
            >>> sql_conns = connections.list(name="SQL")
        """
        response = self._client.get(self._endpoint)
        data = response.json()

        connections = [
            Connection(self._client, connection_data=item) for item in data.get("value", [])
        ]

        # Filter by name if provided
        if name:
            name_lower = name.lower()
            connections = [conn for conn in connections if name_lower in (conn.name or "").lower()]

        return connections

    def get(self, connection_id: str) -> Connection:
        """
        Get a specific connection by ID.

        Args:
            connection_id: Connection GUID

        Returns:
            Connection: The connection object

        Examples:
            >>> conn = connections.get("abc-123")
            >>> print(conn.name)
        """
        response = self._client.get(f"{self._endpoint}/{connection_id}")
        data = response.json()

        return Connection(self._client, connection_data=data)

    def create(  # pragma: no cover
        self,
        name: str,
        connection_type: str,
        connectivity_type: Union[ConnectivityType, str] = "Cloud",
        privacy_level: Union[PrivacyLevel, str] = "Organizational",
        **kwargs: Any,
    ) -> Connection:
        """
        Create a new connection.

        Args:
            name: Connection display name
            connection_type: Type of connection (e.g., 'Sql', 'AzureBlob', 'SharePoint')
            connectivity_type: ConnectivityType enum or string ('OnPremises', 'VirtualNetwork', 'Cloud')
            privacy_level: PrivacyLevel enum or string ('None', 'Public', 'Organizational', 'Private')
            **kwargs: Additional connection properties

        Returns:
            Connection: The created connection object

        Examples:
            Using enums (recommended):

            >>> from fabias.fabric.enums import ConnectivityType, PrivacyLevel
            >>> conn = connections.create(
            ...     name="SQL Server",
            ...     connection_type="Sql",
            ...     connectivity_type=ConnectivityType.ON_PREMISES_GATEWAY,
            ...     privacy_level=PrivacyLevel.ORGANIZATIONAL
            ... )

            Using strings:

            >>> conn = connections.create(
            ...     name="SQL Server",
            ...     connection_type="Sql",
            ...     connectivity_type="OnPremises",
            ...     privacy_level="Organizational"
            ... )
        """
        # Convert enums to strings if needed
        connectivity_str = (
            connectivity_type.value
            if isinstance(connectivity_type, ConnectivityType)
            else connectivity_type
        )
        privacy_str = (
            privacy_level.value if isinstance(privacy_level, PrivacyLevel) else privacy_level
        )

        payload = {
            "displayName": name,
            "connectionType": connection_type,
            "connectivityType": connectivity_str,
            "privacyLevel": privacy_str,
            **kwargs,
        }

        response = self._client.post(self._endpoint, data=payload)
        data = response.json()

        return Connection(self._client, connection_data=data)

    def update(  # pragma: no cover
        self,
        connection_id: str,
        display_name: Optional[str] = None,
        privacy_level: Optional[Union[PrivacyLevel, str]] = None,
        **kwargs: Any,
    ) -> Connection:
        """
        Update an existing connection.

        Args:
            connection_id: Connection GUID
            display_name: New display name (optional)
            privacy_level: PrivacyLevel enum or string (optional)
            **kwargs: Additional properties to update

        Returns:
            Connection: The updated connection object

        Examples:
            Using enum:

            >>> from fabias.fabric.enums import PrivacyLevel
            >>> conn = connections.update(
            ...     "abc-123",
            ...     display_name="Updated Name",
            ...     privacy_level=PrivacyLevel.PRIVATE
            ... )

            Using string:

            >>> conn = connections.update(
            ...     "abc-123",
            ...     display_name="Updated Name",
            ...     privacy_level="Private"
            ... )
        """
        payload = {**kwargs}

        if display_name is not None:
            payload["displayName"] = display_name
        if privacy_level is not None:
            # Convert enum to string if needed
            privacy_str = (
                privacy_level.value if isinstance(privacy_level, PrivacyLevel) else privacy_level
            )
            payload["privacyLevel"] = privacy_str

        response = self._client.patch(f"{self._endpoint}/{connection_id}", data=payload)
        data = response.json()

        return Connection(self._client, connection_data=data)

    def delete(self, name_or_id: str) -> None:  # pragma: no cover
        """
        Delete a connection by name or GUID.

        Args:
            name_or_id: Connection name or GUID

        Examples:
            >>> fabric.connections.delete("Old Connection")
            >>> fabric.connections.delete("abc-123-guid")
        """
        # Try as GUID first
        if GUID.valid(name_or_id):
            self._get_client().delete(f"{self._endpoint}/{name_or_id}")
            return

        # Resolve name to connection and delete by ID
        from . import connection as get_connection

        conn = get_connection(name_or_id)
        if conn and conn.id:
            self._get_client().delete(f"{self._endpoint}/{conn.id}")

    def supportedTypes(self) -> List[Dict[str, Any]]:
        """
        List supported connection types.

        Returns:
            List[dict]: List of supported connection type configurations

        Examples:
            >>> types = connections.supported_types()
            >>> for conn_type in types:
            ...     print(conn_type.get('name'))
        """
        response = self._client.get(f"{self._endpoint}/supportedTypes")
        data = response.json()

        return data.get("value", [])

    def __repr__(self) -> str:
        return "Connections(tenant-wide)"
Functions
__init__(client)

Initialize Connections handler.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
Source code in src/fabias/_fabric/connections.py
def __init__(self, client: "FabricClient"):
    """
    Initialize Connections handler.

    Args:
        client: Authenticated FabricClient
    """
    self._client = client
    self._endpoint = "/connections"
create(name, connection_type, connectivity_type='Cloud', privacy_level='Organizational', **kwargs)

Create a new connection.

Parameters:

Name Type Description Default
name str

Connection display name

required
connection_type str

Type of connection (e.g., 'Sql', 'AzureBlob', 'SharePoint')

required
connectivity_type Union[ConnectivityType, str]

ConnectivityType enum or string ('OnPremises', 'VirtualNetwork', 'Cloud')

'Cloud'
privacy_level Union[PrivacyLevel, str]

PrivacyLevel enum or string ('None', 'Public', 'Organizational', 'Private')

'Organizational'
**kwargs Any

Additional connection properties

{}

Returns:

Name Type Description
Connection Connection

The created connection object

Examples:

Using enums (recommended):

>>> from fabias.fabric.enums import ConnectivityType, PrivacyLevel
>>> conn = connections.create(
...     name="SQL Server",
...     connection_type="Sql",
...     connectivity_type=ConnectivityType.ON_PREMISES_GATEWAY,
...     privacy_level=PrivacyLevel.ORGANIZATIONAL
... )

Using strings:

>>> conn = connections.create(
...     name="SQL Server",
...     connection_type="Sql",
...     connectivity_type="OnPremises",
...     privacy_level="Organizational"
... )
Source code in src/fabias/_fabric/connections.py
def create(  # pragma: no cover
    self,
    name: str,
    connection_type: str,
    connectivity_type: Union[ConnectivityType, str] = "Cloud",
    privacy_level: Union[PrivacyLevel, str] = "Organizational",
    **kwargs: Any,
) -> Connection:
    """
    Create a new connection.

    Args:
        name: Connection display name
        connection_type: Type of connection (e.g., 'Sql', 'AzureBlob', 'SharePoint')
        connectivity_type: ConnectivityType enum or string ('OnPremises', 'VirtualNetwork', 'Cloud')
        privacy_level: PrivacyLevel enum or string ('None', 'Public', 'Organizational', 'Private')
        **kwargs: Additional connection properties

    Returns:
        Connection: The created connection object

    Examples:
        Using enums (recommended):

        >>> from fabias.fabric.enums import ConnectivityType, PrivacyLevel
        >>> conn = connections.create(
        ...     name="SQL Server",
        ...     connection_type="Sql",
        ...     connectivity_type=ConnectivityType.ON_PREMISES_GATEWAY,
        ...     privacy_level=PrivacyLevel.ORGANIZATIONAL
        ... )

        Using strings:

        >>> conn = connections.create(
        ...     name="SQL Server",
        ...     connection_type="Sql",
        ...     connectivity_type="OnPremises",
        ...     privacy_level="Organizational"
        ... )
    """
    # Convert enums to strings if needed
    connectivity_str = (
        connectivity_type.value
        if isinstance(connectivity_type, ConnectivityType)
        else connectivity_type
    )
    privacy_str = (
        privacy_level.value if isinstance(privacy_level, PrivacyLevel) else privacy_level
    )

    payload = {
        "displayName": name,
        "connectionType": connection_type,
        "connectivityType": connectivity_str,
        "privacyLevel": privacy_str,
        **kwargs,
    }

    response = self._client.post(self._endpoint, data=payload)
    data = response.json()

    return Connection(self._client, connection_data=data)
delete(name_or_id)

Delete a connection by name or GUID.

Parameters:

Name Type Description Default
name_or_id str

Connection name or GUID

required

Examples:

>>> fabric.connections.delete("Old Connection")
>>> fabric.connections.delete("abc-123-guid")
Source code in src/fabias/_fabric/connections.py
def delete(self, name_or_id: str) -> None:  # pragma: no cover
    """
    Delete a connection by name or GUID.

    Args:
        name_or_id: Connection name or GUID

    Examples:
        >>> fabric.connections.delete("Old Connection")
        >>> fabric.connections.delete("abc-123-guid")
    """
    # Try as GUID first
    if GUID.valid(name_or_id):
        self._get_client().delete(f"{self._endpoint}/{name_or_id}")
        return

    # Resolve name to connection and delete by ID
    from . import connection as get_connection

    conn = get_connection(name_or_id)
    if conn and conn.id:
        self._get_client().delete(f"{self._endpoint}/{conn.id}")
get(connection_id)

Get a specific connection by ID.

Parameters:

Name Type Description Default
connection_id str

Connection GUID

required

Returns:

Name Type Description
Connection Connection

The connection object

Examples:

>>> conn = connections.get("abc-123")
>>> print(conn.name)
Source code in src/fabias/_fabric/connections.py
def get(self, connection_id: str) -> Connection:
    """
    Get a specific connection by ID.

    Args:
        connection_id: Connection GUID

    Returns:
        Connection: The connection object

    Examples:
        >>> conn = connections.get("abc-123")
        >>> print(conn.name)
    """
    response = self._client.get(f"{self._endpoint}/{connection_id}")
    data = response.json()

    return Connection(self._client, connection_data=data)
list(name=None)

List all connections (tenant-wide).

Parameters:

Name Type Description Default
name Optional[str]

Optional filter by connection name (case-insensitive partial match)

None

Returns:

Type Description
List[Connection]

List[Connection]: List of connection objects

Examples:

>>> connections = Connections(client)
>>> for conn in connections.list():
...     print(conn.name)
>>> # Search by name
>>> sql_conns = connections.list(name="SQL")
Source code in src/fabias/_fabric/connections.py
def list(self, name: Optional[str] = None) -> List[Connection]:
    """
    List all connections (tenant-wide).

    Args:
        name: Optional filter by connection name (case-insensitive partial match)

    Returns:
        List[Connection]: List of connection objects

    Examples:
        >>> connections = Connections(client)
        >>> for conn in connections.list():
        ...     print(conn.name)

        >>> # Search by name
        >>> sql_conns = connections.list(name="SQL")
    """
    response = self._client.get(self._endpoint)
    data = response.json()

    connections = [
        Connection(self._client, connection_data=item) for item in data.get("value", [])
    ]

    # Filter by name if provided
    if name:
        name_lower = name.lower()
        connections = [conn for conn in connections if name_lower in (conn.name or "").lower()]

    return connections
supportedTypes()

List supported connection types.

Returns:

Type Description
List[Dict[str, Any]]

List[dict]: List of supported connection type configurations

Examples:

>>> types = connections.supported_types()
>>> for conn_type in types:
...     print(conn_type.get('name'))
Source code in src/fabias/_fabric/connections.py
def supportedTypes(self) -> List[Dict[str, Any]]:
    """
    List supported connection types.

    Returns:
        List[dict]: List of supported connection type configurations

    Examples:
        >>> types = connections.supported_types()
        >>> for conn_type in types:
        ...     print(conn_type.get('name'))
    """
    response = self._client.get(f"{self._endpoint}/supportedTypes")
    data = response.json()

    return data.get("value", [])
update(connection_id, display_name=None, privacy_level=None, **kwargs)

Update an existing connection.

Parameters:

Name Type Description Default
connection_id str

Connection GUID

required
display_name Optional[str]

New display name (optional)

None
privacy_level Optional[Union[PrivacyLevel, str]]

PrivacyLevel enum or string (optional)

None
**kwargs Any

Additional properties to update

{}

Returns:

Name Type Description
Connection Connection

The updated connection object

Examples:

Using enum:

>>> from fabias.fabric.enums import PrivacyLevel
>>> conn = connections.update(
...     "abc-123",
...     display_name="Updated Name",
...     privacy_level=PrivacyLevel.PRIVATE
... )

Using string:

>>> conn = connections.update(
...     "abc-123",
...     display_name="Updated Name",
...     privacy_level="Private"
... )
Source code in src/fabias/_fabric/connections.py
def update(  # pragma: no cover
    self,
    connection_id: str,
    display_name: Optional[str] = None,
    privacy_level: Optional[Union[PrivacyLevel, str]] = None,
    **kwargs: Any,
) -> Connection:
    """
    Update an existing connection.

    Args:
        connection_id: Connection GUID
        display_name: New display name (optional)
        privacy_level: PrivacyLevel enum or string (optional)
        **kwargs: Additional properties to update

    Returns:
        Connection: The updated connection object

    Examples:
        Using enum:

        >>> from fabias.fabric.enums import PrivacyLevel
        >>> conn = connections.update(
        ...     "abc-123",
        ...     display_name="Updated Name",
        ...     privacy_level=PrivacyLevel.PRIVATE
        ... )

        Using string:

        >>> conn = connections.update(
        ...     "abc-123",
        ...     display_name="Updated Name",
        ...     privacy_level="Private"
        ... )
    """
    payload = {**kwargs}

    if display_name is not None:
        payload["displayName"] = display_name
    if privacy_level is not None:
        # Convert enum to string if needed
        privacy_str = (
            privacy_level.value if isinstance(privacy_level, PrivacyLevel) else privacy_level
        )
        payload["privacyLevel"] = privacy_str

    response = self._client.patch(f"{self._endpoint}/{connection_id}", data=payload)
    data = response.json()

    return Connection(self._client, connection_data=data)

ConnectionsAccessor

Accessor for Connection list/create operations.

Source code in src/fabias/_fabric/connections.py
class ConnectionsAccessor:
    """Accessor for Connection list/create operations."""

    def __init__(self, client_or_getter: Union["FabricClient", Callable[[], "FabricClient"]]):
        """
        Args:
            client_or_getter: FabricClient or callable returning one (for lazy loading)
        """
        if callable(client_or_getter):
            self._client_getter: Optional[Callable[[], "FabricClient"]] = client_or_getter
            self._client: Optional["FabricClient"] = None
        else:
            self._client = client_or_getter
            self._client_getter = None

    def _get_client(self) -> "FabricClient":
        """Get client, lazily loading if needed."""
        if self._client is None:
            assert self._client_getter is not None
            self._client = self._client_getter()
        return self._client

    def __call__(self) -> List[Connection]:
        """
        List all connections.

        Automatically handles pagination via continuation tokens.

        Returns:
            list[Connection]: List of Connection objects

        Examples:
            >>> import fabias.fabric as fabric
            >>> fabric.client(auth=my_auth)
            >>>
            >>> # List all connections
            >>> all_conns = fabric.connections()
        """
        # Get all connections (pagination handled automatically by client)
        endpoint = "/connections"
        response = self._get_client().get(endpoint)
        data = response.json()

        return [
            Connection(self._get_client(), connection_data=item) for item in data.get("value", [])
        ]

    def get(self, connection_id: str) -> Connection:
        """
        Get a connection by ID.

        Args:
            connection_id: Connection GUID

        Returns:
            Connection: The Connection object

        Examples:
            >>> conn = fabric.connections.get("connection-guid")
        """
        return Connection(self._get_client(), identifier=connection_id)

    def add(  # pragma: no cover
        self, name: str, connection_type: str, connectivity_type: str, **kwargs: Any
    ) -> Connection:
        """
        Create a new connection.

        Args:
            name: Connection name
            connection_type: Type of connection (e.g., "Sql", "AzureBlob")
            connectivity_type: "OnPremises", "ShareableCloud", or "NotShareable"
            **kwargs: Additional properties (privacy_level, credential_details, etc.)

        Returns:
            Connection: The created Connection object

        Examples:
            >>> conn = fabric.connections.add(
            ...     name="My SQL Server",
            ...     connection_type="Sql",
            ...     connectivity_type="OnPremises",
            ...     privacy_level="Organizational"
            ... )
        """
        endpoint = "/connections"
        payload = {
            "displayName": name,
            "connectionDetails": {"type": connection_type},
            "connectivityType": connectivity_type,
            **kwargs,
        }

        try:
            response = self._get_client().post(endpoint, data=payload)
            connection_data = response.json()

            return Connection(self._get_client(), connection_data=connection_data)
        except Exception as e:
            # If connection already exists (409), fetch and return it
            from .._shared.exceptions import ApiError

            if (
                (isinstance(e, ApiError) and e.status_code == 409)
                or "409" in str(e)
                or "already exists" in str(e).lower()
            ):
                # Search for existing connection by name
                connections = self.__call__()
                matches = [c for c in connections if c.name == name]
                if matches:
                    return matches[0]
            raise
Functions
__call__()

List all connections.

Automatically handles pagination via continuation tokens.

Returns:

Type Description
List[Connection]

list[Connection]: List of Connection objects

Examples:

>>> import fabias.fabric as fabric
>>> fabric.client(auth=my_auth)
>>>
>>> # List all connections
>>> all_conns = fabric.connections()
Source code in src/fabias/_fabric/connections.py
def __call__(self) -> List[Connection]:
    """
    List all connections.

    Automatically handles pagination via continuation tokens.

    Returns:
        list[Connection]: List of Connection objects

    Examples:
        >>> import fabias.fabric as fabric
        >>> fabric.client(auth=my_auth)
        >>>
        >>> # List all connections
        >>> all_conns = fabric.connections()
    """
    # Get all connections (pagination handled automatically by client)
    endpoint = "/connections"
    response = self._get_client().get(endpoint)
    data = response.json()

    return [
        Connection(self._get_client(), connection_data=item) for item in data.get("value", [])
    ]
__init__(client_or_getter)

Parameters:

Name Type Description Default
client_or_getter Union[FabricClient, Callable[[], FabricClient]]

FabricClient or callable returning one (for lazy loading)

required
Source code in src/fabias/_fabric/connections.py
def __init__(self, client_or_getter: Union["FabricClient", Callable[[], "FabricClient"]]):
    """
    Args:
        client_or_getter: FabricClient or callable returning one (for lazy loading)
    """
    if callable(client_or_getter):
        self._client_getter: Optional[Callable[[], "FabricClient"]] = client_or_getter
        self._client: Optional["FabricClient"] = None
    else:
        self._client = client_or_getter
        self._client_getter = None
add(name, connection_type, connectivity_type, **kwargs)

Create a new connection.

Parameters:

Name Type Description Default
name str

Connection name

required
connection_type str

Type of connection (e.g., "Sql", "AzureBlob")

required
connectivity_type str

"OnPremises", "ShareableCloud", or "NotShareable"

required
**kwargs Any

Additional properties (privacy_level, credential_details, etc.)

{}

Returns:

Name Type Description
Connection Connection

The created Connection object

Examples:

>>> conn = fabric.connections.add(
...     name="My SQL Server",
...     connection_type="Sql",
...     connectivity_type="OnPremises",
...     privacy_level="Organizational"
... )
Source code in src/fabias/_fabric/connections.py
def add(  # pragma: no cover
    self, name: str, connection_type: str, connectivity_type: str, **kwargs: Any
) -> Connection:
    """
    Create a new connection.

    Args:
        name: Connection name
        connection_type: Type of connection (e.g., "Sql", "AzureBlob")
        connectivity_type: "OnPremises", "ShareableCloud", or "NotShareable"
        **kwargs: Additional properties (privacy_level, credential_details, etc.)

    Returns:
        Connection: The created Connection object

    Examples:
        >>> conn = fabric.connections.add(
        ...     name="My SQL Server",
        ...     connection_type="Sql",
        ...     connectivity_type="OnPremises",
        ...     privacy_level="Organizational"
        ... )
    """
    endpoint = "/connections"
    payload = {
        "displayName": name,
        "connectionDetails": {"type": connection_type},
        "connectivityType": connectivity_type,
        **kwargs,
    }

    try:
        response = self._get_client().post(endpoint, data=payload)
        connection_data = response.json()

        return Connection(self._get_client(), connection_data=connection_data)
    except Exception as e:
        # If connection already exists (409), fetch and return it
        from .._shared.exceptions import ApiError

        if (
            (isinstance(e, ApiError) and e.status_code == 409)
            or "409" in str(e)
            or "already exists" in str(e).lower()
        ):
            # Search for existing connection by name
            connections = self.__call__()
            matches = [c for c in connections if c.name == name]
            if matches:
                return matches[0]
        raise
get(connection_id)

Get a connection by ID.

Parameters:

Name Type Description Default
connection_id str

Connection GUID

required

Returns:

Name Type Description
Connection Connection

The Connection object

Examples:

>>> conn = fabric.connections.get("connection-guid")
Source code in src/fabias/_fabric/connections.py
def get(self, connection_id: str) -> Connection:
    """
    Get a connection by ID.

    Args:
        connection_id: Connection GUID

    Returns:
        Connection: The Connection object

    Examples:
        >>> conn = fabric.connections.get("connection-guid")
    """
    return Connection(self._get_client(), identifier=connection_id)

RoleAssignmentAccessor

Bases: BaseRoleAssignmentAccessor

Accessor for connection role assignment operations.

Inherits from BaseRoleAssignmentAccessor to provide: - call(): List all role assignments - add(principal_id, principal_type, role): Add role assignment (with 409 handling) - update(principal_id, role): Update role assignment - delete(principal_id): Delete role assignment

Source code in src/fabias/_fabric/connections.py
class RoleAssignmentAccessor(BaseRoleAssignmentAccessor):
    """
    Accessor for connection role assignment operations.

    Inherits from BaseRoleAssignmentAccessor to provide:
    - __call__(): List all role assignments
    - add(principal_id, principal_type, role): Add role assignment (with 409 handling)
    - update(principal_id, role): Update role assignment
    - delete(principal_id): Delete role assignment
    """

    def __init__(self, client: "FabricClient", connection_id: str):
        """Initialize role assignment accessor for a connection."""
        endpoint = f"/connections/{connection_id}/roleAssignments"
        super().__init__(client, endpoint, ConnectionRoleAssignment)
Functions
__init__(client, connection_id)

Initialize role assignment accessor for a connection.

Source code in src/fabias/_fabric/connections.py
def __init__(self, client: "FabricClient", connection_id: str):
    """Initialize role assignment accessor for a connection."""
    endpoint = f"/connections/{connection_id}/roleAssignments"
    super().__init__(client, endpoint, ConnectionRoleAssignment)

fabias.capacity(name_or_id)

Get a specific capacity by name or ID.

Parameters:

Name Type Description Default
name_or_id str

Capacity display name or GUID

required

Returns:

Name Type Description
Capacity Capacity

The capacity object

Raises:

Type Description
NotFoundError

If capacity not found

FabiasError

If multiple capacities match

Examples:

By name:

>>> capacity = fabric.capacity("Premium-P1")
>>> print(f"SKU: {capacity.sku}, Region: {capacity.region}")

By GUID:

>>> capacity = fabric.capacity("capacity-guid")
Note

Requires Capacity Admin or Fabric Administrator permissions.

Source code in src/fabias/_fabric/__init__.py
def capacity(name_or_id: str) -> "Capacity":
    """
    Get a specific capacity by name or ID.

    Args:
        name_or_id: Capacity display name or GUID

    Returns:
        Capacity: The capacity object

    Raises:
        NotFoundError: If capacity not found
        FabiasError: If multiple capacities match

    Examples:
        By name:

        >>> capacity = fabric.capacity("Premium-P1")
        >>> print(f"SKU: {capacity.sku}, Region: {capacity.region}")

        By GUID:

        >>> capacity = fabric.capacity("capacity-guid")

    Note:
        Requires Capacity Admin or Fabric Administrator permissions.
    """
    # Get accessor via __getattr__ (lazy loaded)
    _capacities = __getattr__("capacities")
    return _capacities.get(name_or_id)

fabias.capacities = CapacitiesAccessor(_get_client) module-attribute

Classes

fabias.Workspace

Represents a Microsoft Fabric workspace.

Provides access to workspace resources including pipelines, lakehouses, notebooks, environments, and Git integration. Resolves workspace identifiers from display names or GUIDs.

Attributes:

Name Type Description
id str

Workspace unique identifier (GUID)

name str

Workspace display name

capacityId str

Capacity GUID hosting this workspace

description str

Workspace description

Examples:

Access via FabricClient:

>>> client = FabricClient()
>>> workspace = client.workspace("GENESIS_EXT")
>>> print(f"Workspace: {workspace.name} ({workspace.id})")

Access workspace resources:

>>> pipeline = workspace.pipeline("Daily ETL")
>>> lakehouse = workspace.lakehouse("Analytics")
>>> git = workspace.git
Source code in src/fabias/_fabric/workspace.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
class Workspace:
    """
    Represents a Microsoft Fabric workspace.

    Provides access to workspace resources including pipelines, lakehouses,
    notebooks, environments, and Git integration. Resolves workspace
    identifiers from display names or GUIDs.

    Attributes:
        id (str): Workspace unique identifier (GUID)
        name (str): Workspace display name
        capacityId (str): Capacity GUID hosting this workspace
        description (str): Workspace description

    Examples:
        Access via FabricClient:

        >>> client = FabricClient()
        >>> workspace = client.workspace("GENESIS_EXT")
        >>> print(f"Workspace: {workspace.name} ({workspace.id})")

        Access workspace resources:

        >>> pipeline = workspace.pipeline("Daily ETL")
        >>> lakehouse = workspace.lakehouse("Analytics")
        >>> git = workspace.git
    """

    def __init__(
        self,
        client: "FabricClient",
        workspace_id: Optional[str] = None,
        workspace_data: Optional[dict] = None,
    ):
        """
        Initialize workspace.

        Args:
            client: Authenticated FabricClient instance
            workspace_id: Workspace identifier (name, GUID, or None for current)
            workspace_data: Pre-populated workspace data from API (skips resolution)

        Behavior:
            - If workspace_data provided: Pre-populate attributes (no API call)
            - If GUID provided: Store it without API call (lazy load properties)
            - If name provided: Resolve immediately to get GUID (required for operations)
            - If None or 'default': Resolve to current workspace (Fabric only)

        Raises:
            NotFoundError: If workspace name cannot be found
            FabiasError: If not in Fabric and no workspace_id provided
        """
        self._client = client
        self._workspace_id = workspace_id
        self._data_fetched = False

        # Data attributes (lazy loaded unless resolved by name or pre-populated)
        self._id: Optional[str] = None
        self._name: Optional[str] = None
        self._capacity_id: Optional[str] = None
        self._description: Optional[str] = None

        # Cached sub-objects
        self._git: Optional["Git"] = None
        self._spark: Optional["Spark"] = None
        self._items_cache: dict = {}

        # Pre-populate from provided data
        if workspace_data:
            self._id = workspace_data.get("id")
            self._name = workspace_data.get("displayName") or workspace_data.get("name")
            self._capacity_id = workspace_data.get("capacityId")
            self._description = workspace_data.get("description")
            self._data_fetched = True
            return

        # If GUID provided, store it without API call
        if workspace_id and GUID.valid(workspace_id):
            self._id = workspace_id
            return

        # If name provided (or None/default), resolve immediately to get GUID
        # We need GUID for all subsequent operations
        self._resolve_identifier(workspace_id)

    def _resolve_identifier(self, workspace_id: Optional[str]) -> None:
        """
        Resolve workspace identifier to get the GUID.

        Called when a name is provided (or None/default).
        Populates all data since we're already making an API call.
        Filters out deleted workspaces when searching by name.

        Args:
            workspace_id: Identifier to resolve
        """
        # Handle None or "default" - use current workspace
        if not workspace_id or workspace_id.strip().lower() == "default":  # pragma: no cover
            if not runtime.isFabric:
                raise FabiasError(
                    "Cannot determine current workspace outside Fabric. "
                    "Please provide a workspace ID or name."
                )

            # Get from runtime context
            workspace_id = runtime.context.get("workspace_id")

        if not workspace_id:
            raise FabiasError("Could not determine workspace ID")

        # If it happens to be a GUID at this point, just store it
        if GUID.valid(workspace_id):
            self._id = workspace_id
            return

        # Name-based resolution: try admin API first (if credentials available)
        # Admin API has state field to filter deleted workspaces
        if self._client.hasExplicitCredentials:
            try:
                response = self._client.get(f"/admin/workspaces?name={workspace_id}")
                data = response.json()
                workspaces = data.get("workspaces", [])

                for workspace in workspaces:
                    if workspace.get("name") == workspace_id:
                        state = workspace.get("state", "Active")

                        # Filter out deleted workspaces
                        if state == "Deleted":
                            workspace_guid = workspace.get("id")
                            raise NotFoundError(
                                f"Workspace '{workspace_id}' exists but is in 'Deleted' state. "
                                f"Restore it using: fabric.workspaces.restore('{workspace_guid}')",
                                resource_type="Workspace",
                                resource_id=workspace_id,
                            )

                        self._id = workspace.get("id")
                        self._name = workspace.get("name")
                        self._capacity_id = workspace.get("capacityId")
                        self._description = workspace.get("description")
                        self._data_fetched = True
                        return
            except NotFoundError:
                raise  # Re-raise if workspace is deleted
            except Exception:
                pass  # Fall through to list search

        # Fallback: use client.workspaces() which handles sempy vs API internally
        workspaces = self._client.workspaces()

        matches = [w for w in workspaces if w.name == workspace_id or w.id == workspace_id]

        if not matches:
            raise NotFoundError(
                f"Workspace '{workspace_id}' not found",
                resource_type="Workspace",
                resource_id=workspace_id,
            )

        if len(matches) > 1:
            raise FabiasError(f"Multiple workspaces found with name '{workspace_id}'")

        matched = matches[0]

        # Verify the workspace is accessible (not deleted)
        # If the regular API returned it, try to access it
        try:
            self._client.get(f"/workspaces/{matched.id}")
        except Exception:
            raise NotFoundError(
                f"Workspace '{workspace_id}' exists but is not accessible (may be deleted)",
                resource_type="Workspace",
                resource_id=workspace_id,
            )

        self._id = matched.id
        self._name = matched.name
        self._capacity_id = matched.capacityId
        self._description = matched.description
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load workspace data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/workspaces/{self._id}")
        data = response.json()
        self._name = data.get("displayName")
        self._capacity_id = data.get("capacityId")
        self._description = data.get("description")
        self._data_fetched = True

    @property
    def id(self) -> Optional[str]:
        """Workspace GUID."""
        return self._id

    def _get_id(self) -> str:
        """Get workspace ID with assertion (for internal use)."""
        if self._id is None:
            raise FabiasError("Workspace ID not set - workspace not properly initialized")
        return self._id

    @property
    def name(self) -> Optional[str]:
        """Workspace display name (lazy loaded)."""
        self._fetch()
        return self._name

    @property
    def capacityId(self) -> Optional[str]:
        """Capacity GUID hosting this workspace (lazy loaded)."""
        self._fetch()
        return self._capacity_id

    @property
    def description(self) -> Optional[str]:
        """Workspace description (lazy loaded)."""
        self._fetch()
        return self._description

    def delete(self) -> None:  # pragma: no cover
        """
        Delete this workspace (soft delete).

        The workspace enters a retention period (default 7 days, configurable up to 90 days)
        during which Fabric administrators can restore it using the restore() method.

        Example:
            >>> ws = fabric.workspace("Old Workspace")
            >>> ws.delete()
            >>> # Later, restore it:
            >>> ws.restore()
        """
        if not self._id:
            raise FabiasError("Cannot delete workspace: ID not resolved")

        self._client.delete(f"/workspaces/{self._id}")

        # Clear local state since workspace no longer exists
        self._id = None
        self._name = None
        self._capacity_id = None
        self._description = None
        self._data_fetched = False

    def restore(
        self,
        new_admin_principal_id: Optional[str] = None,
        new_name: Optional[str] = None,
    ) -> "Workspace":  # pragma: no cover
        """
        Restore a deleted workspace.

        Requires Fabric Administrator privileges. The workspace must be in "Deleted" state
        within the retention period (7-90 days, default 7).

        Args:
            new_admin_principal_id: Optional principal ID to assign as workspace admin
            new_name: Optional new name for the workspace (required for My workspaces)

        Returns:
            Workspace: The restored workspace

        Raises:
            FabiasError: If workspace ID not set or restore fails

        Examples:
            >>> # Restore with original settings
            >>> deleted_ws = fabric.workspace(workspace_id)  # By GUID
            >>> deleted_ws.restore()

            >>> # Restore with new admin
            >>> deleted_ws.restore(new_admin_principal_id="user-guid")

            >>> # Restore My workspace with new name
            >>> deleted_ws.restore(new_name="Restored Workspace")
        """
        if not self._id:
            raise FabiasError("Cannot restore workspace: ID not resolved")

        payload: dict[str, Any] = {}
        if new_admin_principal_id:
            payload["newWorkspaceAdminPrincipal"] = {
                "id": new_admin_principal_id,
                "type": "User",  # Could also be ServicePrincipal, Group, etc.
            }
        if new_name:
            payload["newWorkspaceName"] = new_name

        # Call admin restore API
        self._client.post(f"/admin/workspaces/{self._id}/restore", data=payload)

        # Refresh workspace data
        self._data_fetched = False
        self._fetch()

        return self

    @property
    def spark(self) -> "Spark":
        """
        Get Spark settings handler for this workspace.

        Returns:
            Spark: Spark settings handler

        Examples:
            Get current Spark settings:

            >>> settings = workspace.spark.settings()
            >>> print(f"Auto log: {settings.automatic_log}")
            >>> print(f"Pool max nodes: {settings.pool_max_nodes}")

            Update Spark settings:

            >>> workspace.spark.settings.update(
            ...     automatic_log=True,
            ...     pool={"starterPool": {"maxNodeCount": 10, "maxExecutors": 5}},
            ... )
        """
        if self._spark is None:
            from .spark import Spark

            self._spark = Spark(self._client, self._get_id())
        return self._spark

    @property
    def git(self) -> "Git":
        """
        Get Git integration handler for this workspace.

        Returns:
            Git: Git operations handler

        Examples:
            >>> git = workspace.git
            >>> status = git.status()
            >>> if status.has_changes:
            ...     git.pull()
        """
        if self._git is None:
            from .git import Git

            self._git = Git(self._client, self._get_id())
        return self._git

    def roleAssignment(self, principal_id: str) -> "WorkspaceRoleAssignment":
        """
        Get a specific role assignment by principal ID.

        This method accepts a principal ID and looks up the corresponding
        role assignment. If multiple or no assignments are found, raises an error.

        Args:
            principal_id: Principal GUID (user, group, or service principal)

        Returns:
            WorkspaceRoleAssignment: The role assignment

        Raises:
            NotFoundError: If no role assignment found for the principal
            FabiasError: If multiple role assignments found (shouldn't happen)

        Examples:
            >>> ws = fabric.workspace("GENESIS")
            >>> assignment = ws.roleAssignment("user-guid")
            >>> print(assignment.role)
        """
        # List all assignments to find the one with matching principal_id
        assignments = self.roleAssignments()

        # Find assignment with matching principal ID
        matches = [a for a in assignments if a.principalId == principal_id]

        if not matches:
            from .._shared.exceptions import NotFoundError

            raise NotFoundError(
                f"No role assignment found for principal {principal_id}",
                resource_type="WorkspaceRoleAssignment",
                resource_id=principal_id,
            )

        if len(matches) > 1:
            from .._shared.exceptions import FabiasError

            raise FabiasError(
                f"Multiple role assignments found for principal {principal_id}. "
                "This should not happen."
            )

        return cast("WorkspaceRoleAssignment", matches[0])

    @property
    def roleAssignments(self) -> "WorkspaceRoleAssignmentAccessor":
        """
        Access role assignments for this workspace.

        Returns:
            WorkspaceRoleAssignmentAccessor: Accessor for listing and managing role assignments

        Examples:
            List role assignments:

            >>> ws = fabric.workspace("GENESIS")
            >>> for assignment in ws.roleAssignments():
            ...     print(f"{assignment.principalName}: {assignment.role}")

            Add role assignment:

            >>> ws.roleAssignments.add(
            ...     principal_id="user-guid",
            ...     principal_type="User",
            ...     role="Admin"
            ... )
        """
        return WorkspaceRoleAssignmentAccessor(self._client, self._get_id())

    # =============================================================================
    # Items
    # =============================================================================

    def pipeline(self, identifier: str) -> "Pipeline":
        """
        Get a specific pipeline by name or ID.

        Args:
            identifier: Pipeline name or GUID

        Returns:
            Pipeline: The pipeline object

        Examples:
            >>> pipeline = workspace.pipeline("Daily ETL")
            >>> job = pipeline.run()
        """
        from .items.pipeline import Pipeline

        return Pipeline(self._client, self._get_id(), identifier)

    @property
    def pipelines(self) -> "PipelineAccessor":
        """
        Access pipeline list and creation operations.

        Returns:
            PipelineAccessor: Accessor for listing and creating pipelines

        Examples:
            List all pipelines:

            >>> for pipeline in workspace.pipelines():
            ...     print(pipeline.name)

            Create new pipeline:

            >>> pipeline = workspace.pipelines.add("New ETL")
        """
        from .items.pipeline import PipelineAccessor

        return PipelineAccessor(self._client, self._get_id())

    def lakehouse(self, identifier: str) -> "Lakehouse":
        """
        Get a specific lakehouse by name or ID.

        Args:
            identifier: Lakehouse name or GUID

        Returns:
            Lakehouse: The lakehouse object

        Examples:
            >>> lakehouse = workspace.lakehouse("Analytics")
        """
        from .items.lakehouse import Lakehouse

        return Lakehouse(self._client, self._get_id(), identifier)

    @property
    def lakehouses(self) -> "LakehouseAccessor":
        """
        Access lakehouse list and creation operations.

        Returns:
            LakehouseAccessor: Accessor for listing and creating lakehouses

        Examples:
            List all lakehouses:

            >>> for lh in workspace.lakehouses():
            ...     print(lh.name)

            Create new lakehouse:

            >>> lakehouse = workspace.lakehouses.add("Data Lake")
        """
        from .items.lakehouse import LakehouseAccessor

        return LakehouseAccessor(self._client, self._get_id())

    def notebook(self, identifier: str) -> "Notebook":
        """
        Get a specific notebook by name or ID.

        Args:
            identifier: Notebook name or GUID

        Returns:
            Notebook: The notebook object

        Examples:
            >>> notebook = workspace.notebook("ETL Script")
        """
        from .items.notebook import Notebook

        return Notebook(self._client, self._get_id(), identifier)

    @property
    def notebooks(self) -> "NotebookAccessor":
        """
        Access notebook list and creation operations.

        Returns:
            NotebookAccessor: Accessor for listing and creating notebooks

        Examples:
            List all notebooks:

            >>> for nb in workspace.notebooks():
            ...     print(nb.name)

            Create new notebook:

            >>> notebook = workspace.notebooks.add("New Analysis")
        """
        from .items.notebook import NotebookAccessor

        return NotebookAccessor(self._client, self._get_id())

    def environment(self, identifier: str) -> "Environment":
        """
        Get a specific environment by name or ID.

        Args:
            identifier: Environment name or GUID

        Returns:
            Environment: The environment object

        Examples:
            >>> env = workspace.environment("ML Environment")
        """
        from .items.environment import Environment

        return Environment(self._client, self._get_id(), identifier)

    @property
    def environments(self) -> "EnvironmentAccessor":
        """
        Access environment list and creation operations.

        Returns:
            EnvironmentAccessor: Accessor for listing and creating environments

        Examples:
            List all environments:

            >>> for env in workspace.environments():
            ...     print(env.name)

            Create new environment:

            >>> env = workspace.environments.add("Data Science")
        """
        from .items.environment import EnvironmentAccessor

        return EnvironmentAccessor(self._client, self._get_id())

    def variableLibrary(self, identifier: str) -> "VariableLibrary":
        """
        Get a specific variable library by name or ID.

        Args:
            identifier: Variable library name or GUID

        Returns:
            VariableLibrary: The variable library object

        Examples:
            >>> lib = workspace.variableLibrary("Deployment Config")
            >>> print(f"Active value set: {lib.active_value_set}")
        """
        from .items.variablelibrary import VariableLibrary

        return VariableLibrary(self._client, self._get_id(), identifier)

    @property
    def variableLibraries(self) -> "VariableLibraryAccessor":
        """
        Access variable library list and creation operations.

        Returns:
            VariableLibraryAccessor: Accessor for listing and creating variable libraries

        Examples:
            List all variable libraries:

            >>> for lib in workspace.variableLibraries():
            ...     print(f"{lib.name}: active={lib.active_value_set}")

            Create new variable library:

            >>> lib = workspace.variableLibraries.add("Deployment Config")

            Create with initial variables:

            >>> import base64, json
            >>> variables_json = {
            ...     "$schema": "...",
            ...     "variables": [{"name": "env", "type": "String", "value": "dev"}]
            ... }
            >>> lib = workspace.variableLibraries.add(
            ...     "Deployment Config",
            ...     definition={
            ...         "format": "VariableLibraryV1",
            ...         "parts": [{
            ...             "path": "variables.json",
            ...             "payload": base64.b64encode(
            ...                 json.dumps(variables_json).encode()
            ...             ).decode(),
            ...             "payloadType": "InlineBase64"
            ...         }]
            ...     }
            ... )
        """
        from .items.variablelibrary import VariableLibraryAccessor

        return VariableLibraryAccessor(self._client, self._get_id())

    # =============================================================================
    # Folders
    # =============================================================================

    def folder(self, identifier: str) -> "Folder":
        """
        Get a specific folder by name or ID.

        Args:
            identifier: Folder display name or GUID

        Returns:
            Folder: The folder object

        Examples:
            >>> folder = workspace.folder("Sales")
            >>> print(f"{folder.name} (parent: {folder.parent})")
        """
        from .folders import Folder

        return Folder(self._client, self._get_id(), identifier)

    @property
    def folders(self) -> "FolderAccessor":
        """
        Access folder list and creation operations.

        Returns:
            FolderAccessor: Accessor for listing and creating folders

        Examples:
            List all folders:

            >>> for f in workspace.folders():
            ...     print(f"{f.name} (parent: {f.parent})")

            List direct children of a folder:

            >>> children = workspace.folders(root_folder_id=parent.id, recursive=False)

            Create new folder:

            >>> folder = workspace.folders.add("Reports")

            Create nested folder:

            >>> child = workspace.folders.add("Q1", parent=parent.id)
        """
        from .folders import FolderAccessor

        return FolderAccessor(self._client, self._get_id())

    # =============================================================================
    # Items
    # =============================================================================

    def items(self, item_type: Optional[Union[ItemType, str]] = None) -> list:
        """
        List items in this workspace, converted to specific types.

        Returns typed item objects (Pipeline, Notebook, etc.) based on the
        'type' field in the API response. Unknown types return base Item.

        Args:
            item_type: Optional filter by type. Can be ItemType enum or string
                (e.g., ItemType.DATA_PIPELINE, "DataPipeline", ItemType.NOTEBOOK, "Notebook")

        Returns:
            list: List of Item objects (Pipeline, Notebook, etc.)

        Examples:
            All items:

            >>> items = workspace.items()
            >>> for item in items:
            ...     print(f"{item.name}: {type(item).__name__}")

            Filtered by type:

            >>> pipelines = workspace.items(item_type="DataPipeline")
            >>> notebooks = workspace.items(item_type="Notebook")
        """
        from .items import fromJson

        # Build endpoint with optional type filter
        endpoint = f"/workspaces/{self.id}/items"
        if item_type:
            # Convert enum to string if needed
            type_str = item_type.value if isinstance(item_type, ItemType) else item_type
            endpoint = f"{endpoint}?type={type_str}"

        # Get all items (pagination handled automatically by client)
        response = self._client.get(endpoint)
        data = response.json()

        # Convert JSON to typed objects
        return [
            fromJson(self._client, self._get_id(), item_data) for item_data in data.get("value", [])
        ]

    @classmethod
    @classmethod
    def create(  # pragma: no cover
        cls, client: "FabricClient", name: str, capacity: str, description: Optional[str] = None
    ) -> "Workspace":
        """
        Create a new workspace.

        If a workspace with the same name already exists and is in "Active" state,
        returns the existing workspace. If the workspace exists but is in "Deleted"
        state, raises an error indicating it must be permanently deleted first.

        Args:
            client: Authenticated FabricClient instance
            name: Display name for the new workspace
            capacity: Capacity GUID to host the workspace
            description: Optional workspace description

        Returns:
            Workspace: The newly created or existing active workspace

        Raises:
            FabiasError: If workspace exists in "Deleted" state

        Examples:
            >>> from fabias.fabric import FabricClient, Workspace
            >>> client = FabricClient(auth=auth)
            >>> ws = Workspace.create(client, "New Workspace", capacity="...")
        """
        payload = {"displayName": name, "capacityId": capacity}
        if description is not None:
            payload["description"] = description

        try:
            response = client.post("/workspaces", data=payload)

            if response.status_code == 202:
                operation_id = response.headers.get("x-ms-operation-id")
                raise FabiasError(
                    f"Workspace creation started asynchronously (operation: {operation_id}). "
                    "Polling not yet implemented."
                )

            workspace_data = response.json()
            workspace_id = workspace_data.get("id")

            # Return new Workspace instance with resolved ID
            return cls(client, workspace_id=workspace_id)
        except Exception as e:
            # If workspace already exists (409), fetch and return it if Active
            from .._shared.exceptions import ApiError

            if (
                (isinstance(e, ApiError) and e.status_code == 409)
                or "409" in str(e)
                or "already exists" in str(e).lower()
            ):
                # Use admin API to get state information
                if client.hasExplicitCredentials:
                    try:
                        response = client.get(f"/admin/workspaces?name={name}")
                        data = response.json()
                        workspaces_data = data.get("workspaces", [])

                        for ws_data in workspaces_data:
                            if ws_data.get("name") == name:
                                state = ws_data.get("state", "Active")

                                if state == "Deleted":
                                    raise FabiasError(
                                        f"Workspace '{name}' exists in 'Deleted' state. "
                                        "It must be permanently deleted by a Fabric admin "
                                        "before creating a new workspace with this name."
                                    )

                                # Active or other state - return the workspace
                                return cls(client, workspace_data=ws_data)
                    except FabiasError:
                        raise  # Re-raise our custom error
                    except Exception:
                        pass  # Fall through to regular workspaces API

                # Fallback: regular workspaces API (may not have state field)
                workspaces = client.workspaces()
                matches = [w for w in workspaces if w.name == name]

                if matches:
                    # Without state field, we can't distinguish deleted workspaces
                    # Try to access the workspace to verify it's active
                    ws = matches[0]
                    try:
                        # Attempt to fetch workspace details - will fail if deleted
                        client.get(f"/workspaces/{ws.id}")
                        return ws
                    except Exception:
                        raise FabiasError(
                            f"Workspace '{name}' exists but is not accessible. "
                            "It may be in 'Deleted' state. Use admin API or wait for "
                            "permanent deletion before creating a workspace with this name."
                        )
            raise

    def __repr__(self) -> str:
        """Return string representation."""
        return f"Workspace(id={self.id}, name={self.name!r})"

    def __getattr__(self, name: str) -> Callable:
        """
        Dynamic attribute access for undefined endpoints.

        Allows accessing Fabric API endpoints that don't have explicit methods.
        Converts attribute name to API endpoint and returns a callable or data.

        Args:
            name: Attribute name (will be converted to API endpoint)

        Returns:
            Callable: A function that accepts an optional item_id parameter

        Examples:
            # List eventhouses (no explicit method defined)
            >>> eventhouses = workspace.eventhouses()

            # Get specific eventhouse
            >>> eh = workspace.eventhouse("some-id")

            # Access any Fabric item type
            >>> warehouses = workspace.warehouses()
            >>> kqlDatabases = workspace.kqlDatabases()
        """
        # Don't intercept private attributes or known properties
        if name.startswith("_"):
            raise AttributeError(name)

        def dynamic_item_accessor(item_id: Optional[str] = None):
            """
            Access items of a given type.

            If item_id is provided, gets that specific item.
            Otherwise, lists all items of this type.
            """
            # Handle singular vs plural naming
            # e.g., "eventhouse" -> get one, "eventhouses" -> list all
            if item_id is not None:
                # Get specific item: /workspaces/{id}/{type}/{item_id}
                endpoint = f"/workspaces/{self.id}/{name}/{item_id}"
                response = self._client.get(endpoint)
                return response.json()
            else:
                # List items: /workspaces/{id}/{type}
                endpoint = f"/workspaces/{self.id}/{name}"
                response = self._client.get(endpoint)
                data = response.json()
                # Most Fabric list endpoints return {"value": [...]}
                if "value" in data:
                    return data["value"]
                return data

        return dynamic_item_accessor

Attributes

id property

Workspace GUID.

name property

Workspace display name (lazy loaded).

capacityId property

Capacity GUID hosting this workspace (lazy loaded).

description property

Workspace description (lazy loaded).

pipelines property

Access pipeline list and creation operations.

Returns:

Name Type Description
PipelineAccessor PipelineAccessor

Accessor for listing and creating pipelines

Examples:

List all pipelines:

>>> for pipeline in workspace.pipelines():
...     print(pipeline.name)

Create new pipeline:

>>> pipeline = workspace.pipelines.add("New ETL")

lakehouses property

Access lakehouse list and creation operations.

Returns:

Name Type Description
LakehouseAccessor LakehouseAccessor

Accessor for listing and creating lakehouses

Examples:

List all lakehouses:

>>> for lh in workspace.lakehouses():
...     print(lh.name)

Create new lakehouse:

>>> lakehouse = workspace.lakehouses.add("Data Lake")

notebooks property

Access notebook list and creation operations.

Returns:

Name Type Description
NotebookAccessor NotebookAccessor

Accessor for listing and creating notebooks

Examples:

List all notebooks:

>>> for nb in workspace.notebooks():
...     print(nb.name)

Create new notebook:

>>> notebook = workspace.notebooks.add("New Analysis")

environments property

Access environment list and creation operations.

Returns:

Name Type Description
EnvironmentAccessor EnvironmentAccessor

Accessor for listing and creating environments

Examples:

List all environments:

>>> for env in workspace.environments():
...     print(env.name)

Create new environment:

>>> env = workspace.environments.add("Data Science")

variableLibraries property

Access variable library list and creation operations.

Returns:

Name Type Description
VariableLibraryAccessor VariableLibraryAccessor

Accessor for listing and creating variable libraries

Examples:

List all variable libraries:

>>> for lib in workspace.variableLibraries():
...     print(f"{lib.name}: active={lib.active_value_set}")

Create new variable library:

>>> lib = workspace.variableLibraries.add("Deployment Config")

Create with initial variables:

>>> import base64, json
>>> variables_json = {
...     "$schema": "...",
...     "variables": [{"name": "env", "type": "String", "value": "dev"}]
... }
>>> lib = workspace.variableLibraries.add(
...     "Deployment Config",
...     definition={
...         "format": "VariableLibraryV1",
...         "parts": [{
...             "path": "variables.json",
...             "payload": base64.b64encode(
...                 json.dumps(variables_json).encode()
...             ).decode(),
...             "payloadType": "InlineBase64"
...         }]
...     }
... )

git property

Get Git integration handler for this workspace.

Returns:

Name Type Description
Git Git

Git operations handler

Examples:

>>> git = workspace.git
>>> status = git.status()
>>> if status.has_changes:
...     git.pull()

roleAssignments property

Access role assignments for this workspace.

Returns:

Name Type Description
WorkspaceRoleAssignmentAccessor WorkspaceRoleAssignmentAccessor

Accessor for listing and managing role assignments

Examples:

List role assignments:

>>> ws = fabric.workspace("GENESIS")
>>> for assignment in ws.roleAssignments():
...     print(f"{assignment.principalName}: {assignment.role}")

Add role assignment:

>>> ws.roleAssignments.add(
...     principal_id="user-guid",
...     principal_type="User",
...     role="Admin"
... )

Functions

pipeline(identifier)

Get a specific pipeline by name or ID.

Parameters:

Name Type Description Default
identifier str

Pipeline name or GUID

required

Returns:

Name Type Description
Pipeline Pipeline

The pipeline object

Examples:

>>> pipeline = workspace.pipeline("Daily ETL")
>>> job = pipeline.run()
Source code in src/fabias/_fabric/workspace.py
def pipeline(self, identifier: str) -> "Pipeline":
    """
    Get a specific pipeline by name or ID.

    Args:
        identifier: Pipeline name or GUID

    Returns:
        Pipeline: The pipeline object

    Examples:
        >>> pipeline = workspace.pipeline("Daily ETL")
        >>> job = pipeline.run()
    """
    from .items.pipeline import Pipeline

    return Pipeline(self._client, self._get_id(), identifier)

lakehouse(identifier)

Get a specific lakehouse by name or ID.

Parameters:

Name Type Description Default
identifier str

Lakehouse name or GUID

required

Returns:

Name Type Description
Lakehouse Lakehouse

The lakehouse object

Examples:

>>> lakehouse = workspace.lakehouse("Analytics")
Source code in src/fabias/_fabric/workspace.py
def lakehouse(self, identifier: str) -> "Lakehouse":
    """
    Get a specific lakehouse by name or ID.

    Args:
        identifier: Lakehouse name or GUID

    Returns:
        Lakehouse: The lakehouse object

    Examples:
        >>> lakehouse = workspace.lakehouse("Analytics")
    """
    from .items.lakehouse import Lakehouse

    return Lakehouse(self._client, self._get_id(), identifier)

notebook(identifier)

Get a specific notebook by name or ID.

Parameters:

Name Type Description Default
identifier str

Notebook name or GUID

required

Returns:

Name Type Description
Notebook Notebook

The notebook object

Examples:

>>> notebook = workspace.notebook("ETL Script")
Source code in src/fabias/_fabric/workspace.py
def notebook(self, identifier: str) -> "Notebook":
    """
    Get a specific notebook by name or ID.

    Args:
        identifier: Notebook name or GUID

    Returns:
        Notebook: The notebook object

    Examples:
        >>> notebook = workspace.notebook("ETL Script")
    """
    from .items.notebook import Notebook

    return Notebook(self._client, self._get_id(), identifier)

environment(identifier)

Get a specific environment by name or ID.

Parameters:

Name Type Description Default
identifier str

Environment name or GUID

required

Returns:

Name Type Description
Environment Environment

The environment object

Examples:

>>> env = workspace.environment("ML Environment")
Source code in src/fabias/_fabric/workspace.py
def environment(self, identifier: str) -> "Environment":
    """
    Get a specific environment by name or ID.

    Args:
        identifier: Environment name or GUID

    Returns:
        Environment: The environment object

    Examples:
        >>> env = workspace.environment("ML Environment")
    """
    from .items.environment import Environment

    return Environment(self._client, self._get_id(), identifier)

variableLibrary(identifier)

Get a specific variable library by name or ID.

Parameters:

Name Type Description Default
identifier str

Variable library name or GUID

required

Returns:

Name Type Description
VariableLibrary VariableLibrary

The variable library object

Examples:

>>> lib = workspace.variableLibrary("Deployment Config")
>>> print(f"Active value set: {lib.active_value_set}")
Source code in src/fabias/_fabric/workspace.py
def variableLibrary(self, identifier: str) -> "VariableLibrary":
    """
    Get a specific variable library by name or ID.

    Args:
        identifier: Variable library name or GUID

    Returns:
        VariableLibrary: The variable library object

    Examples:
        >>> lib = workspace.variableLibrary("Deployment Config")
        >>> print(f"Active value set: {lib.active_value_set}")
    """
    from .items.variablelibrary import VariableLibrary

    return VariableLibrary(self._client, self._get_id(), identifier)

items(item_type=None)

List items in this workspace, converted to specific types.

Returns typed item objects (Pipeline, Notebook, etc.) based on the 'type' field in the API response. Unknown types return base Item.

Parameters:

Name Type Description Default
item_type Optional[Union[ItemType, str]]

Optional filter by type. Can be ItemType enum or string (e.g., ItemType.DATA_PIPELINE, "DataPipeline", ItemType.NOTEBOOK, "Notebook")

None

Returns:

Name Type Description
list list

List of Item objects (Pipeline, Notebook, etc.)

Examples:

All items:

>>> items = workspace.items()
>>> for item in items:
...     print(f"{item.name}: {type(item).__name__}")

Filtered by type:

>>> pipelines = workspace.items(item_type="DataPipeline")
>>> notebooks = workspace.items(item_type="Notebook")
Source code in src/fabias/_fabric/workspace.py
def items(self, item_type: Optional[Union[ItemType, str]] = None) -> list:
    """
    List items in this workspace, converted to specific types.

    Returns typed item objects (Pipeline, Notebook, etc.) based on the
    'type' field in the API response. Unknown types return base Item.

    Args:
        item_type: Optional filter by type. Can be ItemType enum or string
            (e.g., ItemType.DATA_PIPELINE, "DataPipeline", ItemType.NOTEBOOK, "Notebook")

    Returns:
        list: List of Item objects (Pipeline, Notebook, etc.)

    Examples:
        All items:

        >>> items = workspace.items()
        >>> for item in items:
        ...     print(f"{item.name}: {type(item).__name__}")

        Filtered by type:

        >>> pipelines = workspace.items(item_type="DataPipeline")
        >>> notebooks = workspace.items(item_type="Notebook")
    """
    from .items import fromJson

    # Build endpoint with optional type filter
    endpoint = f"/workspaces/{self.id}/items"
    if item_type:
        # Convert enum to string if needed
        type_str = item_type.value if isinstance(item_type, ItemType) else item_type
        endpoint = f"{endpoint}?type={type_str}"

    # Get all items (pagination handled automatically by client)
    response = self._client.get(endpoint)
    data = response.json()

    # Convert JSON to typed objects
    return [
        fromJson(self._client, self._get_id(), item_data) for item_data in data.get("value", [])
    ]

roleAssignment(principal_id)

Get a specific role assignment by principal ID.

This method accepts a principal ID and looks up the corresponding role assignment. If multiple or no assignments are found, raises an error.

Parameters:

Name Type Description Default
principal_id str

Principal GUID (user, group, or service principal)

required

Returns:

Name Type Description
WorkspaceRoleAssignment WorkspaceRoleAssignment

The role assignment

Raises:

Type Description
NotFoundError

If no role assignment found for the principal

FabiasError

If multiple role assignments found (shouldn't happen)

Examples:

>>> ws = fabric.workspace("GENESIS")
>>> assignment = ws.roleAssignment("user-guid")
>>> print(assignment.role)
Source code in src/fabias/_fabric/workspace.py
def roleAssignment(self, principal_id: str) -> "WorkspaceRoleAssignment":
    """
    Get a specific role assignment by principal ID.

    This method accepts a principal ID and looks up the corresponding
    role assignment. If multiple or no assignments are found, raises an error.

    Args:
        principal_id: Principal GUID (user, group, or service principal)

    Returns:
        WorkspaceRoleAssignment: The role assignment

    Raises:
        NotFoundError: If no role assignment found for the principal
        FabiasError: If multiple role assignments found (shouldn't happen)

    Examples:
        >>> ws = fabric.workspace("GENESIS")
        >>> assignment = ws.roleAssignment("user-guid")
        >>> print(assignment.role)
    """
    # List all assignments to find the one with matching principal_id
    assignments = self.roleAssignments()

    # Find assignment with matching principal ID
    matches = [a for a in assignments if a.principalId == principal_id]

    if not matches:
        from .._shared.exceptions import NotFoundError

        raise NotFoundError(
            f"No role assignment found for principal {principal_id}",
            resource_type="WorkspaceRoleAssignment",
            resource_id=principal_id,
        )

    if len(matches) > 1:
        from .._shared.exceptions import FabiasError

        raise FabiasError(
            f"Multiple role assignments found for principal {principal_id}. "
            "This should not happen."
        )

    return cast("WorkspaceRoleAssignment", matches[0])

delete()

Delete this workspace (soft delete).

The workspace enters a retention period (default 7 days, configurable up to 90 days) during which Fabric administrators can restore it using the restore() method.

Example

ws = fabric.workspace("Old Workspace") ws.delete()

Later, restore it:

ws.restore()

Source code in src/fabias/_fabric/workspace.py
def delete(self) -> None:  # pragma: no cover
    """
    Delete this workspace (soft delete).

    The workspace enters a retention period (default 7 days, configurable up to 90 days)
    during which Fabric administrators can restore it using the restore() method.

    Example:
        >>> ws = fabric.workspace("Old Workspace")
        >>> ws.delete()
        >>> # Later, restore it:
        >>> ws.restore()
    """
    if not self._id:
        raise FabiasError("Cannot delete workspace: ID not resolved")

    self._client.delete(f"/workspaces/{self._id}")

    # Clear local state since workspace no longer exists
    self._id = None
    self._name = None
    self._capacity_id = None
    self._description = None
    self._data_fetched = False

restore(new_admin_principal_id=None, new_name=None)

Restore a deleted workspace.

Requires Fabric Administrator privileges. The workspace must be in "Deleted" state within the retention period (7-90 days, default 7).

Parameters:

Name Type Description Default
new_admin_principal_id Optional[str]

Optional principal ID to assign as workspace admin

None
new_name Optional[str]

Optional new name for the workspace (required for My workspaces)

None

Returns:

Name Type Description
Workspace Workspace

The restored workspace

Raises:

Type Description
FabiasError

If workspace ID not set or restore fails

Examples:

>>> # Restore with original settings
>>> deleted_ws = fabric.workspace(workspace_id)  # By GUID
>>> deleted_ws.restore()
>>> # Restore with new admin
>>> deleted_ws.restore(new_admin_principal_id="user-guid")
>>> # Restore My workspace with new name
>>> deleted_ws.restore(new_name="Restored Workspace")
Source code in src/fabias/_fabric/workspace.py
def restore(
    self,
    new_admin_principal_id: Optional[str] = None,
    new_name: Optional[str] = None,
) -> "Workspace":  # pragma: no cover
    """
    Restore a deleted workspace.

    Requires Fabric Administrator privileges. The workspace must be in "Deleted" state
    within the retention period (7-90 days, default 7).

    Args:
        new_admin_principal_id: Optional principal ID to assign as workspace admin
        new_name: Optional new name for the workspace (required for My workspaces)

    Returns:
        Workspace: The restored workspace

    Raises:
        FabiasError: If workspace ID not set or restore fails

    Examples:
        >>> # Restore with original settings
        >>> deleted_ws = fabric.workspace(workspace_id)  # By GUID
        >>> deleted_ws.restore()

        >>> # Restore with new admin
        >>> deleted_ws.restore(new_admin_principal_id="user-guid")

        >>> # Restore My workspace with new name
        >>> deleted_ws.restore(new_name="Restored Workspace")
    """
    if not self._id:
        raise FabiasError("Cannot restore workspace: ID not resolved")

    payload: dict[str, Any] = {}
    if new_admin_principal_id:
        payload["newWorkspaceAdminPrincipal"] = {
            "id": new_admin_principal_id,
            "type": "User",  # Could also be ServicePrincipal, Group, etc.
        }
    if new_name:
        payload["newWorkspaceName"] = new_name

    # Call admin restore API
    self._client.post(f"/admin/workspaces/{self._id}/restore", data=payload)

    # Refresh workspace data
    self._data_fetched = False
    self._fetch()

    return self

fabias.Pipeline

Bases: Item

Represents a Microsoft Fabric Data Pipeline.

Provides methods to execute pipelines and track execution status.

Examples:

>>> pipeline = workspace.pipeline("Daily ETL")
>>> job = pipeline.run()
>>> job.wait()
>>> print(f"Status: {job.status}")

With parameters:

>>> job = pipeline.run(parameters={
...     "StartDate": "2025-01-01",
...     "EndDate": "2025-01-31"
... })
Source code in src/fabias/_fabric/items/pipeline.py
class Pipeline(Item):
    """
    Represents a Microsoft Fabric Data Pipeline.

    Provides methods to execute pipelines and track execution status.

    Examples:
        >>> pipeline = workspace.pipeline("Daily ETL")
        >>> job = pipeline.run()
        >>> job.wait()
        >>> print(f"Status: {job.status}")

        With parameters:

        >>> job = pipeline.run(parameters={
        ...     "StartDate": "2025-01-01",
        ...     "EndDate": "2025-01-31"
        ... })
    """

    ITEM_TYPE = "DataPipeline"
    ITEM_TYPE_DISPLAY = "Pipeline"

    def run(self, parameters: Optional[Dict[str, str]] = None) -> AzResponse:
        """
        Execute the pipeline.

        Starts an asynchronous pipeline run and returns a Job for tracking.

        Args:
            parameters: Optional pipeline parameters as key-value pairs

        Returns:
            Job: Job object for monitoring execution

        Raises:
            FabiasError: If pipeline execution fails to start
        """
        endpoint = (
            f"/workspaces/{self.workspace_id}/items/{self.id}/jobs/instances?jobType=Pipeline"
        )
        body = parameters if parameters else {}

        try:
            return self._client.post(endpoint, data=body)

        except Exception as e:
            raise FabiasError(f"Failed to run pipeline '{self.name}': {e}")

    def __repr__(self) -> str:
        return f"Pipeline(id={self.id}, name={self.name!r})"

Functions

run(parameters=None)

Execute the pipeline.

Starts an asynchronous pipeline run and returns a Job for tracking.

Parameters:

Name Type Description Default
parameters Optional[Dict[str, str]]

Optional pipeline parameters as key-value pairs

None

Returns:

Name Type Description
Job AzResponse

Job object for monitoring execution

Raises:

Type Description
FabiasError

If pipeline execution fails to start

Source code in src/fabias/_fabric/items/pipeline.py
def run(self, parameters: Optional[Dict[str, str]] = None) -> AzResponse:
    """
    Execute the pipeline.

    Starts an asynchronous pipeline run and returns a Job for tracking.

    Args:
        parameters: Optional pipeline parameters as key-value pairs

    Returns:
        Job: Job object for monitoring execution

    Raises:
        FabiasError: If pipeline execution fails to start
    """
    endpoint = (
        f"/workspaces/{self.workspace_id}/items/{self.id}/jobs/instances?jobType=Pipeline"
    )
    body = parameters if parameters else {}

    try:
        return self._client.post(endpoint, data=body)

    except Exception as e:
        raise FabiasError(f"Failed to run pipeline '{self.name}': {e}")

fabias.Lakehouse

Bases: Item

Represents a Microsoft Fabric Lakehouse.

Provides access to lakehouse metadata and OneLake data access security (ABAC). Lakehouses are Delta Lake storage containers used for analytics workloads.

Examples:

>>> lakehouse = workspace.lakehouse("Analytics")
>>> print(f"Lakehouse ID: {lakehouse.id}")
>>>
>>> # Manage data access roles (ABAC)
>>> roles = lakehouse.accessRoles()
>>> role = lakehouse.accessRole("DefaultReader")
Source code in src/fabias/_fabric/items/lakehouse.py
class Lakehouse(Item):
    """
    Represents a Microsoft Fabric Lakehouse.

    Provides access to lakehouse metadata and OneLake data access security (ABAC).
    Lakehouses are Delta Lake storage containers used for analytics workloads.

    Examples:
        >>> lakehouse = workspace.lakehouse("Analytics")
        >>> print(f"Lakehouse ID: {lakehouse.id}")
        >>>
        >>> # Manage data access roles (ABAC)
        >>> roles = lakehouse.accessRoles()
        >>> role = lakehouse.accessRole("DefaultReader")
    """

    ITEM_TYPE = "Lakehouse"
    ITEM_TYPE_DISPLAY = "Lakehouse"

    def __repr__(self) -> str:
        return f"Lakehouse(id={self.id}, name={self.name!r})"

fabias.VariableLibrary

Bases: Item

Represents a Microsoft Fabric Variable Library.

Variable libraries store typed named variables and alternative value sets for use in application lifecycle management (ALM) pipelines. Different value sets can be activated per deployment stage (dev/test/prod) allowing the same pipeline run with different configurations.

Variable types: Boolean, DateTime, Number, Integer, String, ItemReference

Attributes:

Name Type Description
id str

Variable library GUID

name str

Display name

description str

Description

active_value_set str | None

Name of the currently active value set

Examples:

Get a variable library:

>>> lib = workspace.variableLibrary("Deployment Config")
>>> print(f"Active value set: {lib.active_value_set}")

Activate a different value set:

>>> lib.activateValueSet("Production")

Retrieve the full definition (variables + value sets):

>>> definition = lib.getDefinition()
>>> for part in definition["parts"]:
...     print(part["path"])

Set variables in bulk (SDK handles all encoding):

>>> lib.setVariables([
...     {"name": "environment", "type": "String", "value": "dev"},
...     {"name": "maxRetries",  "type": "Integer", "value": 3},
... ])

Manage value sets:

>>> lib.setValueSet("Production", {"environment": "prod", "maxRetries": "10"})
>>> lib.valueSets()   # [{"name": "Production", "variableOverrides": [...]}]
Source code in src/fabias/_fabric/items/variablelibrary.py
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
class VariableLibrary(Item):
    """
    Represents a Microsoft Fabric Variable Library.

    Variable libraries store typed named variables and alternative value sets for
    use in application lifecycle management (ALM) pipelines. Different value sets
    can be activated per deployment stage (dev/test/prod) allowing the same
    pipeline run with different configurations.

    Variable types: Boolean, DateTime, Number, Integer, String, ItemReference

    Attributes:
        id (str): Variable library GUID
        name (str): Display name
        description (str): Description
        active_value_set (str | None): Name of the currently active value set

    Examples:
        Get a variable library:

        >>> lib = workspace.variableLibrary("Deployment Config")
        >>> print(f"Active value set: {lib.active_value_set}")

        Activate a different value set:

        >>> lib.activateValueSet("Production")

        Retrieve the full definition (variables + value sets):

        >>> definition = lib.getDefinition()
        >>> for part in definition["parts"]:
        ...     print(part["path"])

        Set variables in bulk (SDK handles all encoding):

        >>> lib.setVariables([
        ...     {"name": "environment", "type": "String", "value": "dev"},
        ...     {"name": "maxRetries",  "type": "Integer", "value": 3},
        ... ])

        Manage value sets:

        >>> lib.setValueSet("Production", {"environment": "prod", "maxRetries": "10"})
        >>> lib.valueSets()   # [{"name": "Production", "variableOverrides": [...]}]
    """

    ITEM_TYPE = "VariableLibrary"
    ITEM_TYPE_DISPLAY = "VariableLibrary"

    def __init__(
        self,
        client: "FabricClient",
        workspace_id: str,
        identifier: Optional[str] = None,
        item_data: Optional[Dict[str, Any]] = None,
    ):
        # Initialize variable-library-specific attribute before super().__init__
        # because super().__init__ calls fromJson() which sets it.
        self.active_value_set: Optional[str] = None
        self.variables: VariableAccessor = VariableAccessor(self)
        super().__init__(client, workspace_id, identifier, item_data)

    def fromJson(self, data: Dict[str, Any]) -> None:
        """
        Populate from API response, capturing variable library properties.

        Extends base fromJson to also parse ``properties.activeValueSetName``.
        """
        super().fromJson(data)
        properties = data.get("properties") or {}
        self.active_value_set = properties.get("activeValueSetName")

    def _resolve_via_api(self, identifier: str) -> None:
        """
        Resolve identifier using the generic items endpoint, then fetch full
        details (including ``properties``) from the VariableLibraries endpoint.
        """
        endpoint = f"/workspaces/{self.workspace_id}/items?type=VariableLibrary"
        response = self._client.get(endpoint)
        data = response.json()
        items = data.get("value", [])

        matches = [
            item
            for item in items
            if item.get("displayName") == identifier or item.get("id") == identifier
        ]

        if not matches:
            raise NotFoundError(
                f"VariableLibrary '{identifier}' not found in workspace",
                resource_type="VariableLibrary",
                resource_id=identifier,
            )

        if len(matches) > 1:
            raise FabiasError(f"Multiple variable libraries found with identifier '{identifier}'")

        item = matches[0]
        self.id = item.get("id")
        self.name = item.get("displayName")
        self.description = item.get("description")
        self.folder_id = item.get("folderId") or item.get("folder", {}).get("id")

        # Fetch full details to populate properties (e.g. activeValueSetName)
        self.refresh()

    @property
    def _endpoint(self) -> str:
        """Base API endpoint for this variable library."""
        return f"/workspaces/{self.workspace_id}/VariableLibraries/{self.id}"

    def refresh(self) -> "VariableLibrary":
        """
        Refresh variable library metadata from the API.

        Fetches the latest state including ``active_value_set``.

        Returns:
            VariableLibrary: Self for method chaining

        Examples:
            >>> lib.refresh()
            >>> print(lib.active_value_set)
        """
        response = self._client.get(self._endpoint)
        self.fromJson(response.json())
        self.variables._invalidate()
        return self

    # =========================================================================
    # Helpers
    # =========================================================================

    def _fetchDefinition(self) -> Dict[str, Any]:
        """
        Fetch the raw definition object from the API, handling LRO transparently.

        Returns the ``definition`` dict (with ``format`` and ``parts`` keys).
        """
        response = self._client.post(f"{self._endpoint}/getDefinition")
        if response.longRunning:
            response.wait()
            data = response.result() or {}
        else:
            data = response.json()
        return data.get("definition", data)

    def getDefinition(self) -> Dict[str, Any]:
        """Fetch the raw definition (``format`` + ``parts``) from the API.

        Returns:
            dict: Definition object with ``format`` and ``parts`` keys.

        Examples:
            >>> definition = lib.getDefinition()
            >>> for part in definition["parts"]:
            ...     print(part["path"])
        """
        return self._fetchDefinition()

    def updateDefinition(
        self, parts: List[Dict[str, Any]], update_metadata: bool = False
    ) -> "VariableLibrary":
        """
        Override the variable library definition with raw parts.

        Replaces the entire definition with the provided parts. Parts must include
        at minimum ``variables.json``. Use ``valueSets/<name>.json`` for value sets
        and ``settings.json`` for ordering. Handles long-running operations (LRO)
        transparently — blocks until the operation completes.

        Args:
            parts: List of definition part dicts, each with:

                - ``path``: e.g. ``"variables.json"``,
                  ``"valueSets/Production.json"``, ``"settings.json"``
                - ``payload``: Base64-encoded JSON content
                - ``payloadType``: ``"InlineBase64"``

        Returns:
            VariableLibrary: Self for method chaining (state refreshed from API)

        Note:
            Prefer :meth:`setVariables` and :meth:`setValueSet` for common
            use cases — they handle all encoding automatically.
        """
        payload = {"definition": {"format": "VariableLibraryV1", "parts": parts}}

        response = self._client.post(
            f"{self._endpoint}/updateDefinition{'?updateMetadata=True' if update_metadata else ''}",
            data=payload,
        )
        if response.longRunning:
            response.wait()
        self.refresh()
        return self

    # =========================================================================
    # Variable management (high-level)
    # =========================================================================

    def variable(self, name: str) -> Variable:
        """
        Return a single variable by name.

        Shorthand for ``lib.variables[name]``.

        Args:
            name: Variable name

        Returns:
            Variable: The variable

        Raises:
            KeyError: If the variable does not exist

        Examples:
            >>> env = lib.variable("environment")
            >>> print(env.value, env.overrides)
        """
        return self.variables[name]

    def setVariables(self, variables: List[Dict[str, Any]]) -> "VariableLibrary":
        """
        Replace all variables in bulk.

        Fetches the current definition to preserve existing value sets and
        settings, replaces the ``variables.json`` part, and pushes the full
        updated definition back. Handles LRO transparently.

        Args:
            variables: List of variable dicts. Each must have ``name``,
                ``type``, and ``value``. ``note`` is optional. Example::

                    [
                        {"name": "environment", "type": "String", "value": "dev"},
                        {"name": "maxRetries",  "type": "Integer", "value": 3},
                        {"name": "enableCache", "type": "Boolean", "value": True},
                    ]

        Returns:
            VariableLibrary: Self for method chaining

        Examples:
            >>> lib.setVariables([
            ...     {"name": "environment", "type": "String", "value": "prod"},
            ...     {"name": "maxRetries",  "type": "Integer", "value": 5},
            ... ])
        """
        current = self._fetchDefinition()
        parts = current.get("parts", [])

        new_variables_part = {
            "path": "variables.json",
            "payload": _encode_part({"$schema": _VARIABLES_SCHEMA, "variables": variables}),
            "payloadType": "InlineBase64",
        }
        updated_parts = [p for p in parts if p.get("path") != "variables.json"]
        updated_parts.insert(0, new_variables_part)

        return self.updateDefinition(updated_parts)

    def valueSets(self) -> List[Dict[str, Any]]:
        """
        Return all value sets as a plain list of decoded dicts.

        Each value set has ``name``, optionally ``description``, and
        ``variableOverrides`` — a list of ``{name, value}`` pairs.

        Returns:
            list[dict]: Value sets decoded from ``valueSets/*.json`` parts

        Examples:
            >>> for vs in lib.valueSets():
            ...     print(vs["name"], vs.get("variableOverrides", []))
        """
        definition = self._fetchDefinition()
        result = []
        for part in definition.get("parts", []):
            path = part.get("path", "")
            if path.startswith("valueSets/") and path.endswith(".json"):
                result.append(_decode_part(part["payload"]))
        return result

    def setValueSet(
        self,
        name: str,
        overrides: Dict[str, Any],
        description: Optional[str] = None,
    ) -> "VariableLibrary":
        """
        Create or update a value set.

        Fetches the current definition, adds or replaces the named value set
        part, and pushes the full updated definition back. Handles LRO.

        Args:
            name: Value set name (also the filename — ``valueSets/{name}.json``)
            overrides: Variable overrides as ``{variable_name: override_value}``
            description: Optional description for the value set

        Returns:
            VariableLibrary: Self for method chaining

        Examples:
            >>> lib.setValueSet("Production", {
            ...     "environment": "prod",
            ...     "maxRetries": "10",
            ... })
        """
        current = self._fetchDefinition()
        parts = current.get("parts", [])

        valueset_path = f"valueSets/{name}.json"
        content: Dict[str, Any] = {
            "$schema": _VALUESET_SCHEMA,
            "name": name,
            "variableOverrides": [{"name": k, "value": v} for k, v in overrides.items()],
        }
        if description is not None:
            content["description"] = description

        new_part = {
            "path": valueset_path,
            "payload": _encode_part(content),
            "payloadType": "InlineBase64",
        }
        updated_parts = [p for p in parts if p.get("path") != valueset_path]
        updated_parts.append(new_part)

        return self.updateDefinition(updated_parts)

    def deleteValueSet(self, name: str) -> "VariableLibrary":
        """
        Remove a value set from the library.

        Fetches the current definition, drops the named value set part,
        and pushes the updated definition back. Handles LRO.

        Note:
            You cannot delete the currently active value set. Call
            :meth:`activateValueSet` on a different value set first.

        Args:
            name: Name of the value set to delete

        Returns:
            VariableLibrary: Self for method chaining

        Examples:
            >>> lib.deleteValueSet("OldStaging")
        """
        current = self._fetchDefinition()
        parts = current.get("parts", [])
        valueset_path = f"valueSets/{name}.json"
        updated_parts = [p for p in parts if p.get("path") != valueset_path]
        return self.updateDefinition(updated_parts)

    def activateValueSet(self, name: str) -> "VariableLibrary":
        """
        Set the active value set for this variable library.

        The active value set determines which variable overrides are in effect.
        Only one value set can be active at a time. You cannot delete an active
        value set—activate another first.

        Args:
            name: Name of the value set to activate

        Returns:
            VariableLibrary: Self for method chaining (``active_value_set`` updated)

        Raises:
            FabiasError: If the update request fails

        Examples:
            >>> lib.activateValueSet("Production")
            >>> print(lib.active_value_set)   # "Production"

            Switch back to default:

            >>> lib.activateValueSet("Default value set")
        """
        payload = {"properties": {"activeValueSetName": name}}
        response = self._client.patch(self._endpoint, data=payload)
        self.fromJson(response.json())
        return self

    def update(
        self,
        name: Optional[str] = None,
        description: Optional[str] = None,
        valueSet: Optional[str] = None,
    ) -> "VariableLibrary":
        """
        Update variable library metadata.

        Uses the VariableLibraries-specific PATCH endpoint, which also preserves
        the ``active_value_set`` in the returned response.

        Args:
            name: New display name
            description: New description (max 256 characters)
            valueSet: Name of the value set to activate (alternative to calling :meth:`activateValueSet` separately)
        Returns:
            VariableLibrary: Self for method chaining

        Examples:
            >>> lib.update(description="Updated for production deployment")
            >>> lib.update(name="Prod Config", description="Production variables")
        """
        payload: Dict[str, Any] = {}
        if name is not None:
            payload["displayName"] = name
        if description is not None:
            payload["description"] = description
        if valueSet is not None:
            payload["properties"] = {"activeValueSetName": valueSet}
        if not payload:
            return self

        response = self._client.patch(self._endpoint, data=payload)
        self.fromJson(response.json())
        return self

    def delete(self) -> None:  # pragma: no cover
        """
        Delete this variable library.

        Warning: This operation is permanent and cannot be undone.

        Examples:
            >>> old_lib = workspace.variableLibrary("Old Config")
            >>> old_lib.delete()
        """
        if not self.id:
            raise FabiasError("Cannot delete variable library: ID not resolved")

        self._client.delete(self._endpoint)

        self.id = None
        self.name = None
        self.description = None
        self.folder_id = None
        self.active_value_set = None

    def __repr__(self) -> str:
        return (
            f"VariableLibrary(id={self.id}, name={self.name!r}, "
            f"active_value_set={self.active_value_set!r})"
        )

Attributes

variables = VariableAccessor(self) instance-attribute

Functions

variable(name)

Return a single variable by name.

Shorthand for lib.variables[name].

Parameters:

Name Type Description Default
name str

Variable name

required

Returns:

Name Type Description
Variable Variable

The variable

Raises:

Type Description
KeyError

If the variable does not exist

Examples:

>>> env = lib.variable("environment")
>>> print(env.value, env.overrides)
Source code in src/fabias/_fabric/items/variablelibrary.py
def variable(self, name: str) -> Variable:
    """
    Return a single variable by name.

    Shorthand for ``lib.variables[name]``.

    Args:
        name: Variable name

    Returns:
        Variable: The variable

    Raises:
        KeyError: If the variable does not exist

    Examples:
        >>> env = lib.variable("environment")
        >>> print(env.value, env.overrides)
    """
    return self.variables[name]

valueSets()

Return all value sets as a plain list of decoded dicts.

Each value set has name, optionally description, and variableOverrides — a list of {name, value} pairs.

Returns:

Type Description
List[Dict[str, Any]]

list[dict]: Value sets decoded from valueSets/*.json parts

Examples:

>>> for vs in lib.valueSets():
...     print(vs["name"], vs.get("variableOverrides", []))
Source code in src/fabias/_fabric/items/variablelibrary.py
def valueSets(self) -> List[Dict[str, Any]]:
    """
    Return all value sets as a plain list of decoded dicts.

    Each value set has ``name``, optionally ``description``, and
    ``variableOverrides`` — a list of ``{name, value}`` pairs.

    Returns:
        list[dict]: Value sets decoded from ``valueSets/*.json`` parts

    Examples:
        >>> for vs in lib.valueSets():
        ...     print(vs["name"], vs.get("variableOverrides", []))
    """
    definition = self._fetchDefinition()
    result = []
    for part in definition.get("parts", []):
        path = part.get("path", "")
        if path.startswith("valueSets/") and path.endswith(".json"):
            result.append(_decode_part(part["payload"]))
    return result

setValueSet(name, overrides, description=None)

Create or update a value set.

Fetches the current definition, adds or replaces the named value set part, and pushes the full updated definition back. Handles LRO.

Parameters:

Name Type Description Default
name str

Value set name (also the filename — valueSets/{name}.json)

required
overrides Dict[str, Any]

Variable overrides as {variable_name: override_value}

required
description Optional[str]

Optional description for the value set

None

Returns:

Name Type Description
VariableLibrary VariableLibrary

Self for method chaining

Examples:

>>> lib.setValueSet("Production", {
...     "environment": "prod",
...     "maxRetries": "10",
... })
Source code in src/fabias/_fabric/items/variablelibrary.py
def setValueSet(
    self,
    name: str,
    overrides: Dict[str, Any],
    description: Optional[str] = None,
) -> "VariableLibrary":
    """
    Create or update a value set.

    Fetches the current definition, adds or replaces the named value set
    part, and pushes the full updated definition back. Handles LRO.

    Args:
        name: Value set name (also the filename — ``valueSets/{name}.json``)
        overrides: Variable overrides as ``{variable_name: override_value}``
        description: Optional description for the value set

    Returns:
        VariableLibrary: Self for method chaining

    Examples:
        >>> lib.setValueSet("Production", {
        ...     "environment": "prod",
        ...     "maxRetries": "10",
        ... })
    """
    current = self._fetchDefinition()
    parts = current.get("parts", [])

    valueset_path = f"valueSets/{name}.json"
    content: Dict[str, Any] = {
        "$schema": _VALUESET_SCHEMA,
        "name": name,
        "variableOverrides": [{"name": k, "value": v} for k, v in overrides.items()],
    }
    if description is not None:
        content["description"] = description

    new_part = {
        "path": valueset_path,
        "payload": _encode_part(content),
        "payloadType": "InlineBase64",
    }
    updated_parts = [p for p in parts if p.get("path") != valueset_path]
    updated_parts.append(new_part)

    return self.updateDefinition(updated_parts)

deleteValueSet(name)

Remove a value set from the library.

Fetches the current definition, drops the named value set part, and pushes the updated definition back. Handles LRO.

Note

You cannot delete the currently active value set. Call :meth:activateValueSet on a different value set first.

Parameters:

Name Type Description Default
name str

Name of the value set to delete

required

Returns:

Name Type Description
VariableLibrary VariableLibrary

Self for method chaining

Examples:

>>> lib.deleteValueSet("OldStaging")
Source code in src/fabias/_fabric/items/variablelibrary.py
def deleteValueSet(self, name: str) -> "VariableLibrary":
    """
    Remove a value set from the library.

    Fetches the current definition, drops the named value set part,
    and pushes the updated definition back. Handles LRO.

    Note:
        You cannot delete the currently active value set. Call
        :meth:`activateValueSet` on a different value set first.

    Args:
        name: Name of the value set to delete

    Returns:
        VariableLibrary: Self for method chaining

    Examples:
        >>> lib.deleteValueSet("OldStaging")
    """
    current = self._fetchDefinition()
    parts = current.get("parts", [])
    valueset_path = f"valueSets/{name}.json"
    updated_parts = [p for p in parts if p.get("path") != valueset_path]
    return self.updateDefinition(updated_parts)

activateValueSet(name)

Set the active value set for this variable library.

The active value set determines which variable overrides are in effect. Only one value set can be active at a time. You cannot delete an active value set—activate another first.

Parameters:

Name Type Description Default
name str

Name of the value set to activate

required

Returns:

Name Type Description
VariableLibrary VariableLibrary

Self for method chaining (active_value_set updated)

Raises:

Type Description
FabiasError

If the update request fails

Examples:

>>> lib.activateValueSet("Production")
>>> print(lib.active_value_set)   # "Production"

Switch back to default:

>>> lib.activateValueSet("Default value set")
Source code in src/fabias/_fabric/items/variablelibrary.py
def activateValueSet(self, name: str) -> "VariableLibrary":
    """
    Set the active value set for this variable library.

    The active value set determines which variable overrides are in effect.
    Only one value set can be active at a time. You cannot delete an active
    value set—activate another first.

    Args:
        name: Name of the value set to activate

    Returns:
        VariableLibrary: Self for method chaining (``active_value_set`` updated)

    Raises:
        FabiasError: If the update request fails

    Examples:
        >>> lib.activateValueSet("Production")
        >>> print(lib.active_value_set)   # "Production"

        Switch back to default:

        >>> lib.activateValueSet("Default value set")
    """
    payload = {"properties": {"activeValueSetName": name}}
    response = self._client.patch(self._endpoint, data=payload)
    self.fromJson(response.json())
    return self

setVariables(variables)

Replace all variables in bulk.

Fetches the current definition to preserve existing value sets and settings, replaces the variables.json part, and pushes the full updated definition back. Handles LRO transparently.

Parameters:

Name Type Description Default
variables List[Dict[str, Any]]

List of variable dicts. Each must have name, type, and value. note is optional. Example::

[
    {"name": "environment", "type": "String", "value": "dev"},
    {"name": "maxRetries",  "type": "Integer", "value": 3},
    {"name": "enableCache", "type": "Boolean", "value": True},
]
required

Returns:

Name Type Description
VariableLibrary VariableLibrary

Self for method chaining

Examples:

>>> lib.setVariables([
...     {"name": "environment", "type": "String", "value": "prod"},
...     {"name": "maxRetries",  "type": "Integer", "value": 5},
... ])
Source code in src/fabias/_fabric/items/variablelibrary.py
def setVariables(self, variables: List[Dict[str, Any]]) -> "VariableLibrary":
    """
    Replace all variables in bulk.

    Fetches the current definition to preserve existing value sets and
    settings, replaces the ``variables.json`` part, and pushes the full
    updated definition back. Handles LRO transparently.

    Args:
        variables: List of variable dicts. Each must have ``name``,
            ``type``, and ``value``. ``note`` is optional. Example::

                [
                    {"name": "environment", "type": "String", "value": "dev"},
                    {"name": "maxRetries",  "type": "Integer", "value": 3},
                    {"name": "enableCache", "type": "Boolean", "value": True},
                ]

    Returns:
        VariableLibrary: Self for method chaining

    Examples:
        >>> lib.setVariables([
        ...     {"name": "environment", "type": "String", "value": "prod"},
        ...     {"name": "maxRetries",  "type": "Integer", "value": 5},
        ... ])
    """
    current = self._fetchDefinition()
    parts = current.get("parts", [])

    new_variables_part = {
        "path": "variables.json",
        "payload": _encode_part({"$schema": _VARIABLES_SCHEMA, "variables": variables}),
        "payloadType": "InlineBase64",
    }
    updated_parts = [p for p in parts if p.get("path") != "variables.json"]
    updated_parts.insert(0, new_variables_part)

    return self.updateDefinition(updated_parts)

updateDefinition(parts, update_metadata=False)

Override the variable library definition with raw parts.

Replaces the entire definition with the provided parts. Parts must include at minimum variables.json. Use valueSets/<name>.json for value sets and settings.json for ordering. Handles long-running operations (LRO) transparently — blocks until the operation completes.

Parameters:

Name Type Description Default
parts List[Dict[str, Any]]

List of definition part dicts, each with:

  • path: e.g. "variables.json", "valueSets/Production.json", "settings.json"
  • payload: Base64-encoded JSON content
  • payloadType: "InlineBase64"
required

Returns:

Name Type Description
VariableLibrary VariableLibrary

Self for method chaining (state refreshed from API)

Note

Prefer :meth:setVariables and :meth:setValueSet for common use cases — they handle all encoding automatically.

Source code in src/fabias/_fabric/items/variablelibrary.py
def updateDefinition(
    self, parts: List[Dict[str, Any]], update_metadata: bool = False
) -> "VariableLibrary":
    """
    Override the variable library definition with raw parts.

    Replaces the entire definition with the provided parts. Parts must include
    at minimum ``variables.json``. Use ``valueSets/<name>.json`` for value sets
    and ``settings.json`` for ordering. Handles long-running operations (LRO)
    transparently — blocks until the operation completes.

    Args:
        parts: List of definition part dicts, each with:

            - ``path``: e.g. ``"variables.json"``,
              ``"valueSets/Production.json"``, ``"settings.json"``
            - ``payload``: Base64-encoded JSON content
            - ``payloadType``: ``"InlineBase64"``

    Returns:
        VariableLibrary: Self for method chaining (state refreshed from API)

    Note:
        Prefer :meth:`setVariables` and :meth:`setValueSet` for common
        use cases — they handle all encoding automatically.
    """
    payload = {"definition": {"format": "VariableLibraryV1", "parts": parts}}

    response = self._client.post(
        f"{self._endpoint}/updateDefinition{'?updateMetadata=True' if update_metadata else ''}",
        data=payload,
    )
    if response.longRunning:
        response.wait()
    self.refresh()
    return self

refresh()

Refresh variable library metadata from the API.

Fetches the latest state including active_value_set.

Returns:

Name Type Description
VariableLibrary VariableLibrary

Self for method chaining

Examples:

>>> lib.refresh()
>>> print(lib.active_value_set)
Source code in src/fabias/_fabric/items/variablelibrary.py
def refresh(self) -> "VariableLibrary":
    """
    Refresh variable library metadata from the API.

    Fetches the latest state including ``active_value_set``.

    Returns:
        VariableLibrary: Self for method chaining

    Examples:
        >>> lib.refresh()
        >>> print(lib.active_value_set)
    """
    response = self._client.get(self._endpoint)
    self.fromJson(response.json())
    self.variables._invalidate()
    return self

update(name=None, description=None, valueSet=None)

Update variable library metadata.

Uses the VariableLibraries-specific PATCH endpoint, which also preserves the active_value_set in the returned response.

Parameters:

Name Type Description Default
name Optional[str]

New display name

None
description Optional[str]

New description (max 256 characters)

None
valueSet Optional[str]

Name of the value set to activate (alternative to calling :meth:activateValueSet separately)

None

Returns: VariableLibrary: Self for method chaining

Examples:

>>> lib.update(description="Updated for production deployment")
>>> lib.update(name="Prod Config", description="Production variables")
Source code in src/fabias/_fabric/items/variablelibrary.py
def update(
    self,
    name: Optional[str] = None,
    description: Optional[str] = None,
    valueSet: Optional[str] = None,
) -> "VariableLibrary":
    """
    Update variable library metadata.

    Uses the VariableLibraries-specific PATCH endpoint, which also preserves
    the ``active_value_set`` in the returned response.

    Args:
        name: New display name
        description: New description (max 256 characters)
        valueSet: Name of the value set to activate (alternative to calling :meth:`activateValueSet` separately)
    Returns:
        VariableLibrary: Self for method chaining

    Examples:
        >>> lib.update(description="Updated for production deployment")
        >>> lib.update(name="Prod Config", description="Production variables")
    """
    payload: Dict[str, Any] = {}
    if name is not None:
        payload["displayName"] = name
    if description is not None:
        payload["description"] = description
    if valueSet is not None:
        payload["properties"] = {"activeValueSetName": valueSet}
    if not payload:
        return self

    response = self._client.patch(self._endpoint, data=payload)
    self.fromJson(response.json())
    return self

delete()

Delete this variable library.

Warning: This operation is permanent and cannot be undone.

Examples:

>>> old_lib = workspace.variableLibrary("Old Config")
>>> old_lib.delete()
Source code in src/fabias/_fabric/items/variablelibrary.py
def delete(self) -> None:  # pragma: no cover
    """
    Delete this variable library.

    Warning: This operation is permanent and cannot be undone.

    Examples:
        >>> old_lib = workspace.variableLibrary("Old Config")
        >>> old_lib.delete()
    """
    if not self.id:
        raise FabiasError("Cannot delete variable library: ID not resolved")

    self._client.delete(self._endpoint)

    self.id = None
    self.name = None
    self.description = None
    self.folder_id = None
    self.active_value_set = None

fabias.VariableAccessor

Lazy-loading, cache-backed accessor for variables in a :class:VariableLibrary.

Fetches the variable collection once on first access, then all reads and in-memory mutations are zero-cost. Call :meth:commit when finished to push all accumulated changes in a single updateDefinition call.

Read access::

var  = lib.variables["environment"]        # Variable — KeyError if missing
var  = lib.variables.get("region")         # Variable or None
exists = "maxRetries" in lib.variables
all  = lib.variables()                     # List[Variable]
for var in lib.variables: ...
for name, var in lib.variables.items(): ...

In-memory mutation (zero API calls)::

lib.variables.add("region", "String", "eastus")
lib.variables["environment"].value = "prod"   # direct field mutation
lib.variables.remove("legacyFlag")

Commit everything at once::

lib.variables.commit()                     # one updateDefinition call

# Or chain before committing
lib.variables.add("region", "String", "eastus").add("tier", "String", "standard").commit()
Source code in src/fabias/_fabric/items/variablelibrary.py
class VariableAccessor:
    """
    Lazy-loading, cache-backed accessor for variables in a :class:`VariableLibrary`.

    Fetches the variable collection **once** on first access, then all reads and
    in-memory mutations are zero-cost. Call :meth:`commit` when finished to push
    all accumulated changes in a **single** ``updateDefinition`` call.

    Read access::

        var  = lib.variables["environment"]        # Variable — KeyError if missing
        var  = lib.variables.get("region")         # Variable or None
        exists = "maxRetries" in lib.variables
        all  = lib.variables()                     # List[Variable]
        for var in lib.variables: ...
        for name, var in lib.variables.items(): ...

    In-memory mutation (zero API calls)::

        lib.variables.add("region", "String", "eastus")
        lib.variables["environment"].value = "prod"   # direct field mutation
        lib.variables.remove("legacyFlag")

    Commit everything at once::

        lib.variables.commit()                     # one updateDefinition call

        # Or chain before committing
        lib.variables.add("region", "String", "eastus").add("tier", "String", "standard").commit()
    """

    def __init__(self, library: "VariableLibrary") -> None:
        self._lib = library
        self._cache: Optional[Dict[str, Variable]] = None
        self._valuesets: Dict[str, Dict[str, Any]] = {}
        self._platform: Optional[str] = None
        self._settings: Optional[Dict[str, Union[str, List[str]]]] = None

        self._schemas: VariableSchemas = VariableSchemas()

    # -----------------------------------------------------------------------
    # Internal
    # -----------------------------------------------------------------------

    def _load(self) -> Dict[str, Variable]:
        """Fetch and cache the variable collection (runs at most once)."""
        if self._cache is not None:
            return self._cache

        definition = self._lib._fetchDefinition()
        parts = definition.get("parts", [])

        vars_part = next((p for p in parts if p.get("path") == "variables.json"), None)
        settings_part = next((p for p in parts if p.get("path") == "settings.json"), None)
        platform_part = next((p for p in parts if p.get("path") == ".platform"), None)

        self._cache = {}
        if vars_part:
            vars_decoded = _decode_part(vars_part["payload"])
            self._schemas.variables = vars_decoded.get("$schema", _VARIABLES_SCHEMA)
            for v in vars_decoded.get("variables", []):
                vtype = v.get("type", "String")
                self._cache[v["name"]] = Variable(
                    name=v["name"],
                    type=vtype,
                    value=v.get("value"),
                    note=v.get("note", ""),
                    overrides={},
                )

        # Populate overrides from value set parts
        _skip = {"variables.json", "settings.json", ".platform"}
        for part in (p for p in parts if p.get("path") not in _skip):
            m = re.match(r"valueSets/(.+)\.json$", part.get("path", ""))
            value_set = m.group(1) if m else None
            if value_set is None:
                continue
            pld = _decode_part(part["payload"])
            self._schemas.valueSet = pld.get("$schema", _VALUESET_SCHEMA)
            self._valuesets[value_set] = {}
            for vr in pld.get("variableOverrides", []):
                var_name = vr.get("name")
                if var_name in self._cache and value_set:
                    self._valuesets[value_set][var_name] = vr.get("value")
                    cached_var = self._cache[var_name]
                    if cached_var.overrides is None:
                        cached_var.overrides = {}
                    cached_var.overrides[value_set] = cast(
                        Union[bool, str, int, float, ItemReference, datetime],
                        _coerce_value(cached_var.type, vr.get("value")),
                    )

        decoded_settings = _decode_part(settings_part["payload"]) if settings_part else None
        if decoded_settings:
            self._schemas.settings = decoded_settings.get("$schema", _SETTINGS_SCHEMA)
            self._settings = {"valueSetsOrder": decoded_settings.get("valueSetsOrder", [])}
        self._platform = platform_part if platform_part else None

        return self._cache

    def _invalidate(self) -> None:
        """Clear the cache and accumulated state. Called by :meth:`VariableLibrary.refresh` and after :meth:`commit`."""
        self._cache = None
        self._valuesets = {}
        self._settings = None
        self._platform = None

    # -----------------------------------------------------------------------
    # Read interface
    # -----------------------------------------------------------------------
    def __getitem__(self, name: str) -> Variable:
        result = self._load().get(name)
        if result is None:
            raise KeyError(f"Variable '{name}' not found in library")
        return result

    def __contains__(self, name: object) -> bool:
        return name in self._load()

    def __iter__(self):
        return iter(self._load().values())

    def __len__(self) -> int:
        return len(self._load())

    def __call__(self) -> List[Variable]:
        """Return all variables as a list."""
        return list(self._load().values())

    def get(self, name: str, default: Optional[Variable] = None) -> Optional[Variable]:
        """Return the variable by name, or *default* if not found."""
        return self._load().get(name, default)

    def items(self):
        """Yield ``(name, Variable)`` pairs."""
        return self._load().items()

    def keys(self):
        """Return variable names."""
        return self._load().keys()

    def values(self):
        """Return :class:`Variable` objects."""
        return self._load().values()

    # -----------------------------------------------------------------------
    # In-memory mutation (no API calls)
    # -----------------------------------------------------------------------

    @overload
    def add(self, variable: Variable) -> "VariableAccessor": ...

    @overload
    def add(
        self, name: str, value: Any = None, type: Optional[str] = None, note: Optional[str] = None
    ) -> "VariableAccessor": ...

    def add(
        self,
        name_or_variable: Union[str, Variable],
        value: Any = None,
        type: Optional[str] = None,
        note: Optional[str] = None,
    ) -> "VariableAccessor":
        """
        Add a variable to the in-memory collection.

        Does **not** call the API. Call :meth:`commit` when finished.

        Accepts either a :class:`Variable` object (with overrides pre-populated)
        or individual parameters for quick additions.

        Args:
            name_or_variable: A :class:`Variable` instance, or a variable name string
            type: Fabric type — ``"Boolean"``, ``"DateTime"``, ``"Integer"``,
                ``"Number"``, ``"String"``, ``"ItemReference"``, or ``"Guid"``.
                Inferred from the Python type of ``value`` when omitted.
                ``"Guid"`` must always be passed explicitly (stored as ``str``,
                indistinguishable from ``"String"`` by Python type alone).
                Only used when ``name_or_variable`` is a string.
            value: Value (only used when ``name_or_variable`` is a string)
            note: Optional note (only used when ``name_or_variable`` is a string)

        Returns:
            VariableAccessor: Self for chaining

        Examples:
            Quick add — type inferred from value:

            >>> lib.variables.add("region", "eastus").commit()             # → String
            >>> lib.variables.add("retries", 3).commit()                   # → Integer
            >>> lib.variables.add("enabled", True).commit()                # → Boolean

            Explicit type (required for Guid):

            >>> lib.variables.add("correlationId", "some-guid", type="Guid").commit()

            With a pre-built Variable (including overrides):

            >>> var = Variable("region", "eastus",
            ...                overrides={"Production": "westus2", "Staging": "centralus"})
            >>> lib.variables.add(var).commit()

            Duplicate names are silently replaced (upsert):

            >>> lib.variables.add("region", "westus2").commit()
        """
        if isinstance(name_or_variable, Variable):
            var = name_or_variable
        else:
            var = Variable(
                name=name_or_variable,
                value=value,
                type=type,
                note=note or "",
                overrides={},
            )

        # Ensure overrides is a dict (not None)
        if var.overrides is None:
            var.overrides = {}
        self._load()[var.name] = var
        return self

    def remove(self, name: str) -> "VariableAccessor":
        """
        Remove a variable from the in-memory collection.

        Does **not** call the API. Call :meth:`commit` when finished.

        Args:
            name: Variable name to remove

        Returns:
            VariableAccessor: Self for chaining

        Raises:
            NotFoundError: If the variable does not exist

        Examples:
            >>> lib.variables.remove("legacyFlag").commit()
        """
        cache = self._load()
        if name not in cache:
            raise NotFoundError(
                f"Variable '{name}' not found", resource_type="Variable", resource_id=name
            )
        del cache[name]
        return self

    # -----------------------------------------------------------------------
    # Commit
    # -----------------------------------------------------------------------

    def commit(self, update_meta: bool = False) -> "VariableLibrary":
        """
        Push all in-memory changes to the API in a single ``updateDefinition`` call.

        Serializes the current variable collection — defaults into ``variables.json``
        and overrides back into their respective ``valueSets/*.json`` parts (preserving
        existing name/description metadata). The other parts (settings, platform) were
        cached on the initial fetch, so no extra API call is needed.

        Returns:
            VariableLibrary: The parent library (state refreshed after commit)

        Examples:
            >>> lib.variables["environment"].value = "prod"
            >>> lib.variables["environment"].overrides["Staging"] = "staging"
            >>> lib.variables.add("region", "eastus")
            >>> lib.variables.add(Variable("tier", "free", overrides={"Production": "premium"}))
            >>> lib.variables.remove("legacyFlag")
            >>> lib.variables.commit()
        """
        if self._cache is None:
            return self._lib  # nothing loaded — nothing to commit

        # ── Build variables.json ─────────────────────────────────────────────
        serialized = [
            {
                k: v
                for k, v in _to_json_safe(asdict(var)).items()
                if k != "overrides" and v is not None
            }
            for var in self._cache.values()
        ]
        vars_part = {
            "path": "variables.json",
            "payload": _encode_part({"$schema": self._schemas.variables, "variables": serialized}),
            "payloadType": "InlineBase64",
        }

        # ── Rebuild value sets from scratch using only the current cache ──────
        # This ensures removed variables are excluded and stale entries don't
        # pollute the payload.
        final_valuesets: Dict[str, Dict[str, Any]] = {}
        new_vs_names: set = set()
        existing_order: List[str] = list((self._settings or {}).get("valueSetsOrder", []))

        for var in self._cache.values():
            var_dict = _to_json_safe(asdict(var))
            for vs_name, val in (var_dict.get("overrides") or {}).items():
                final_valuesets.setdefault(vs_name, {})[var.name] = val
                if vs_name not in existing_order:
                    new_vs_names.add(vs_name)

        # Drop empty value sets (can happen when a variable is removed)
        final_valuesets = {k: v for k, v in final_valuesets.items() if v}

        # Prune existing_order: remove any names that are now empty / gone
        updated_order = [n for n in existing_order if n in final_valuesets]
        # Append new value set names in sorted order
        final_order = updated_order + sorted(new_vs_names)

        # ── Helper ────────────────────────────────────────────────────────────
        def _make_vs_part(vs_name: str, overrides: Dict[str, Any]) -> Dict[str, Any]:
            return {
                "path": f"valueSets/{vs_name}.json",
                "payload": _encode_part(
                    {
                        "$schema": self._schemas.valueSet,
                        "name": vs_name,
                        "variableOverrides": [
                            {"name": k, "value": v} for k, v in overrides.items()
                        ],
                    }
                ),
                "payloadType": "InlineBase64",
            }

        platform_parts = [self._platform] if (update_meta and self._platform) else []

        all_vs_parts = [
            _make_vs_part(vs_name, overrides) for vs_name, overrides in final_valuesets.items()
        ]
        all_parts = (
            [vars_part]
            + all_vs_parts
            + [
                {
                    "path": "settings.json",
                    "payload": _encode_part(
                        {"$schema": self._schemas.settings, "valueSetsOrder": final_order}
                    ),
                    "payloadType": "InlineBase64",
                }
            ]
            + platform_parts
        )

        self._invalidate()
        return self._lib.updateDefinition(all_parts, update_meta)

    def __repr__(self) -> str:
        if self._cache is not None:
            return f"VariableAccessor({list(self._cache.keys())!r})"
        return "VariableAccessor(<not loaded>)"

Functions

__call__()

Return all variables as a list.

Source code in src/fabias/_fabric/items/variablelibrary.py
def __call__(self) -> List[Variable]:
    """Return all variables as a list."""
    return list(self._load().values())

add(name_or_variable, value=None, type=None, note=None)

add(variable: Variable) -> VariableAccessor
add(name: str, value: Any = None, type: Optional[str] = None, note: Optional[str] = None) -> VariableAccessor

Add a variable to the in-memory collection.

Does not call the API. Call :meth:commit when finished.

Accepts either a :class:Variable object (with overrides pre-populated) or individual parameters for quick additions.

Parameters:

Name Type Description Default
name_or_variable Union[str, Variable]

A :class:Variable instance, or a variable name string

required
type Optional[str]

Fabric type — "Boolean", "DateTime", "Integer", "Number", "String", "ItemReference", or "Guid". Inferred from the Python type of value when omitted. "Guid" must always be passed explicitly (stored as str, indistinguishable from "String" by Python type alone). Only used when name_or_variable is a string.

None
value Any

Value (only used when name_or_variable is a string)

None
note Optional[str]

Optional note (only used when name_or_variable is a string)

None

Returns:

Name Type Description
VariableAccessor VariableAccessor

Self for chaining

Examples:

Quick add — type inferred from value:

>>> lib.variables.add("region", "eastus").commit()             # → String
>>> lib.variables.add("retries", 3).commit()                   # → Integer
>>> lib.variables.add("enabled", True).commit()                # → Boolean

Explicit type (required for Guid):

>>> lib.variables.add("correlationId", "some-guid", type="Guid").commit()

With a pre-built Variable (including overrides):

>>> var = Variable("region", "eastus",
...                overrides={"Production": "westus2", "Staging": "centralus"})
>>> lib.variables.add(var).commit()

Duplicate names are silently replaced (upsert):

>>> lib.variables.add("region", "westus2").commit()
Source code in src/fabias/_fabric/items/variablelibrary.py
def add(
    self,
    name_or_variable: Union[str, Variable],
    value: Any = None,
    type: Optional[str] = None,
    note: Optional[str] = None,
) -> "VariableAccessor":
    """
    Add a variable to the in-memory collection.

    Does **not** call the API. Call :meth:`commit` when finished.

    Accepts either a :class:`Variable` object (with overrides pre-populated)
    or individual parameters for quick additions.

    Args:
        name_or_variable: A :class:`Variable` instance, or a variable name string
        type: Fabric type — ``"Boolean"``, ``"DateTime"``, ``"Integer"``,
            ``"Number"``, ``"String"``, ``"ItemReference"``, or ``"Guid"``.
            Inferred from the Python type of ``value`` when omitted.
            ``"Guid"`` must always be passed explicitly (stored as ``str``,
            indistinguishable from ``"String"`` by Python type alone).
            Only used when ``name_or_variable`` is a string.
        value: Value (only used when ``name_or_variable`` is a string)
        note: Optional note (only used when ``name_or_variable`` is a string)

    Returns:
        VariableAccessor: Self for chaining

    Examples:
        Quick add — type inferred from value:

        >>> lib.variables.add("region", "eastus").commit()             # → String
        >>> lib.variables.add("retries", 3).commit()                   # → Integer
        >>> lib.variables.add("enabled", True).commit()                # → Boolean

        Explicit type (required for Guid):

        >>> lib.variables.add("correlationId", "some-guid", type="Guid").commit()

        With a pre-built Variable (including overrides):

        >>> var = Variable("region", "eastus",
        ...                overrides={"Production": "westus2", "Staging": "centralus"})
        >>> lib.variables.add(var).commit()

        Duplicate names are silently replaced (upsert):

        >>> lib.variables.add("region", "westus2").commit()
    """
    if isinstance(name_or_variable, Variable):
        var = name_or_variable
    else:
        var = Variable(
            name=name_or_variable,
            value=value,
            type=type,
            note=note or "",
            overrides={},
        )

    # Ensure overrides is a dict (not None)
    if var.overrides is None:
        var.overrides = {}
    self._load()[var.name] = var
    return self

fabias.Variable dataclass

Represents a single variable in a variable library.

type is optional — it is inferred from the Python type of value when omitted::

Variable("region", "eastus")                  # str   → "String"
Variable("retries", 3)                          # int   → "Integer"
Variable("ratio", 0.5)                          # float → "Number"
Variable("enabled", True)                       # bool  → "Boolean"
Variable("lh", ItemReference(...))              # → "ItemReference"
Variable("ts", datetime(2024, 1, 1))            # → "DateTime"

Guid values are stored as Python strings and cannot be distinguished from String automatically — pass type="Guid" explicitly when needed.

Source code in src/fabias/_fabric/items/variablelibrary.py
@dataclass(init=False)
class Variable:
    """Represents a single variable in a variable library.

    ``type`` is optional — it is inferred from the Python type of ``value`` when
    omitted::

        Variable("region", "eastus")                  # str   → "String"
        Variable("retries", 3)                          # int   → "Integer"
        Variable("ratio", 0.5)                          # float → "Number"
        Variable("enabled", True)                       # bool  → "Boolean"
        Variable("lh", ItemReference(...))              # → "ItemReference"
        Variable("ts", datetime(2024, 1, 1))            # → "DateTime"

    ``Guid`` values are stored as Python strings and cannot be distinguished from
    ``String`` automatically — pass ``type="Guid"`` explicitly when needed.
    """

    name: str
    type: str
    value: Union[bool, str, int, float, "ItemReference", datetime]
    note: str = ""
    overrides: Optional[Dict[str, Union[bool, str, int, float, "ItemReference", datetime]]] = None

    def __init__(
        self,
        name: str,
        value: Any = None,
        type: Optional[str] = None,
        note: Optional[str] = "",
        overrides: Optional[Dict] = None,
    ):
        self.name = name
        self.note = note or ""
        self.overrides = overrides
        resolved: str
        if type is not None:
            resolved = type
        elif isinstance(value, bool):
            resolved = "Boolean"
        elif isinstance(value, int):
            resolved = "Integer"
        elif isinstance(value, float):
            resolved = "Number"
        elif isinstance(value, datetime):
            resolved = "DateTime"
        elif isinstance(value, ItemReference):
            resolved = "ItemReference"  # noqa: F821
        elif isinstance(value, str):
            resolved = "String"
        else:
            raise FabiasError(
                f"Cannot infer Fabric type from Python type '{value.__class__.__name__}'; "
                "pass type= explicitly (e.g. type='Guid')"
            )
        self.type = resolved
        self.value = cast(
            Union[bool, str, int, float, ItemReference, datetime],
            _coerce_value(resolved, value),
        )

    def override(
        self, value_set: str, value: Union[bool, str, int, float, "ItemReference", datetime]
    ) -> None:
        """Add or update an override value for a specific value set."""
        if self.overrides is None:
            self.overrides = {}
        self.overrides[value_set] = cast(
            Union[bool, str, int, float, ItemReference, datetime],
            _coerce_value(self.type, value),
        )

    def __repr__(self):
        return f"Variable(name={self.name!r}, type={self.type!r}, value={self.value!r}, overrides={self.overrides!r})"

Attributes

name = name instance-attribute

type = resolved instance-attribute

value = cast(Union[bool, str, int, float, ItemReference, datetime], _coerce_value(resolved, value)) instance-attribute

note = note or '' class-attribute instance-attribute

overrides = overrides class-attribute instance-attribute

Functions

override(value_set, value)

Add or update an override value for a specific value set.

Source code in src/fabias/_fabric/items/variablelibrary.py
def override(
    self, value_set: str, value: Union[bool, str, int, float, "ItemReference", datetime]
) -> None:
    """Add or update an override value for a specific value set."""
    if self.overrides is None:
        self.overrides = {}
    self.overrides[value_set] = cast(
        Union[bool, str, int, float, ItemReference, datetime],
        _coerce_value(self.type, value),
    )

fabias.ItemReference dataclass

Represents a reference to another item, used for ItemReference variable types.

Source code in src/fabias/_fabric/items/variablelibrary.py
@dataclass
class ItemReference:
    """Represents a reference to another item, used for ItemReference variable types."""

    itemId: str
    workspaceId: str

fabias.Connection

Represents a Fabric connection.

Attributes:

Name Type Description
id str

Connection unique identifier

name str

Connection display name

connectionType str

Type of connection

connectivityType str

Connectivity type (OnPremises, VirtualNetwork, Cloud)

privacyLevel str

Privacy level setting

credentialDetails dict

Credential configuration details

Source code in src/fabias/_fabric/connections.py
class Connection:
    """
    Represents a Fabric connection.

    Attributes:
        id (str): Connection unique identifier
        name (str): Connection display name
        connectionType (str): Type of connection
        connectivityType (str): Connectivity type (OnPremises, VirtualNetwork, Cloud)
        privacyLevel (str): Privacy level setting
        credentialDetails (dict): Credential configuration details
    """

    def __init__(
        self,
        client: "FabricClient",
        identifier: Optional[str] = None,
        connection_data: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize Connection.

        Args:
            client: Authenticated FabricClient
            identifier: Connection name or GUID (triggers lazy loading or immediate resolution)
            connection_data: Pre-populated connection data from API (skips resolution)
        """
        self._client = client
        self._data_fetched = False

        # Attributes (lazy loaded)
        self._id: Optional[str] = None
        self._name: Optional[str] = None
        self._connection_type: Optional[str] = None
        self._connectivity_type: Optional[str] = None
        self._privacy_level: Optional[str] = None
        self._credential_details: Optional[dict] = None

        # Pre-populated from API response
        if connection_data:
            self._id = connection_data.get("id")
            self._name = connection_data.get("displayName")
            self._connection_type = connection_data.get("connectivityType")
            self._connectivity_type = connection_data.get("connectivityType")
            self._privacy_level = connection_data.get("privacyLevel")
            self._credential_details = connection_data.get("credentialDetails", {})
            self._data_fetched = True
            return

        # GUID provided - store and return (no API call)
        if identifier and GUID.valid(identifier):
            self._id = identifier
            return

        # Name provided - resolve to GUID (API call happens here)
        if identifier:
            self._resolve_identifier(identifier)

    def _resolve_identifier(self, name: str) -> None:
        """
        Resolve connection name to GUID.

        Searches all connections and populates all data since we're making an API call.

        Args:
            name: Connection display name
        """
        # Search by name using Connections handler
        from .connections import Connections

        handler = Connections(self._client)
        matches = handler.list(name=name)

        if not matches:
            from .._shared.exceptions import NotFoundError

            raise NotFoundError(
                f"Connection '{name}' not found", resource_type="Connection", resource_id=name
            )

        # Exact match preferred
        exact = [c for c in matches if c.name == name]
        if len(exact) == 1:
            matched = exact[0]
        elif len(matches) == 1:
            matched = matches[0]
        else:
            from .._shared.exceptions import FabiasError

            raise FabiasError(
                f"Multiple connections found matching '{name}': "
                + ", ".join(c.name for c in matches[:5])
            )

        # Copy all properties from matched connection
        self._id = matched.id
        self._name = matched.name
        self._connection_type = matched.connectionType
        self._connectivity_type = matched.connectivityType
        self._privacy_level = matched.privacyLevel
        self._credential_details = matched.credentialDetails
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load connection data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/connections/{self._id}")
        data = response.json()

        self._name = data.get("displayName")
        self._connection_type = data.get("connectivityType")
        self._connectivity_type = data.get("connectivityType")
        self._privacy_level = data.get("privacyLevel")
        self._credential_details = data.get("credentialDetails", {})
        self._data_fetched = True

    @property
    def id(self) -> Optional[str]:
        """Connection ID is always available (set in __init__)."""
        return self._id

    @property
    def name(self) -> Optional[str]:
        """Connection display name (lazy loaded)."""
        self._fetch()
        return self._name

    @property
    def connectionType(self) -> Optional[str]:
        """Connection type (lazy loaded)."""
        self._fetch()
        return self._connection_type

    @property
    def connectivityType(self) -> Optional[str]:
        """Connectivity type (lazy loaded)."""
        self._fetch()
        return self._connectivity_type

    @property
    def privacyLevel(self) -> Optional[str]:
        """Privacy level (lazy loaded)."""
        self._fetch()
        return self._privacy_level

    @property
    def credentialDetails(self) -> Optional[dict]:
        """Credential details (lazy loaded)."""
        self._fetch()
        return self._credential_details

    def refresh(self) -> "Connection":
        """
        Force refresh connection data from API.

        Returns:
            Connection: Self for method chaining

        Examples:
            >>> conn = fabric.connection("SQL Server")
            >>> conn.refresh()  # Get latest data from API
        """
        self._data_fetched = False
        self._fetch()
        return self

    def roleAssignment(self, principal_id: str) -> "ConnectionRoleAssignment":
        """
        Get a specific role assignment by principal ID.

        This method accepts a principal ID and looks up the corresponding
        role assignment. If multiple or no assignments are found, raises an error.

        Args:
            principal_id: Principal GUID (user, group, or service principal)

        Returns:
            ConnectionRoleAssignment: The role assignment

        Raises:
            NotFoundError: If no role assignment found for the principal
            FabiasError: If multiple role assignments found (shouldn't happen)

        Examples:
            >>> conn = fabric.connection("SQL Server")
            >>> assignment = conn.roleAssignment("user-guid")
            >>> print(assignment.role)
        """
        # List all assignments to find the one with matching principal_id
        assignments = self.roleAssignments()

        # Find assignment with matching principal ID
        matches = [a for a in assignments if a.principalId == principal_id]

        if not matches:
            from .._shared.exceptions import NotFoundError

            raise NotFoundError(
                f"No role assignment found for principal {principal_id}",
                resource_type="ConnectionRoleAssignment",
                resource_id=principal_id,
            )

        if len(matches) > 1:
            from .._shared.exceptions import FabiasError

            raise FabiasError(
                f"Multiple role assignments found for principal {principal_id}. "
                "This should not happen."
            )

        return cast("ConnectionRoleAssignment", matches[0])

    @property
    def roleAssignments(self) -> "RoleAssignmentAccessor":
        """
        Access role assignments for this connection.

        Returns:
            RoleAssignmentAccessor: Accessor for listing and managing role assignments

        Examples:
            List role assignments:

            >>> conn = fabric.connection("SQL Server")
            >>> for assignment in conn.roleAssignments():
            ...     print(f"{assignment.principalName}: {assignment.role}")

            Add role assignment:

            >>> conn.roleAssignments.add(
            ...     principal_id="user-guid",
            ...     principal_type="User",
            ...     role="Admin"
            ... )
        """
        if not self.id:
            raise ValueError("Connection ID not available")
        return RoleAssignmentAccessor(self._client, self.id)

    def __repr__(self) -> str:
        return f"Connection(id={self.id}, " f"name={self.name}, " f"type={self.connectionType})"

Attributes

connectionType property

Connection type (lazy loaded).

connectivityType property

Connectivity type (lazy loaded).

credentialDetails property

Credential details (lazy loaded).

id property

Connection ID is always available (set in init).

name property

Connection display name (lazy loaded).

privacyLevel property

Privacy level (lazy loaded).

roleAssignments property

Access role assignments for this connection.

Returns:

Name Type Description
RoleAssignmentAccessor RoleAssignmentAccessor

Accessor for listing and managing role assignments

Examples:

List role assignments:

>>> conn = fabric.connection("SQL Server")
>>> for assignment in conn.roleAssignments():
...     print(f"{assignment.principalName}: {assignment.role}")

Add role assignment:

>>> conn.roleAssignments.add(
...     principal_id="user-guid",
...     principal_type="User",
...     role="Admin"
... )

Functions

__init__(client, identifier=None, connection_data=None)

Initialize Connection.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
identifier Optional[str]

Connection name or GUID (triggers lazy loading or immediate resolution)

None
connection_data Optional[Dict[str, Any]]

Pre-populated connection data from API (skips resolution)

None
Source code in src/fabias/_fabric/connections.py
def __init__(
    self,
    client: "FabricClient",
    identifier: Optional[str] = None,
    connection_data: Optional[Dict[str, Any]] = None,
):
    """
    Initialize Connection.

    Args:
        client: Authenticated FabricClient
        identifier: Connection name or GUID (triggers lazy loading or immediate resolution)
        connection_data: Pre-populated connection data from API (skips resolution)
    """
    self._client = client
    self._data_fetched = False

    # Attributes (lazy loaded)
    self._id: Optional[str] = None
    self._name: Optional[str] = None
    self._connection_type: Optional[str] = None
    self._connectivity_type: Optional[str] = None
    self._privacy_level: Optional[str] = None
    self._credential_details: Optional[dict] = None

    # Pre-populated from API response
    if connection_data:
        self._id = connection_data.get("id")
        self._name = connection_data.get("displayName")
        self._connection_type = connection_data.get("connectivityType")
        self._connectivity_type = connection_data.get("connectivityType")
        self._privacy_level = connection_data.get("privacyLevel")
        self._credential_details = connection_data.get("credentialDetails", {})
        self._data_fetched = True
        return

    # GUID provided - store and return (no API call)
    if identifier and GUID.valid(identifier):
        self._id = identifier
        return

    # Name provided - resolve to GUID (API call happens here)
    if identifier:
        self._resolve_identifier(identifier)

refresh()

Force refresh connection data from API.

Returns:

Name Type Description
Connection Connection

Self for method chaining

Examples:

>>> conn = fabric.connection("SQL Server")
>>> conn.refresh()  # Get latest data from API
Source code in src/fabias/_fabric/connections.py
def refresh(self) -> "Connection":
    """
    Force refresh connection data from API.

    Returns:
        Connection: Self for method chaining

    Examples:
        >>> conn = fabric.connection("SQL Server")
        >>> conn.refresh()  # Get latest data from API
    """
    self._data_fetched = False
    self._fetch()
    return self

roleAssignment(principal_id)

Get a specific role assignment by principal ID.

This method accepts a principal ID and looks up the corresponding role assignment. If multiple or no assignments are found, raises an error.

Parameters:

Name Type Description Default
principal_id str

Principal GUID (user, group, or service principal)

required

Returns:

Name Type Description
ConnectionRoleAssignment ConnectionRoleAssignment

The role assignment

Raises:

Type Description
NotFoundError

If no role assignment found for the principal

FabiasError

If multiple role assignments found (shouldn't happen)

Examples:

>>> conn = fabric.connection("SQL Server")
>>> assignment = conn.roleAssignment("user-guid")
>>> print(assignment.role)
Source code in src/fabias/_fabric/connections.py
def roleAssignment(self, principal_id: str) -> "ConnectionRoleAssignment":
    """
    Get a specific role assignment by principal ID.

    This method accepts a principal ID and looks up the corresponding
    role assignment. If multiple or no assignments are found, raises an error.

    Args:
        principal_id: Principal GUID (user, group, or service principal)

    Returns:
        ConnectionRoleAssignment: The role assignment

    Raises:
        NotFoundError: If no role assignment found for the principal
        FabiasError: If multiple role assignments found (shouldn't happen)

    Examples:
        >>> conn = fabric.connection("SQL Server")
        >>> assignment = conn.roleAssignment("user-guid")
        >>> print(assignment.role)
    """
    # List all assignments to find the one with matching principal_id
    assignments = self.roleAssignments()

    # Find assignment with matching principal ID
    matches = [a for a in assignments if a.principalId == principal_id]

    if not matches:
        from .._shared.exceptions import NotFoundError

        raise NotFoundError(
            f"No role assignment found for principal {principal_id}",
            resource_type="ConnectionRoleAssignment",
            resource_id=principal_id,
        )

    if len(matches) > 1:
        from .._shared.exceptions import FabiasError

        raise FabiasError(
            f"Multiple role assignments found for principal {principal_id}. "
            "This should not happen."
        )

    return cast("ConnectionRoleAssignment", matches[0])

fabias.Capacity

Represents a Fabric capacity.

Capacities provide compute and storage resources for Fabric workspaces.

Attributes:

Name Type Description
id str

Capacity GUID

display_name str

Capacity display name

sku str

Capacity SKU (e.g., "F2", "F4", "P1")

region str

Azure region

state str

Capacity state (Active, Paused, etc.)

admins list

List of capacity administrators

Examples:

>>> capacity = fabric.capacity("Premium-P1")
>>> print(f"SKU: {capacity.sku}, Region: {capacity.region}")
>>>
>>> # Manage workspace assignments
>>> ws = fabric.workspace("Analytics")
>>> capacity.addWorkspace(ws)
>>> capacity.removeWorkspace(ws)
Source code in src/fabias/_fabric/admin/capacity.py
class Capacity:
    """
    Represents a Fabric capacity.

    Capacities provide compute and storage resources for Fabric workspaces.

    Attributes:
        id (str): Capacity GUID
        display_name (str): Capacity display name
        sku (str): Capacity SKU (e.g., "F2", "F4", "P1")
        region (str): Azure region
        state (str): Capacity state (Active, Paused, etc.)
        admins (list): List of capacity administrators

    Examples:
        >>> capacity = fabric.capacity("Premium-P1")
        >>> print(f"SKU: {capacity.sku}, Region: {capacity.region}")
        >>>
        >>> # Manage workspace assignments
        >>> ws = fabric.workspace("Analytics")
        >>> capacity.addWorkspace(ws)
        >>> capacity.removeWorkspace(ws)
    """

    def __init__(
        self,
        client: "FabricClient",
        identifier: Optional[str] = None,
        capacity_data: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize Capacity.

        Args:
            client: Authenticated FabricClient
            identifier: Capacity name or GUID (triggers lazy loading or immediate resolution)
            capacity_data: Pre-populated capacity data from API (skips resolution)
        """
        self._client = client
        self._data_fetched = False

        # Attributes (lazy loaded)
        self._id: Optional[str] = None
        self._display_name: Optional[str] = None
        self._sku: Optional[str] = None
        self._region: Optional[str] = None
        self._state: Optional[str] = None
        self._admins: Optional[List[str]] = None

        # Pre-populated from API response
        if capacity_data:
            self._id = capacity_data.get("id")
            self._display_name = capacity_data.get("displayName")
            self._sku = capacity_data.get("sku")
            self._region = capacity_data.get("region")
            self._state = capacity_data.get("state")
            self._admins = capacity_data.get("admins", [])
            self._data_fetched = True
            return

        # GUID provided - store and return (no API call)
        if identifier and GUID.valid(identifier):
            self._id = identifier
            return

        # Name provided - resolve to GUID (API call happens here)
        if identifier:
            self._resolve_identifier(identifier)

    def _resolve_identifier(self, name: str) -> None:
        """
        Resolve capacity name to GUID.

        Args:
            name: Capacity display name
        """
        # List all capacities and find matching name
        response = self._client.get("/admin/capacities")
        data = response.json()
        capacities = data.get("value", [])

        matches = [c for c in capacities if c.get("displayName") == name]

        if not matches:
            raise NotFoundError(
                f"Capacity '{name}' not found", resource_type="Capacity", resource_id=name
            )

        if len(matches) > 1:
            raise FabiasError(f"Multiple capacities found with name '{name}'")

        matched = matches[0]
        self._id = matched.get("id")
        self._display_name = matched.get("displayName")
        self._sku = matched.get("sku")
        self._region = matched.get("region")
        self._state = matched.get("state")
        self._admins = matched.get("admins", [])
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load capacity data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/admin/capacities/{self._id}")
        data = response.json()

        self._display_name = data.get("displayName")
        self._sku = data.get("sku")
        self._region = data.get("region")
        self._state = data.get("state")
        self._admins = data.get("admins", [])
        self._data_fetched = True

    @property
    def id(self) -> Optional[str]:
        """Capacity GUID."""
        return self._id

    @property
    def displayName(self) -> Optional[str]:
        """Capacity display name (lazy loaded)."""
        self._fetch()
        return self._display_name

    @property
    def sku(self) -> Optional[str]:
        """Capacity SKU (lazy loaded)."""
        self._fetch()
        return self._sku

    @property
    def region(self) -> Optional[str]:
        """Azure region (lazy loaded)."""
        self._fetch()
        return self._region

    @property
    def state(self) -> Optional[str]:
        """Capacity state (lazy loaded)."""
        self._fetch()
        return self._state

    @property
    def admins(self) -> Optional[List[str]]:
        """List of capacity administrator IDs (lazy loaded)."""
        self._fetch()
        return self._admins

    def refresh(self) -> "Capacity":
        """
        Force refresh capacity data from API.

        Returns:
            Capacity: Self for method chaining
        """
        self._data_fetched = False
        self._fetch()
        return self

    def workspaces(self) -> List["Workspace"]:
        """
        List all workspaces assigned to this capacity.

        Returns:
            list[Workspace]: Workspaces in this capacity

        Examples:
            >>> capacity = fabric.capacity("Premium-P1")
            >>> for ws in capacity.workspaces():
            ...     print(ws.name)
        """
        from ..workspace import Workspace

        response = self._client.get(f"/admin/capacities/{self.id}/workspaces")
        data = response.json()
        workspaces_data = data.get("value", [])

        return [Workspace(self._client, workspace_data=ws_data) for ws_data in workspaces_data]

    def addWorkspace(self, workspace: "Workspace") -> None:
        """
        Assign a workspace to this capacity.

        Args:
            workspace: Workspace object to assign

        Examples:
            >>> capacity = fabric.capacity("Premium-P1")
            >>> ws = fabric.workspace("Analytics")
            >>> capacity.addWorkspace(ws)
        """
        payload = {"capacityId": self.id}
        self._client.post(f"/workspaces/{workspace.id}/assignToCapacity", data=payload)

    def removeWorkspace(self, workspace: "Workspace") -> None:
        """
        Unassign a workspace from this capacity.

        Args:
            workspace: Workspace object to unassign

        Examples:
            >>> capacity = fabric.capacity("Premium-P1")
            >>> ws = fabric.workspace("Analytics")
            >>> capacity.removeWorkspace(ws)
        """
        self._client.post(f"/workspaces/{workspace.id}/unassignFromCapacity")

    def __repr__(self) -> str:
        return f"Capacity(id={self.id}, name={self.displayName}, sku={self.sku})"

Attributes

admins property

List of capacity administrator IDs (lazy loaded).

displayName property

Capacity display name (lazy loaded).

id property

Capacity GUID.

region property

Azure region (lazy loaded).

sku property

Capacity SKU (lazy loaded).

state property

Capacity state (lazy loaded).

Functions

__init__(client, identifier=None, capacity_data=None)

Initialize Capacity.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
identifier Optional[str]

Capacity name or GUID (triggers lazy loading or immediate resolution)

None
capacity_data Optional[Dict[str, Any]]

Pre-populated capacity data from API (skips resolution)

None
Source code in src/fabias/_fabric/admin/capacity.py
def __init__(
    self,
    client: "FabricClient",
    identifier: Optional[str] = None,
    capacity_data: Optional[Dict[str, Any]] = None,
):
    """
    Initialize Capacity.

    Args:
        client: Authenticated FabricClient
        identifier: Capacity name or GUID (triggers lazy loading or immediate resolution)
        capacity_data: Pre-populated capacity data from API (skips resolution)
    """
    self._client = client
    self._data_fetched = False

    # Attributes (lazy loaded)
    self._id: Optional[str] = None
    self._display_name: Optional[str] = None
    self._sku: Optional[str] = None
    self._region: Optional[str] = None
    self._state: Optional[str] = None
    self._admins: Optional[List[str]] = None

    # Pre-populated from API response
    if capacity_data:
        self._id = capacity_data.get("id")
        self._display_name = capacity_data.get("displayName")
        self._sku = capacity_data.get("sku")
        self._region = capacity_data.get("region")
        self._state = capacity_data.get("state")
        self._admins = capacity_data.get("admins", [])
        self._data_fetched = True
        return

    # GUID provided - store and return (no API call)
    if identifier and GUID.valid(identifier):
        self._id = identifier
        return

    # Name provided - resolve to GUID (API call happens here)
    if identifier:
        self._resolve_identifier(identifier)

addWorkspace(workspace)

Assign a workspace to this capacity.

Parameters:

Name Type Description Default
workspace Workspace

Workspace object to assign

required

Examples:

>>> capacity = fabric.capacity("Premium-P1")
>>> ws = fabric.workspace("Analytics")
>>> capacity.addWorkspace(ws)
Source code in src/fabias/_fabric/admin/capacity.py
def addWorkspace(self, workspace: "Workspace") -> None:
    """
    Assign a workspace to this capacity.

    Args:
        workspace: Workspace object to assign

    Examples:
        >>> capacity = fabric.capacity("Premium-P1")
        >>> ws = fabric.workspace("Analytics")
        >>> capacity.addWorkspace(ws)
    """
    payload = {"capacityId": self.id}
    self._client.post(f"/workspaces/{workspace.id}/assignToCapacity", data=payload)

refresh()

Force refresh capacity data from API.

Returns:

Name Type Description
Capacity Capacity

Self for method chaining

Source code in src/fabias/_fabric/admin/capacity.py
def refresh(self) -> "Capacity":
    """
    Force refresh capacity data from API.

    Returns:
        Capacity: Self for method chaining
    """
    self._data_fetched = False
    self._fetch()
    return self

removeWorkspace(workspace)

Unassign a workspace from this capacity.

Parameters:

Name Type Description Default
workspace Workspace

Workspace object to unassign

required

Examples:

>>> capacity = fabric.capacity("Premium-P1")
>>> ws = fabric.workspace("Analytics")
>>> capacity.removeWorkspace(ws)
Source code in src/fabias/_fabric/admin/capacity.py
def removeWorkspace(self, workspace: "Workspace") -> None:
    """
    Unassign a workspace from this capacity.

    Args:
        workspace: Workspace object to unassign

    Examples:
        >>> capacity = fabric.capacity("Premium-P1")
        >>> ws = fabric.workspace("Analytics")
        >>> capacity.removeWorkspace(ws)
    """
    self._client.post(f"/workspaces/{workspace.id}/unassignFromCapacity")

workspaces()

List all workspaces assigned to this capacity.

Returns:

Type Description
List[Workspace]

list[Workspace]: Workspaces in this capacity

Examples:

>>> capacity = fabric.capacity("Premium-P1")
>>> for ws in capacity.workspaces():
...     print(ws.name)
Source code in src/fabias/_fabric/admin/capacity.py
def workspaces(self) -> List["Workspace"]:
    """
    List all workspaces assigned to this capacity.

    Returns:
        list[Workspace]: Workspaces in this capacity

    Examples:
        >>> capacity = fabric.capacity("Premium-P1")
        >>> for ws in capacity.workspaces():
        ...     print(ws.name)
    """
    from ..workspace import Workspace

    response = self._client.get(f"/admin/capacities/{self.id}/workspaces")
    data = response.json()
    workspaces_data = data.get("value", [])

    return [Workspace(self._client, workspace_data=ws_data) for ws_data in workspaces_data]

fabias.Tenant

Represents the Fabric tenant and provides access to tenant-level settings.

This is a singleton-style class - there's only one tenant per authentication context.

Examples:

>>> tenant = fabric.tenant
>>>
>>> # List all settings
>>> for setting in tenant.settings():
...     print(f"{setting.title}: {setting.enabled}")
>>>
>>> # Get specific setting
>>> setting = tenant.setting("AdminApisIncludeDetailedMetadata")
>>> print(f"Enabled: {setting.enabled}")
Source code in src/fabias/_fabric/admin/tenant.py
class Tenant:
    """
    Represents the Fabric tenant and provides access to tenant-level settings.

    This is a singleton-style class - there's only one tenant per authentication context.

    Examples:
        >>> tenant = fabric.tenant
        >>>
        >>> # List all settings
        >>> for setting in tenant.settings():
        ...     print(f"{setting.title}: {setting.enabled}")
        >>>
        >>> # Get specific setting
        >>> setting = tenant.setting("AdminApisIncludeDetailedMetadata")
        >>> print(f"Enabled: {setting.enabled}")
    """

    def __init__(self, client: "FabricClient"):
        """
        Initialize Tenant.

        Args:
            client: Authenticated FabricClient
        """
        self._client = client

    def settings(self) -> List[TenantSetting]:
        """
        List all tenant settings.

        Automatically handles pagination via continuation tokens.

        Returns:
            list[TenantSetting]: All tenant settings

        Examples:
            >>> tenant = fabric.tenant
            >>> settings = tenant.settings()
            >>>
            >>> # Filter by group
            >>> export_settings = [
            ...     s for s in settings
            ...     if s.tenant_setting_group == "ExportAndSharing"
            ... ]
        """
        # Get all tenant settings (pagination handled automatically by client)
        endpoint = "/admin/tenantsettings"
        response = self._client.get(endpoint)
        data = response.json()

        return [TenantSetting(self._client, setting_data) for setting_data in data.get("value", [])]

    def setting(self, setting_name: str) -> Optional[TenantSetting]:
        """
        Get a specific tenant setting by name.

        Args:
            setting_name: Setting identifier (e.g., "AdminApisIncludeDetailedMetadata")

        Returns:
            TenantSetting: The setting, or None if not found

        Examples:
            >>> tenant = fabric.tenant
            >>> setting = tenant.setting("DatamartTenant")
            >>> if setting:
            ...     print(f"Enabled: {setting.enabled}")
        """
        # Note: API doesn't have single-setting endpoint, so we list and filter
        settings = self.settings()
        matches = [s for s in settings if s.setting_name == setting_name]
        return matches[0] if matches else None

    def updateSetting(
        self,
        setting_name: str,
        enabled: bool,
        enabled_security_groups: Optional[List[str]] = None,
        excluded_security_groups: Optional[List[str]] = None,
    ) -> None:
        """
        Update a tenant setting.

        Args:
            setting_name: Setting identifier
            enabled: Enable or disable the setting
            enabled_security_groups: Optional list of security group IDs to enable for
            excluded_security_groups: Optional list of security group IDs to exclude

        Examples:
            >>> tenant = fabric.tenant
            >>> tenant.updateSetting(
            ...     "DatamartTenant",
            ...     enabled=True,
            ...     enabled_security_groups=["group-guid-1", "group-guid-2"]
            ... )

        Note:
            This operation requires Fabric Administrator permissions and may
            require PIM (Privileged Identity Management) elevation.
        """
        payload = {"settingName": setting_name, "enabled": enabled}

        if enabled_security_groups is not None:
            payload["enabledSecurityGroups"] = [
                {"graphId": group_id} for group_id in enabled_security_groups
            ]

        if excluded_security_groups is not None:
            payload["excludedSecurityGroups"] = [
                {"graphId": group_id} for group_id in excluded_security_groups
            ]

        self._client.patch("/admin/tenantsettings", data=payload)

    def activityEvents(
        self,
        start: "Union[str, datetime]",
        end: "Union[str, datetime]",
        filter: Optional[str] = None,
    ) -> List[ActivityEvent]:
        """
        Get Power BI / Fabric activity events for a time range.

        Calls ``GET https://api.powerbi.com/v1.0/myorg/admin/activityevents``
        and returns all events, automatically following continuation pages.

        Args:
            start: Start of time range — ``datetime`` or ISO 8601 string
                   (e.g., ``'2024-06-01T00:00:00'``).
            end: End of time range — ``datetime`` or ISO 8601 string
                 (e.g., ``'2024-06-01T23:59:59'``).
            filter: Optional OData filter to narrow results
                    (e.g., ``"Activity eq 'ShareReport'"``).

        Returns:
            list[ActivityEvent]: All matching activity events.

        Note:
            Requires **Tenant.Read.All** (or **Tenant.ReadWrite.All**) permission
            and the Power BI service principal scope
            ``https://analysis.windows.net/powerbi/api/.default``.
        """
        from datetime import datetime as _dt

        from .._client import FabricClient

        POWERBI_SCOPE = FabricClient.POWERBI_SCOPE

        def _fmt(v: Any) -> str:
            if isinstance(v, _dt):
                v = v.strftime("%Y-%m-%dT%H:%M:%S")
            return f"'{str(v).strip(chr(39))}'"

        params: Dict[str, str] = {"startDateTime": _fmt(start), "endDateTime": _fmt(end)}
        if filter:
            params["$filter"] = filter

        url: Optional[str] = "https://api.powerbi.com/v1.0/myorg/admin/activityevents"

        # Build headers using the Power BI scope (different from Fabric scope)
        headers = self._client._default_headers.copy()
        headers["Authorization"] = self._client._auth.getAuthorizationHeader(POWERBI_SCOPE)

        all_events: List[ActivityEvent] = []

        params_or_none: Optional[Dict[str, str]] = params

        while url:
            response = _requests.get(url, headers=headers, params=params_or_none)
            self._client._handle_response(response)  # Raise ApiError / AuthError on failure
            data = response.json()

            all_events.extend(ActivityEvent(e) for e in data.get("activityEventEntities", []))

            # continuationUri already encodes all query params for subsequent pages
            params_or_none = None
            url = None if data.get("lastResultSet", True) else data.get("continuationUri")

        return all_events

    def __repr__(self) -> str:
        return "Tenant(admin)"

Functions

__init__(client)

Initialize Tenant.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
Source code in src/fabias/_fabric/admin/tenant.py
def __init__(self, client: "FabricClient"):
    """
    Initialize Tenant.

    Args:
        client: Authenticated FabricClient
    """
    self._client = client

activityEvents(start, end, filter=None)

Get Power BI / Fabric activity events for a time range.

Calls GET https://api.powerbi.com/v1.0/myorg/admin/activityevents and returns all events, automatically following continuation pages.

Parameters:

Name Type Description Default
start Union[str, datetime]

Start of time range — datetime or ISO 8601 string (e.g., '2024-06-01T00:00:00').

required
end Union[str, datetime]

End of time range — datetime or ISO 8601 string (e.g., '2024-06-01T23:59:59').

required
filter Optional[str]

Optional OData filter to narrow results (e.g., "Activity eq 'ShareReport'").

None

Returns:

Type Description
List[ActivityEvent]

list[ActivityEvent]: All matching activity events.

Note

Requires Tenant.Read.All (or Tenant.ReadWrite.All) permission and the Power BI service principal scope https://analysis.windows.net/powerbi/api/.default.

Source code in src/fabias/_fabric/admin/tenant.py
def activityEvents(
    self,
    start: "Union[str, datetime]",
    end: "Union[str, datetime]",
    filter: Optional[str] = None,
) -> List[ActivityEvent]:
    """
    Get Power BI / Fabric activity events for a time range.

    Calls ``GET https://api.powerbi.com/v1.0/myorg/admin/activityevents``
    and returns all events, automatically following continuation pages.

    Args:
        start: Start of time range — ``datetime`` or ISO 8601 string
               (e.g., ``'2024-06-01T00:00:00'``).
        end: End of time range — ``datetime`` or ISO 8601 string
             (e.g., ``'2024-06-01T23:59:59'``).
        filter: Optional OData filter to narrow results
                (e.g., ``"Activity eq 'ShareReport'"``).

    Returns:
        list[ActivityEvent]: All matching activity events.

    Note:
        Requires **Tenant.Read.All** (or **Tenant.ReadWrite.All**) permission
        and the Power BI service principal scope
        ``https://analysis.windows.net/powerbi/api/.default``.
    """
    from datetime import datetime as _dt

    from .._client import FabricClient

    POWERBI_SCOPE = FabricClient.POWERBI_SCOPE

    def _fmt(v: Any) -> str:
        if isinstance(v, _dt):
            v = v.strftime("%Y-%m-%dT%H:%M:%S")
        return f"'{str(v).strip(chr(39))}'"

    params: Dict[str, str] = {"startDateTime": _fmt(start), "endDateTime": _fmt(end)}
    if filter:
        params["$filter"] = filter

    url: Optional[str] = "https://api.powerbi.com/v1.0/myorg/admin/activityevents"

    # Build headers using the Power BI scope (different from Fabric scope)
    headers = self._client._default_headers.copy()
    headers["Authorization"] = self._client._auth.getAuthorizationHeader(POWERBI_SCOPE)

    all_events: List[ActivityEvent] = []

    params_or_none: Optional[Dict[str, str]] = params

    while url:
        response = _requests.get(url, headers=headers, params=params_or_none)
        self._client._handle_response(response)  # Raise ApiError / AuthError on failure
        data = response.json()

        all_events.extend(ActivityEvent(e) for e in data.get("activityEventEntities", []))

        # continuationUri already encodes all query params for subsequent pages
        params_or_none = None
        url = None if data.get("lastResultSet", True) else data.get("continuationUri")

    return all_events

setting(setting_name)

Get a specific tenant setting by name.

Parameters:

Name Type Description Default
setting_name str

Setting identifier (e.g., "AdminApisIncludeDetailedMetadata")

required

Returns:

Name Type Description
TenantSetting Optional[TenantSetting]

The setting, or None if not found

Examples:

>>> tenant = fabric.tenant
>>> setting = tenant.setting("DatamartTenant")
>>> if setting:
...     print(f"Enabled: {setting.enabled}")
Source code in src/fabias/_fabric/admin/tenant.py
def setting(self, setting_name: str) -> Optional[TenantSetting]:
    """
    Get a specific tenant setting by name.

    Args:
        setting_name: Setting identifier (e.g., "AdminApisIncludeDetailedMetadata")

    Returns:
        TenantSetting: The setting, or None if not found

    Examples:
        >>> tenant = fabric.tenant
        >>> setting = tenant.setting("DatamartTenant")
        >>> if setting:
        ...     print(f"Enabled: {setting.enabled}")
    """
    # Note: API doesn't have single-setting endpoint, so we list and filter
    settings = self.settings()
    matches = [s for s in settings if s.setting_name == setting_name]
    return matches[0] if matches else None

settings()

List all tenant settings.

Automatically handles pagination via continuation tokens.

Returns:

Type Description
List[TenantSetting]

list[TenantSetting]: All tenant settings

Examples:

>>> tenant = fabric.tenant
>>> settings = tenant.settings()
>>>
>>> # Filter by group
>>> export_settings = [
...     s for s in settings
...     if s.tenant_setting_group == "ExportAndSharing"
... ]
Source code in src/fabias/_fabric/admin/tenant.py
def settings(self) -> List[TenantSetting]:
    """
    List all tenant settings.

    Automatically handles pagination via continuation tokens.

    Returns:
        list[TenantSetting]: All tenant settings

    Examples:
        >>> tenant = fabric.tenant
        >>> settings = tenant.settings()
        >>>
        >>> # Filter by group
        >>> export_settings = [
        ...     s for s in settings
        ...     if s.tenant_setting_group == "ExportAndSharing"
        ... ]
    """
    # Get all tenant settings (pagination handled automatically by client)
    endpoint = "/admin/tenantsettings"
    response = self._client.get(endpoint)
    data = response.json()

    return [TenantSetting(self._client, setting_data) for setting_data in data.get("value", [])]

updateSetting(setting_name, enabled, enabled_security_groups=None, excluded_security_groups=None)

Update a tenant setting.

Parameters:

Name Type Description Default
setting_name str

Setting identifier

required
enabled bool

Enable or disable the setting

required
enabled_security_groups Optional[List[str]]

Optional list of security group IDs to enable for

None
excluded_security_groups Optional[List[str]]

Optional list of security group IDs to exclude

None

Examples:

>>> tenant = fabric.tenant
>>> tenant.updateSetting(
...     "DatamartTenant",
...     enabled=True,
...     enabled_security_groups=["group-guid-1", "group-guid-2"]
... )
Note

This operation requires Fabric Administrator permissions and may require PIM (Privileged Identity Management) elevation.

Source code in src/fabias/_fabric/admin/tenant.py
def updateSetting(
    self,
    setting_name: str,
    enabled: bool,
    enabled_security_groups: Optional[List[str]] = None,
    excluded_security_groups: Optional[List[str]] = None,
) -> None:
    """
    Update a tenant setting.

    Args:
        setting_name: Setting identifier
        enabled: Enable or disable the setting
        enabled_security_groups: Optional list of security group IDs to enable for
        excluded_security_groups: Optional list of security group IDs to exclude

    Examples:
        >>> tenant = fabric.tenant
        >>> tenant.updateSetting(
        ...     "DatamartTenant",
        ...     enabled=True,
        ...     enabled_security_groups=["group-guid-1", "group-guid-2"]
        ... )

    Note:
        This operation requires Fabric Administrator permissions and may
        require PIM (Privileged Identity Management) elevation.
    """
    payload = {"settingName": setting_name, "enabled": enabled}

    if enabled_security_groups is not None:
        payload["enabledSecurityGroups"] = [
            {"graphId": group_id} for group_id in enabled_security_groups
        ]

    if excluded_security_groups is not None:
        payload["excludedSecurityGroups"] = [
            {"graphId": group_id} for group_id in excluded_security_groups
        ]

    self._client.patch("/admin/tenantsettings", data=payload)

fabias.Folder

Represents a folder in a Fabric workspace.

Folders organize workspace items into a hierarchical structure. Supports lazy loading: when created with a GUID, properties are fetched on first access.

Attributes:

Name Type Description
id str

Folder unique identifier (GUID)

name str

Folder display name

workspace_id str

Parent workspace GUID

parent str

Parent folder GUID, or None if at workspace root

Examples:

Get a folder:

>>> folder = workspace.folder("Sales")
>>> print(f"{folder.name} (ID: {folder.id})")

Get a nested folder by path:

>>> folder = workspace.folder("Sales/Q1/January")

List all folders:

>>> for f in workspace.folders():
...     print(f"{f.name} -> parent: {f.parent}")

Create a subfolder:

>>> parent = workspace.folder("Sales")
>>> child = workspace.folders.add("Q1", parent=parent.id)

Move a folder:

>>> folder.move(target_folder_id=other_folder.id)

Rename a folder:

>>> folder.rename("New Name")
Source code in src/fabias/_fabric/folders.py
class Folder:
    """
    Represents a folder in a Fabric workspace.

    Folders organize workspace items into a hierarchical structure.
    Supports lazy loading: when created with a GUID, properties are
    fetched on first access.

    Attributes:
        id (str): Folder unique identifier (GUID)
        name (str): Folder display name
        workspace_id (str): Parent workspace GUID
        parent (str): Parent folder GUID, or None if at workspace root

    Examples:
        Get a folder:

        >>> folder = workspace.folder("Sales")
        >>> print(f"{folder.name} (ID: {folder.id})")

        Get a nested folder by path:

        >>> folder = workspace.folder("Sales/Q1/January")

        List all folders:

        >>> for f in workspace.folders():
        ...     print(f"{f.name} -> parent: {f.parent}")

        Create a subfolder:

        >>> parent = workspace.folder("Sales")
        >>> child = workspace.folders.add("Q1", parent=parent.id)

        Move a folder:

        >>> folder.move(target_folder_id=other_folder.id)

        Rename a folder:

        >>> folder.rename("New Name")
    """

    def __init__(
        self,
        client: "FabricClient",
        workspace_id: str,
        identifier: Optional[str] = None,
        folder_data: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize Folder.

        Args:
            client: Authenticated FabricClient
            workspace_id: Workspace GUID containing the folder
            identifier: Folder name, GUID, or slash-separated path
                (e.g. ``"Sales/Q1/January"``)
            folder_data: Pre-populated folder data from API (skips resolution)
        """
        self._client = client
        self._workspace_id = workspace_id
        self._data_fetched = False

        # Attributes (lazy loaded)
        self._id: Optional[str] = None
        self._name: Optional[str] = None
        self._parent: Optional[str] = None

        # Pre-populated from API response
        if folder_data:
            self._populate(folder_data)
            return

        if identifier is None:
            raise ValueError("Either identifier or folder_data must be provided")

        # GUID provided — store and return (no API call)
        if GUID.valid(identifier):
            self._id = identifier
            return

        # Name provided — resolve immediately
        self._resolve_by_name(identifier)

    def _populate(self, data: Dict[str, Any]) -> None:
        """Populate attributes from API response data."""
        self._id = data.get("id")
        self._name = data.get("displayName")
        self._parent = data.get("parentFolderId")
        # workspaceId may also be in the payload but we already have it
        self._data_fetched = True

    def _fetch(self) -> None:
        """Lazy load folder data from API if not already fetched."""
        if self._data_fetched or not self._id:
            return

        response = self._client.get(f"/workspaces/{self._workspace_id}/folders/{self._id}")
        self._populate(response.json())

    def _resolve_by_name(self, name: str) -> None:
        """
        Resolve folder name or path to GUID.

        Supports both simple names and slash-separated paths:
          - ``"Sales"`` — matches any folder named Sales (must be unique)
          - ``"Sales/Q1"`` — matches Q1 under the root-level Sales folder
          - ``"Sales/Q1/January"`` — walks the full path from root

        A single API call fetches all folders; path segments are resolved
        in-memory against the returned tree.
        """
        response = self._client.get(f"/workspaces/{self._workspace_id}/folders")
        all_folders = response.json().get("value", [])

        # Path resolution: split on "/" and walk top-down
        segments = [s for s in name.split("/") if s]

        if len(segments) > 1:
            self._resolve_path(segments, all_folders, name)
            return

        # Simple name (no slash) — exact match, error on ambiguity
        segment = segments[0]
        matches = [f for f in all_folders if f.get("displayName") == segment]

        if not matches:
            raise NotFoundError(
                f"Folder '{name}' not found in workspace",
                resource_type="Folder",
                resource_id=name,
            )

        if len(matches) > 1:
            # Build path hints from the folder tree
            hints = []
            for m in matches:
                hints.append(self._build_path(m, all_folders))
            hint_str = ", ".join(f"'{h}'" for h in hints)
            raise FabiasError(
                f"Multiple folders found with name '{name}'. "
                f"Specify the full path to disambiguate: {hint_str}"
            )

        self._populate(matches[0])

    def _resolve_path(
        self,
        segments: List[str],
        all_folders: List[Dict[str, Any]],
        original: str,
    ) -> None:
        """Walk path segments top-down, resolving each against its parent."""
        parent_id: Optional[str] = None  # None = workspace root

        for i, segment in enumerate(segments):
            matches = [
                f
                for f in all_folders
                if f.get("displayName") == segment and f.get("parentFolderId") == parent_id
            ]

            if not matches:
                partial = "/".join(segments[: i + 1])
                raise NotFoundError(
                    f"Folder path '{original}' not found — "
                    f"no folder named '{segment}' "
                    f"{'at workspace root' if parent_id is None else f'under {segments[i - 1]!r}'}",
                    resource_type="Folder",
                    resource_id=original,
                )

            if len(matches) > 1:
                partial = "/".join(segments[: i + 1])
                raise FabiasError(
                    f"Ambiguous path at '{partial}': multiple folders named "
                    f"'{segment}' at the same level."
                )

            parent_id = matches[0]["id"]

        # Last match is our target
        self._populate(matches[0])  # type: ignore[possibly-undefined]

    @staticmethod
    def _build_path(folder: Dict[str, Any], all_folders: List[Dict[str, Any]]) -> str:
        """Build the full path string for a folder (for error hints)."""
        parts = [folder.get("displayName", "")]
        parent_id = folder.get("parentFolderId")
        seen: set = set()
        while parent_id and parent_id not in seen:
            seen.add(parent_id)
            parent = next((f for f in all_folders if f.get("id") == parent_id), None)
            if not parent:
                break
            parts.append(parent.get("displayName", ""))
            parent_id = parent.get("parentFolderId")
        parts.reverse()
        return "/".join(parts)

    # =========================================================================
    # Properties
    # =========================================================================

    @property
    def id(self) -> Optional[str]:
        """Folder ID (always available after init)."""
        return self._id

    @property
    def name(self) -> Optional[str]:
        """Folder display name. Triggers lazy load if needed."""
        self._fetch()
        return self._name

    @property
    def workspace_id(self) -> str:
        """Workspace ID this folder belongs to."""
        return self._workspace_id

    @property
    def parent(self) -> Optional[str]:
        """Parent folder ID (None if at workspace root). Triggers lazy load if needed."""
        self._fetch()
        return self._parent

    # =========================================================================
    # Methods
    # =========================================================================

    def refresh(self) -> "Folder":
        """
        Force refresh folder data from API.

        Returns:
            Folder: self (for method chaining)
        """
        self._data_fetched = False
        self._fetch()
        return self

    def rename(self, name: str) -> "Folder":
        """
        Rename this folder.

        Args:
            name: New folder name

        Returns:
            Folder: self (updated with new name)

        Examples:
            >>> folder.rename("New Folder Name")
        """
        response = self._client.patch(
            f"/workspaces/{self._workspace_id}/folders/{self._id}",
            data={"displayName": name},
        )
        self._populate(response.json())
        return self

    def move(self, target_folder_id: Optional[str] = None) -> "Folder":
        """
        Move this folder within the workspace.

        Args:
            target_folder_id: Destination folder GUID. If None, moves to
                workspace root.

        Returns:
            Folder: self (updated with new parent)

        Raises:
            FabiasError: If move would create circular hierarchy

        Examples:
            Move into another folder:

            >>> folder.move(target_folder_id=other_folder.id)

            Move to workspace root:

            >>> folder.move()
        """
        payload: Dict[str, Any] = {}
        if target_folder_id is not None:
            payload["targetFolderId"] = target_folder_id

        response = self._client.post(
            f"/workspaces/{self._workspace_id}/folders/{self._id}/move",
            data=payload,
        )
        self._populate(response.json())
        return self

    def delete(self) -> None:
        """
        Delete this folder.

        The folder must be empty (no items or nested folders).

        Raises:
            FabiasError: If folder is not empty

        Examples:
            >>> folder.delete()
        """
        self._client.delete(f"/workspaces/{self._workspace_id}/folders/{self._id}")

    def __repr__(self) -> str:
        name = self._name or "(not loaded)"
        return f"Folder(id={self._id}, name={name!r})"

    def __str__(self) -> str:
        return self.name or self._id or "Folder"

Attributes

id property

Folder ID (always available after init).

name property

Folder display name. Triggers lazy load if needed.

parent property

Parent folder ID (None if at workspace root). Triggers lazy load if needed.

workspace_id property

Workspace ID this folder belongs to.

Functions

__init__(client, workspace_id, identifier=None, folder_data=None)

Initialize Folder.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
workspace_id str

Workspace GUID containing the folder

required
identifier Optional[str]

Folder name, GUID, or slash-separated path (e.g. "Sales/Q1/January")

None
folder_data Optional[Dict[str, Any]]

Pre-populated folder data from API (skips resolution)

None
Source code in src/fabias/_fabric/folders.py
def __init__(
    self,
    client: "FabricClient",
    workspace_id: str,
    identifier: Optional[str] = None,
    folder_data: Optional[Dict[str, Any]] = None,
):
    """
    Initialize Folder.

    Args:
        client: Authenticated FabricClient
        workspace_id: Workspace GUID containing the folder
        identifier: Folder name, GUID, or slash-separated path
            (e.g. ``"Sales/Q1/January"``)
        folder_data: Pre-populated folder data from API (skips resolution)
    """
    self._client = client
    self._workspace_id = workspace_id
    self._data_fetched = False

    # Attributes (lazy loaded)
    self._id: Optional[str] = None
    self._name: Optional[str] = None
    self._parent: Optional[str] = None

    # Pre-populated from API response
    if folder_data:
        self._populate(folder_data)
        return

    if identifier is None:
        raise ValueError("Either identifier or folder_data must be provided")

    # GUID provided — store and return (no API call)
    if GUID.valid(identifier):
        self._id = identifier
        return

    # Name provided — resolve immediately
    self._resolve_by_name(identifier)

delete()

Delete this folder.

The folder must be empty (no items or nested folders).

Raises:

Type Description
FabiasError

If folder is not empty

Examples:

>>> folder.delete()
Source code in src/fabias/_fabric/folders.py
def delete(self) -> None:
    """
    Delete this folder.

    The folder must be empty (no items or nested folders).

    Raises:
        FabiasError: If folder is not empty

    Examples:
        >>> folder.delete()
    """
    self._client.delete(f"/workspaces/{self._workspace_id}/folders/{self._id}")

move(target_folder_id=None)

Move this folder within the workspace.

Parameters:

Name Type Description Default
target_folder_id Optional[str]

Destination folder GUID. If None, moves to workspace root.

None

Returns:

Name Type Description
Folder Folder

self (updated with new parent)

Raises:

Type Description
FabiasError

If move would create circular hierarchy

Examples:

Move into another folder:

>>> folder.move(target_folder_id=other_folder.id)

Move to workspace root:

>>> folder.move()
Source code in src/fabias/_fabric/folders.py
def move(self, target_folder_id: Optional[str] = None) -> "Folder":
    """
    Move this folder within the workspace.

    Args:
        target_folder_id: Destination folder GUID. If None, moves to
            workspace root.

    Returns:
        Folder: self (updated with new parent)

    Raises:
        FabiasError: If move would create circular hierarchy

    Examples:
        Move into another folder:

        >>> folder.move(target_folder_id=other_folder.id)

        Move to workspace root:

        >>> folder.move()
    """
    payload: Dict[str, Any] = {}
    if target_folder_id is not None:
        payload["targetFolderId"] = target_folder_id

    response = self._client.post(
        f"/workspaces/{self._workspace_id}/folders/{self._id}/move",
        data=payload,
    )
    self._populate(response.json())
    return self

refresh()

Force refresh folder data from API.

Returns:

Name Type Description
Folder Folder

self (for method chaining)

Source code in src/fabias/_fabric/folders.py
def refresh(self) -> "Folder":
    """
    Force refresh folder data from API.

    Returns:
        Folder: self (for method chaining)
    """
    self._data_fetched = False
    self._fetch()
    return self

rename(name)

Rename this folder.

Parameters:

Name Type Description Default
name str

New folder name

required

Returns:

Name Type Description
Folder Folder

self (updated with new name)

Examples:

>>> folder.rename("New Folder Name")
Source code in src/fabias/_fabric/folders.py
def rename(self, name: str) -> "Folder":
    """
    Rename this folder.

    Args:
        name: New folder name

    Returns:
        Folder: self (updated with new name)

    Examples:
        >>> folder.rename("New Folder Name")
    """
    response = self._client.patch(
        f"/workspaces/{self._workspace_id}/folders/{self._id}",
        data={"displayName": name},
    )
    self._populate(response.json())
    return self

fabias.Notebook

Bases: Item

Represents a Microsoft Fabric Notebook.

Provides access to notebook metadata and content.

Examples:

>>> notebook = workspace.notebook("ETL Processing")
>>> print(f"Notebook: {notebook.name} ({notebook.id})")
>>> # Get notebook definition (long-running operation)
>>> response = notebook.getDefinition()
>>> response.wait()
>>> definition = response.result()
>>>
>>> # Decode notebook content from base64
>>> files = notebook.definition.files
>>> for file in files:
...     print(f"{file.path}: {file.content[:100]}...")
Source code in src/fabias/_fabric/items/notebook.py
class Notebook(Item):
    """
    Represents a Microsoft Fabric Notebook.

    Provides access to notebook metadata and content.

    Examples:
        >>> notebook = workspace.notebook("ETL Processing")
        >>> print(f"Notebook: {notebook.name} ({notebook.id})")

        >>> # Get notebook definition (long-running operation)
        >>> response = notebook.getDefinition()
        >>> response.wait()
        >>> definition = response.result()
        >>>
        >>> # Decode notebook content from base64
        >>> files = notebook.definition.files
        >>> for file in files:
        ...     print(f"{file.path}: {file.content[:100]}...")
    """

    ITEM_TYPE = "Notebook"
    ITEM_TYPE_DISPLAY = "Notebook"

    def __init__(
        self,
        client: "FabricClient",
        workspace_id: str,
        identifier: Optional[str] = None,
        item_data: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(client, workspace_id, identifier, item_data)
        self._definition_cache: Optional[NotebookDefinition] = (
            None  # Cache for notebook definition data
        )

    @property
    def definition(self) -> Optional[NotebookDefinition]:
        return self.getDefinition()

    def getDefinition(
        self, format: NotebookFormat = NotebookFormat.GITSOURCE, freshLoad: bool = False
    ) -> NotebookDefinition:
        """
        Get notebook definition and content (returns long-running operation).

        This initiates an async operation to retrieve the notebook's definition,
        which includes the notebook content encoded as base64.

        Attributes:
            format (NotebookFormat):  Desired notebook format (currently only GITSOURCE supported)
            freshLoad (bool): If True, bypasses cached definition and fetches anew

        Returns:
            List[Dict[str, Any]]: Files or file sections contained in definition

        Examples:
            >>> response = notebook.getDefinition()
            >>> response.wait()
            >>> definition = response.result()
            >>> print(definition['definition']['parts'])
        """

        if self._definition_cache and not freshLoad:
            return self._definition_cache

        endpoint = f"/workspaces/{self.workspace_id}/notebooks/{self.id}/getDefinition?format={format.value}"
        # POST request returns 202 with Location header for polling
        response = self._client.post(endpoint)

        if not response.longRunning:
            if response.successful:
                # If API returns definition directly (non-LRO), cache and return it
                if not response.data:
                    raise FabiasError(f"No definition data returned for notebook '{self.name}'")
                self._definition_cache = NotebookDefinition(response.data)
                return self._definition_cache

            raise FabiasError(
                f"Failed to get definition for notebook '{self.name}': "
                f"{response.status_code} {response.reason}"
            )

        print(f"Fetching definition for notebook '{self.name}' (this may take a moment)...")
        response.wait()
        definition = response.result()
        if not definition:
            raise FabiasError(f"No definition data returned for notebook '{self.name}'")
        self._definition_cache = NotebookDefinition(definition)

        return self._definition_cache

    def __repr__(self) -> str:
        return f"Notebook(id={self.id}, name={self.name!r})"

Functions

getDefinition(format=NotebookFormat.GITSOURCE, freshLoad=False)

Get notebook definition and content (returns long-running operation).

This initiates an async operation to retrieve the notebook's definition, which includes the notebook content encoded as base64.

Attributes:

Name Type Description
format NotebookFormat

Desired notebook format (currently only GITSOURCE supported)

freshLoad bool

If True, bypasses cached definition and fetches anew

Returns:

Type Description
NotebookDefinition

List[Dict[str, Any]]: Files or file sections contained in definition

Examples:

>>> response = notebook.getDefinition()
>>> response.wait()
>>> definition = response.result()
>>> print(definition['definition']['parts'])
Source code in src/fabias/_fabric/items/notebook.py
def getDefinition(
    self, format: NotebookFormat = NotebookFormat.GITSOURCE, freshLoad: bool = False
) -> NotebookDefinition:
    """
    Get notebook definition and content (returns long-running operation).

    This initiates an async operation to retrieve the notebook's definition,
    which includes the notebook content encoded as base64.

    Attributes:
        format (NotebookFormat):  Desired notebook format (currently only GITSOURCE supported)
        freshLoad (bool): If True, bypasses cached definition and fetches anew

    Returns:
        List[Dict[str, Any]]: Files or file sections contained in definition

    Examples:
        >>> response = notebook.getDefinition()
        >>> response.wait()
        >>> definition = response.result()
        >>> print(definition['definition']['parts'])
    """

    if self._definition_cache and not freshLoad:
        return self._definition_cache

    endpoint = f"/workspaces/{self.workspace_id}/notebooks/{self.id}/getDefinition?format={format.value}"
    # POST request returns 202 with Location header for polling
    response = self._client.post(endpoint)

    if not response.longRunning:
        if response.successful:
            # If API returns definition directly (non-LRO), cache and return it
            if not response.data:
                raise FabiasError(f"No definition data returned for notebook '{self.name}'")
            self._definition_cache = NotebookDefinition(response.data)
            return self._definition_cache

        raise FabiasError(
            f"Failed to get definition for notebook '{self.name}': "
            f"{response.status_code} {response.reason}"
        )

    print(f"Fetching definition for notebook '{self.name}' (this may take a moment)...")
    response.wait()
    definition = response.result()
    if not definition:
        raise FabiasError(f"No definition data returned for notebook '{self.name}'")
    self._definition_cache = NotebookDefinition(definition)

    return self._definition_cache

fabias.Environment

Represents a Microsoft Fabric Environment with full management capabilities.

Provides methods to: - Check environment status and publish state - Publish pending changes - Cancel publishing - Manage custom libraries

Attributes:

Name Type Description
id str

Environment GUID

name str

Environment display name

state str

Current publish state (Running, Success, Failed, etc.)

Examples:

>>> env = workspace.environment("ML Environment")
>>>
>>> # Check publish state
>>> env.refresh()
>>> if env.isPublishing:
...     print("Publishing in progress...")
>>>
>>> # Manage libraries
>>> libs = env.libraries
>>> await libs.upload("my_package.whl")
>>> env.publish()
Source code in src/fabias/_fabric/items/environment.py
class Environment:
    """
    Represents a Microsoft Fabric Environment with full management capabilities.

    Provides methods to:
    - Check environment status and publish state
    - Publish pending changes
    - Cancel publishing
    - Manage custom libraries

    Attributes:
        id (str): Environment GUID
        name (str): Environment display name
        state (str): Current publish state (Running, Success, Failed, etc.)

    Examples:
        >>> env = workspace.environment("ML Environment")
        >>>
        >>> # Check publish state
        >>> env.refresh()
        >>> if env.isPublishing:
        ...     print("Publishing in progress...")
        >>>
        >>> # Manage libraries
        >>> libs = env.libraries
        >>> await libs.upload("my_package.whl")
        >>> env.publish()
    """

    PUBLISH_STATES_ACTIVE = {"Running", "Waiting", "Cancelling"}

    def __init__(
        self,
        client: "FabricClient",
        workspace_id: str,
        identifier: Optional[str] = None,
        item_data: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize Environment with resolution or pre-populated data.

        Args:
            client: Authenticated FabricClient
            workspace_id: Workspace GUID
            identifier: Environment display name or GUID (optional if item_data provided)
            item_data: Pre-populated item data from API (skips resolution)
        """
        self._client = client
        self._workspace_id = workspace_id
        self.id: Optional[str] = None
        self.name: Optional[str] = None
        self.state: Optional[str] = None

        self._libraries: Optional[Libraries] = None

        if item_data:
            # Pre-populate from provided data
            self.id = item_data.get("id")
            self.name = item_data.get("displayName")
            # Don't call refresh() for pre-populated items from list
        elif identifier:
            # Resolve via API or sempy
            self._resolve_environment(identifier)
            self.refresh()
        else:
            raise ValueError("Either identifier or item_data must be provided")

    def _resolve_environment(self, identifier: str) -> None:
        """Resolve environment identifier."""
        # Try sempy.fabric first
        if runtime.isFabric:
            sempy = runtime.sempy
            if sempy:
                try:
                    items = sempy.list_items(workspace=self._workspace_id, type="Environment")

                    if GUID.valid(identifier):
                        row = items[items["Id"] == identifier]
                    else:
                        row = items[items["Display Name"] == identifier]

                    if not row.empty:
                        self.id = row.iloc[0]["Id"]
                        self.name = row.iloc[0].get("Display Name")
                        return
                except Exception:
                    pass

        # Use REST API
        endpoint = f"/workspaces/{self._workspace_id}/items?type=Environment"
        response = self._client.get(endpoint)
        items = response.json().get("value", [])

        matches = [
            item
            for item in items
            if item.get("displayName") == identifier or item.get("id") == identifier
        ]

        if not matches:
            raise FabiasError(f"Environment '{identifier}' not found")

        self.id = matches[0].get("id")
        self.name = matches[0].get("displayName")

    @property
    def _endpoint(self) -> str:
        """Get the API endpoint for this environment."""
        return f"/workspaces/{self._workspace_id}/environments/{self.id}"

    def refresh(self) -> "Environment":
        """
        Refresh environment data from API.

        Returns:
            Environment: Self for method chaining
        """
        response = self._client.get(self._endpoint)
        data = response.json()

        self.name = data.get("displayName")

        properties = data.get("properties", {})
        publish_details = properties.get("publishDetails", {})
        self.state = publish_details.get("state")

        return self

    @property
    def isPublishing(self) -> bool:
        """Check if environment is currently publishing."""
        return self.state in self.PUBLISH_STATES_ACTIVE

    @property
    def libraries(self) -> Libraries:
        """
        Get library manager for this environment.

        Returns:
            Libraries: Library management interface
        """
        if self._libraries is None:
            if not self.id:
                raise ValueError("Environment ID not available")
            self._libraries = Libraries(self._client, self._workspace_id, self.id)
        return self._libraries

    def publish(self) -> Dict[str, Any]:  # pragma: no cover
        """
        Publish pending environment changes.

        Initiates publishing of staged library changes. This is a
        long-running operation.

        Returns:
            dict: Publish response with state information

        Raises:
            FabiasError: If publish fails to initiate
        """
        response = self._client.post(f"{self._endpoint}/staging/publish")
        data = response.json()

        self.state = data.get("publishDetails", {}).get("state")
        return data

    def cancelPublish(self) -> Dict[str, Any]:
        """
        Cancel an in-progress publish operation.

        Returns:
            dict: Cancel response with updated state
        """
        response = self._client.post(f"{self._endpoint}/staging/cancelPublish")
        data = response.json()

        self.state = data.get("publishDetails", {}).get("state")
        return data

    def waitForPublish(
        self,
        callback: Optional[Callable[["Environment"], None]] = None,
        timeout: int = 1800,
        poll_interval: int = 30,
    ) -> "Environment":
        """
        Wait for publishing to complete.

        Polls the environment status until publishing completes or fails.

        Args:
            callback: Optional callback(Environment) called after each poll
            timeout: Maximum seconds to wait
            poll_interval: Seconds between status checks

        Returns:
            Environment: Self with final publish state

        Raises:
            FabiasError: If timeout reached or publish fails
        """
        from time import sleep

        max_iterations = timeout // poll_interval
        iteration = 0

        while self.isPublishing:
            if iteration >= max_iterations:
                raise FabiasError(f"Publish timed out after {timeout} seconds")

            sleep(poll_interval)
            self.refresh()

            if callback and callable(callback):
                callback(self)

            iteration += 1

        if self.state == "Failed":
            raise FabiasError("Environment publish failed")

        return self

    def __repr__(self) -> str:
        return f"Environment(id={self.id}, name={self.name!r}, state={self.state})"

Attributes

isPublishing property

Check if environment is currently publishing.

libraries property

Get library manager for this environment.

Returns:

Name Type Description
Libraries Libraries

Library management interface

Functions

__init__(client, workspace_id, identifier=None, item_data=None)

Initialize Environment with resolution or pre-populated data.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
workspace_id str

Workspace GUID

required
identifier Optional[str]

Environment display name or GUID (optional if item_data provided)

None
item_data Optional[Dict[str, Any]]

Pre-populated item data from API (skips resolution)

None
Source code in src/fabias/_fabric/items/environment.py
def __init__(
    self,
    client: "FabricClient",
    workspace_id: str,
    identifier: Optional[str] = None,
    item_data: Optional[Dict[str, Any]] = None,
):
    """
    Initialize Environment with resolution or pre-populated data.

    Args:
        client: Authenticated FabricClient
        workspace_id: Workspace GUID
        identifier: Environment display name or GUID (optional if item_data provided)
        item_data: Pre-populated item data from API (skips resolution)
    """
    self._client = client
    self._workspace_id = workspace_id
    self.id: Optional[str] = None
    self.name: Optional[str] = None
    self.state: Optional[str] = None

    self._libraries: Optional[Libraries] = None

    if item_data:
        # Pre-populate from provided data
        self.id = item_data.get("id")
        self.name = item_data.get("displayName")
        # Don't call refresh() for pre-populated items from list
    elif identifier:
        # Resolve via API or sempy
        self._resolve_environment(identifier)
        self.refresh()
    else:
        raise ValueError("Either identifier or item_data must be provided")

cancelPublish()

Cancel an in-progress publish operation.

Returns:

Name Type Description
dict Dict[str, Any]

Cancel response with updated state

Source code in src/fabias/_fabric/items/environment.py
def cancelPublish(self) -> Dict[str, Any]:
    """
    Cancel an in-progress publish operation.

    Returns:
        dict: Cancel response with updated state
    """
    response = self._client.post(f"{self._endpoint}/staging/cancelPublish")
    data = response.json()

    self.state = data.get("publishDetails", {}).get("state")
    return data

publish()

Publish pending environment changes.

Initiates publishing of staged library changes. This is a long-running operation.

Returns:

Name Type Description
dict Dict[str, Any]

Publish response with state information

Raises:

Type Description
FabiasError

If publish fails to initiate

Source code in src/fabias/_fabric/items/environment.py
def publish(self) -> Dict[str, Any]:  # pragma: no cover
    """
    Publish pending environment changes.

    Initiates publishing of staged library changes. This is a
    long-running operation.

    Returns:
        dict: Publish response with state information

    Raises:
        FabiasError: If publish fails to initiate
    """
    response = self._client.post(f"{self._endpoint}/staging/publish")
    data = response.json()

    self.state = data.get("publishDetails", {}).get("state")
    return data

refresh()

Refresh environment data from API.

Returns:

Name Type Description
Environment Environment

Self for method chaining

Source code in src/fabias/_fabric/items/environment.py
def refresh(self) -> "Environment":
    """
    Refresh environment data from API.

    Returns:
        Environment: Self for method chaining
    """
    response = self._client.get(self._endpoint)
    data = response.json()

    self.name = data.get("displayName")

    properties = data.get("properties", {})
    publish_details = properties.get("publishDetails", {})
    self.state = publish_details.get("state")

    return self

waitForPublish(callback=None, timeout=1800, poll_interval=30)

Wait for publishing to complete.

Polls the environment status until publishing completes or fails.

Parameters:

Name Type Description Default
callback Optional[Callable[[Environment], None]]

Optional callback(Environment) called after each poll

None
timeout int

Maximum seconds to wait

1800
poll_interval int

Seconds between status checks

30

Returns:

Name Type Description
Environment Environment

Self with final publish state

Raises:

Type Description
FabiasError

If timeout reached or publish fails

Source code in src/fabias/_fabric/items/environment.py
def waitForPublish(
    self,
    callback: Optional[Callable[["Environment"], None]] = None,
    timeout: int = 1800,
    poll_interval: int = 30,
) -> "Environment":
    """
    Wait for publishing to complete.

    Polls the environment status until publishing completes or fails.

    Args:
        callback: Optional callback(Environment) called after each poll
        timeout: Maximum seconds to wait
        poll_interval: Seconds between status checks

    Returns:
        Environment: Self with final publish state

    Raises:
        FabiasError: If timeout reached or publish fails
    """
    from time import sleep

    max_iterations = timeout // poll_interval
    iteration = 0

    while self.isPublishing:
        if iteration >= max_iterations:
            raise FabiasError(f"Publish timed out after {timeout} seconds")

        sleep(poll_interval)
        self.refresh()

        if callback and callable(callback):
            callback(self)

        iteration += 1

    if self.state == "Failed":
        raise FabiasError("Environment publish failed")

    return self

fabias.Libraries

Manages custom libraries in a Microsoft Fabric Environment.

Provides methods to: - List staging and published libraries - Upload new libraries (wheel, py, jar, tar.gz) - Delete libraries from staging - Replace existing libraries with new versions

Examples:

>>> env = workspace.environment("ML Environment")
>>> libs = env.libraries
>>>
>>> # Get current libraries
>>> libs.refresh()
>>> print(libs.staging)
>>>
>>> # Upload a new library
>>> libs.upload("/path/to/my_package-1.0.0-py3-none-any.whl")
>>>
>>> # Replace an existing library
>>> libs.replace("my_package", "/path/to/my_package-1.1.0-py3-none-any.whl")
Source code in src/fabias/_fabric/items/libraries.py
class Libraries:
    """
    Manages custom libraries in a Microsoft Fabric Environment.

    Provides methods to:
    - List staging and published libraries
    - Upload new libraries (wheel, py, jar, tar.gz)
    - Delete libraries from staging
    - Replace existing libraries with new versions

    Examples:
        >>> env = workspace.environment("ML Environment")
        >>> libs = env.libraries
        >>>
        >>> # Get current libraries
        >>> libs.refresh()
        >>> print(libs.staging)
        >>>
        >>> # Upload a new library
        >>> libs.upload("/path/to/my_package-1.0.0-py3-none-any.whl")
        >>>
        >>> # Replace an existing library
        >>> libs.replace("my_package", "/path/to/my_package-1.1.0-py3-none-any.whl")
    """

    def __init__(self, client: "FabricClient", workspace_id: str, environment_id: str):
        """
        Initialize library manager.

        Args:
            client: Authenticated FabricClient
            workspace_id: Workspace GUID
            environment_id: Environment GUID
        """
        self._client = client
        self._workspace_id = workspace_id
        self._environment_id = environment_id
        self._endpoint = f"/workspaces/{workspace_id}/environments/{environment_id}"

        # Cached library data
        self.staging: Dict[str, Any] = {}
        self.published: Dict[str, Any] = {}

    def refresh(self) -> "Libraries":
        """
        Refresh library data from API.

        Fetches both staging and published library lists.

        Returns:
            Libraries: Self for method chaining
        """
        staging_data = self._get_libraries(staging=True)
        published_data = self._get_libraries(staging=False)

        self.staging = {
            "environment": staging_data.get("environmentYml"),
            "libraries": self._extract_library_list(staging_data),
        }

        self.published = {
            "environment": published_data.get("environmentYml"),
            "libraries": self._extract_library_list(published_data),
        }

        return self

    def _get_libraries(self, staging: bool = False) -> Dict[str, Any]:
        """
        Fetch libraries from API.

        Args:
            staging: If True, fetch staging libraries, else published

        Returns:
            dict: Library data from API
        """
        path = "staging/libraries" if staging else "libraries"
        response = self._client.get(f"{self._endpoint}/{path}")
        return response.json()

    def _extract_library_list(self, data: Dict[str, Any]) -> List[str]:
        """
        Extract flat library list from API response.

        Args:
            data: API response data

        Returns:
            list: List of library filenames
        """
        custom_libs = data.get("customLibraries", {})

        libraries = []
        libraries.extend(custom_libs.get("wheelFiles", []))
        libraries.extend(custom_libs.get("pyFiles", []))
        libraries.extend(custom_libs.get("jarFiles", []))
        libraries.extend(custom_libs.get("rTarFiles", []))

        return libraries

    def upload(self, file_path: str) -> bool:  # pragma: no cover
        """
        Upload a library file to staging.

        Supports .whl, .py, .jar, and .tar.gz files.

        Args:
            file_path: Path to the library file

        Returns:
            bool: True if upload succeeded

        Raises:
            FabiasError: If file not found or upload fails
        """
        path = Path(file_path)

        if not path.exists():
            raise FabiasError(f"File not found: {file_path}")

        with open(path, "rb") as f:
            file_content = f.read()

        files = {"file": (path.name, file_content, "application/octet-stream")}

        self._client.post_multipart(f"{self._endpoint}/staging/libraries", files=files)

        return True

    def delete(self, filename: str) -> bool:  # pragma: no cover
        """
        Delete a library from staging.

        Args:
            filename: Name of the library file to delete

        Returns:
            bool: True if deletion succeeded
        """
        self._client.delete(f"{self._endpoint}/staging/libraries?libraryToDelete={filename}")

        return True

    def replace(
        self,
        library_pattern: str,
        file_path: str,
        callback: Optional[Callable[[str, Dict], None]] = None,
    ) -> None:
        """
        Replace existing library with a new version.

        Deletes all matching libraries from staging and uploads the new file.

        Args:
            library_pattern: Regex pattern to match library names (e.g., "my_package")
            file_path: Path to the new library file
            callback: Optional progress callback(action, details)

        Examples:
            >>> libs.replace(
            ...     "my_package",
            ...     "/path/to/my_package-2.0.0-py3-none-any.whl",
            ...     callback=lambda action, details: print(f"{action}: {details}")
            ... )
        """
        # Refresh library list if needed
        if not self.staging or not self.staging.get("libraries"):
            if callback:
                callback("getLibrary", {})
            self.refresh()
            if callback:
                callback("getLibrary", self.staging)

        # Find and delete matching libraries
        regex = re.compile(library_pattern)
        matches = [lib for lib in self.staging.get("libraries", []) if regex.search(lib)]

        for match in matches:
            if callback:
                callback("delete", {"file": match, "status": "starting"})

            if self.delete(match):
                if callback:
                    callback("delete", {"file": match, "status": "deleted"})

        # Upload new library
        filename = Path(file_path).name
        if callback:
            callback("upload", {"file": filename, "status": "starting"})

        if self.upload(file_path):
            if callback:
                callback("upload", {"file": filename, "status": "uploaded"})

    def __repr__(self) -> str:
        staging_count = len(self.staging.get("libraries", []))
        published_count = len(self.published.get("libraries", []))
        return f"Libraries(staging={staging_count}, published={published_count})"

Functions

__init__(client, workspace_id, environment_id)

Initialize library manager.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
workspace_id str

Workspace GUID

required
environment_id str

Environment GUID

required
Source code in src/fabias/_fabric/items/libraries.py
def __init__(self, client: "FabricClient", workspace_id: str, environment_id: str):
    """
    Initialize library manager.

    Args:
        client: Authenticated FabricClient
        workspace_id: Workspace GUID
        environment_id: Environment GUID
    """
    self._client = client
    self._workspace_id = workspace_id
    self._environment_id = environment_id
    self._endpoint = f"/workspaces/{workspace_id}/environments/{environment_id}"

    # Cached library data
    self.staging: Dict[str, Any] = {}
    self.published: Dict[str, Any] = {}

delete(filename)

Delete a library from staging.

Parameters:

Name Type Description Default
filename str

Name of the library file to delete

required

Returns:

Name Type Description
bool bool

True if deletion succeeded

Source code in src/fabias/_fabric/items/libraries.py
def delete(self, filename: str) -> bool:  # pragma: no cover
    """
    Delete a library from staging.

    Args:
        filename: Name of the library file to delete

    Returns:
        bool: True if deletion succeeded
    """
    self._client.delete(f"{self._endpoint}/staging/libraries?libraryToDelete={filename}")

    return True

refresh()

Refresh library data from API.

Fetches both staging and published library lists.

Returns:

Name Type Description
Libraries Libraries

Self for method chaining

Source code in src/fabias/_fabric/items/libraries.py
def refresh(self) -> "Libraries":
    """
    Refresh library data from API.

    Fetches both staging and published library lists.

    Returns:
        Libraries: Self for method chaining
    """
    staging_data = self._get_libraries(staging=True)
    published_data = self._get_libraries(staging=False)

    self.staging = {
        "environment": staging_data.get("environmentYml"),
        "libraries": self._extract_library_list(staging_data),
    }

    self.published = {
        "environment": published_data.get("environmentYml"),
        "libraries": self._extract_library_list(published_data),
    }

    return self

replace(library_pattern, file_path, callback=None)

Replace existing library with a new version.

Deletes all matching libraries from staging and uploads the new file.

Parameters:

Name Type Description Default
library_pattern str

Regex pattern to match library names (e.g., "my_package")

required
file_path str

Path to the new library file

required
callback Optional[Callable[[str, Dict], None]]

Optional progress callback(action, details)

None

Examples:

>>> libs.replace(
...     "my_package",
...     "/path/to/my_package-2.0.0-py3-none-any.whl",
...     callback=lambda action, details: print(f"{action}: {details}")
... )
Source code in src/fabias/_fabric/items/libraries.py
def replace(
    self,
    library_pattern: str,
    file_path: str,
    callback: Optional[Callable[[str, Dict], None]] = None,
) -> None:
    """
    Replace existing library with a new version.

    Deletes all matching libraries from staging and uploads the new file.

    Args:
        library_pattern: Regex pattern to match library names (e.g., "my_package")
        file_path: Path to the new library file
        callback: Optional progress callback(action, details)

    Examples:
        >>> libs.replace(
        ...     "my_package",
        ...     "/path/to/my_package-2.0.0-py3-none-any.whl",
        ...     callback=lambda action, details: print(f"{action}: {details}")
        ... )
    """
    # Refresh library list if needed
    if not self.staging or not self.staging.get("libraries"):
        if callback:
            callback("getLibrary", {})
        self.refresh()
        if callback:
            callback("getLibrary", self.staging)

    # Find and delete matching libraries
    regex = re.compile(library_pattern)
    matches = [lib for lib in self.staging.get("libraries", []) if regex.search(lib)]

    for match in matches:
        if callback:
            callback("delete", {"file": match, "status": "starting"})

        if self.delete(match):
            if callback:
                callback("delete", {"file": match, "status": "deleted"})

    # Upload new library
    filename = Path(file_path).name
    if callback:
        callback("upload", {"file": filename, "status": "starting"})

    if self.upload(file_path):
        if callback:
            callback("upload", {"file": filename, "status": "uploaded"})

upload(file_path)

Upload a library file to staging.

Supports .whl, .py, .jar, and .tar.gz files.

Parameters:

Name Type Description Default
file_path str

Path to the library file

required

Returns:

Name Type Description
bool bool

True if upload succeeded

Raises:

Type Description
FabiasError

If file not found or upload fails

Source code in src/fabias/_fabric/items/libraries.py
def upload(self, file_path: str) -> bool:  # pragma: no cover
    """
    Upload a library file to staging.

    Supports .whl, .py, .jar, and .tar.gz files.

    Args:
        file_path: Path to the library file

    Returns:
        bool: True if upload succeeded

    Raises:
        FabiasError: If file not found or upload fails
    """
    path = Path(file_path)

    if not path.exists():
        raise FabiasError(f"File not found: {file_path}")

    with open(path, "rb") as f:
        file_content = f.read()

    files = {"file": (path.name, file_content, "application/octet-stream")}

    self._client.post_multipart(f"{self._endpoint}/staging/libraries", files=files)

    return True

fabias.SparkSettings

Mutable container for workspace Spark settings.

Fetch via workspace.spark.settings (lazy-loaded and cached). Mutate properties in-place, then call :meth:commit to push all changes.

Attributes:

Name Type Description
log

Enable automatic Spark run logging.

highconcurrency

Shared session settings.

environment

Default environment -- name and runtime version.

jobs

Job admission control -- reserve cores and queue timeout.

pool

Pool config -- default pool, starter pool, custom compute.

Examples::

s = workspace.spark.settings
s.log = True
s.highconcurrency.interactive = True
s.environment = Environment("ML Env", "1.3")
s.pool.customcompute = True
s.pool.starter.nodes = 10
s.pool.starter.executors = 5
s.commit()
Source code in src/fabias/_fabric/spark.py
class SparkSettings:
    """Mutable container for workspace Spark settings.

    Fetch via ``workspace.spark.settings`` (lazy-loaded and cached). Mutate
    properties in-place, then call :meth:`commit` to push all changes.

    Attributes:
        log:             Enable automatic Spark run logging.
        highconcurrency: Shared session settings.
        environment:     Default environment -- name and runtime version.
        jobs:            Job admission control -- reserve cores and queue timeout.
        pool:            Pool config -- default pool, starter pool, custom compute.

    Examples::

        s = workspace.spark.settings
        s.log = True
        s.highconcurrency.interactive = True
        s.environment = Environment("ML Env", "1.3")
        s.pool.customcompute = True
        s.pool.starter.nodes = 10
        s.pool.starter.executors = 5
        s.commit()
    """

    def __init__(self, client: "FabricClient", workspace_id: str, data: Dict[str, Any]):
        self._client = client
        self._workspace_id = workspace_id
        self._endpoint = f"/workspaces/{workspace_id}/spark/settings"
        self._hydrate(data)

    def _hydrate(self, data: Dict[str, Any]) -> None:
        self._raw = data  # preserve for debugging
        self.log: Optional[bool] = data.get("automaticLog", {}).get("enabled")
        self.highconcurrency: HighConcurrencySettings = HighConcurrencySettings.fromJson(
            data.get("highConcurrency", {})
        )
        self.environment: SparkEnvironment = SparkEnvironment.fromJson(data.get("environment", {}))
        self.jobs: JobSettings = JobSettings.fromJson(data.get("job", {}))
        self.pool: PoolSettings = PoolSettings.fromJson(data.get("pool", {}))

    def _toJson(self) -> Dict[str, Any]:
        payload: Dict[str, Any] = {}
        if self.log is not None:
            payload["automaticLog"] = {"enabled": self.log}
        hc = self.highconcurrency.toJson()
        if hc:
            payload["highConcurrency"] = hc
        env = self.environment.toJson()
        if env:
            payload["environment"] = env
        jobs = self.jobs.toJson()
        if jobs:
            payload["job"] = jobs
        pool = self.pool.toJson()
        if pool:
            payload["pool"] = pool
        return payload

    def commit(self) -> "SparkSettings":
        """Push all accumulated changes to the API, then re-sync from server.

        Returns:
            self -- for optional chaining/inspection.
        """
        self._client.patch(self._endpoint, data=self._toJson())
        response = self._client.get(self._endpoint)
        self._hydrate(response.json())
        return self

    def __repr__(self) -> str:
        return (
            f"SparkSettings("
            f"log={self.log!r}, "
            f"environment={self.environment!r}, "
            f"pool_default={self.pool.default.name!r})"
        )

Functions

commit()

Push all accumulated changes to the API, then re-sync from server.

Returns:

Type Description
'SparkSettings'

self -- for optional chaining/inspection.

Source code in src/fabias/_fabric/spark.py
def commit(self) -> "SparkSettings":
    """Push all accumulated changes to the API, then re-sync from server.

    Returns:
        self -- for optional chaining/inspection.
    """
    self._client.patch(self._endpoint, data=self._toJson())
    response = self._client.get(self._endpoint)
    self._hydrate(response.json())
    return self

fabias.Spark

Manages Spark settings for a Microsoft Fabric workspace.

Accessed via workspace.spark. The settings property lazy-loads and caches the :class:SparkSettings object.

Examples::

workspace.spark.settings.log = True
workspace.spark.settings.pool.customcompute = True
workspace.spark.settings.commit()
Source code in src/fabias/_fabric/spark.py
class Spark:
    """Manages Spark settings for a Microsoft Fabric workspace.

    Accessed via ``workspace.spark``. The ``settings`` property lazy-loads
    and caches the :class:`SparkSettings` object.

    Examples::

        workspace.spark.settings.log = True
        workspace.spark.settings.pool.customcompute = True
        workspace.spark.settings.commit()
    """

    def __init__(self, client: "FabricClient", workspace_id: str):
        self._client = client
        self._workspace_id = workspace_id
        self._settings_cache: Optional[SparkSettings] = None

    @property
    def settings(self) -> SparkSettings:
        """Lazy-load and return the cached :class:`SparkSettings`."""
        if self._settings_cache is None:
            endpoint = f"/workspaces/{self._workspace_id}/spark/settings"
            response = self._client.get(endpoint)
            self._settings_cache = SparkSettings(self._client, self._workspace_id, response.json())
        return self._settings_cache

Attributes

settings property

Lazy-load and return the cached :class:SparkSettings.

fabias.Git

Manages Git integration for a Microsoft Fabric workspace.

Provides methods to: - Connect/disconnect workspace to/from Git repository - Initialize Git connections - Check Git sync status - Update workspace from Git (pull) - Commit workspace changes to Git (push) - Configure Git credentials

Examples:

>>> workspace = client.workspace()
>>> git = workspace.git
>>>
>>> # Connect to repository
>>> git.connect("https://github.com/org/repo", "main")
>>> git.initialize().wait()
>>>
>>> # Check status
>>> status = git.status()
>>> print(f"Changes: {len(status.changes)}")
>>>
>>> # Update from Git
>>> if status.has_changes:
...     operation = git.pull()
...     operation.wait()
Source code in src/fabias/_fabric/git.py
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
class Git:
    """
    Manages Git integration for a Microsoft Fabric workspace.

    Provides methods to:
    - Connect/disconnect workspace to/from Git repository
    - Initialize Git connections
    - Check Git sync status
    - Update workspace from Git (pull)
    - Commit workspace changes to Git (push)
    - Configure Git credentials

    Examples:
        >>> workspace = client.workspace()
        >>> git = workspace.git
        >>>
        >>> # Connect to repository
        >>> git.connect("https://github.com/org/repo", "main")
        >>> git.initialize().wait()
        >>>
        >>> # Check status
        >>> status = git.status()
        >>> print(f"Changes: {len(status.changes)}")
        >>>
        >>> # Update from Git
        >>> if status.has_changes:
        ...     operation = git.pull()
        ...     operation.wait()
    """

    def __init__(self, client: "FabricClient", workspace_id: str):
        """
        Initialize Git handler for a workspace.

        Args:
            client: Authenticated FabricClient
            workspace_id: Workspace GUID
        """
        self._client = client
        self._workspace_id = workspace_id
        self._endpoint = f"/workspaces/{workspace_id}/git"

        # Cached status
        self._status: Optional[GitStatus] = None

    @property
    def credentials(self) -> GitCredentialsAccessor:
        """
        Access Git credentials for this workspace.

        Returns:
            GitCredentialsAccessor: Accessor for getting and updating credentials

        Examples:
            Get credentials:

            >>> creds = git.credentials()
            >>> print(f"Source: {creds.source}")

            Update credentials:

            >>> conn = fabric.connection("GIT-FLOW-SA")
            >>> git.credentials.update(connection=conn)
        """
        return GitCredentialsAccessor(self._client, self._workspace_id)

    def connection(self) -> GitConnection:
        """
        Get Git connection information for this workspace.

        Returns:
            GitConnection: Connection details with repository, branch, provider info

        Examples:
            >>> git = workspace.git
            >>> conn = git.connection()
            >>> if conn.is_connected:
            ...     print(f"Repo: {conn.repository}")
            ...     print(f"Branch: {conn.branch}")
        """
        endpoint = f"/workspaces/{self._workspace_id}/git/connection"

        try:
            response = self._client.get(endpoint)
            data = response.json()
            return GitConnection(data)
        except Exception as e:
            if "404" in str(e):
                # Return empty connection (not connected)
                return GitConnection({"gitProviderDetails": {}})
            raise

    def connect(
        self,
        repository_url: str,
        branch: str,
        directory: str = "/",
        connection: Optional["Connection"] = None,
        provider_type: Optional[Union[GitProviderType, str]] = None,
    ) -> "Git":
        """
        Connect workspace to a Git repository and branch.

        Args:
            repository_url: Full Git repository URL (e.g., https://github.com/owner/repo)
            branch: Branch name to connect to
            directory: Directory path within repository (default: root "/")
            connection: Optional Connection object for authenticated access (e.g., for private repos)

        Returns:
            Git: Self for method chaining

        Examples:
            >>> # Public repository
            >>> git.connect(
            ...     repository_url="https://github.com/myorg/myrepo",
            ...     branch="main",
            ...     directory="/fabric-workspace"
            ... )

            >>> # Private repository with connection
            >>> conn = fabric.connection("GIT-FLOW-SA")
            >>> git.connect(
            ...     repository_url="https://github.com/myorg/private-repo",
            ...     branch="main",
            ...     connection=conn
            ... )
        """
        # Determine provider type
        if provider_type:
            # Convert enum to string if needed
            provider = (
                provider_type.value if isinstance(provider_type, GitProviderType) else provider_type
            )
        else:
            # Auto-detect from URL
            provider = (
                "AzureDevOps"
                if "dev.azure.com" in repository_url or "visualstudio.com" in repository_url
                else "GitHub"
            )

        payload = {
            "gitProviderDetails": {
                "gitProviderType": provider,
                "branchName": branch,
                "directoryName": directory,
            }
        }

        # Add provider-specific fields
        if "dev.azure.com" in repository_url or "visualstudio.com" in repository_url:
            # Azure DevOps
            payload["gitProviderDetails"]["organizationName"] = (
                repository_url.split("/")[-4]
                if "dev.azure.com" in repository_url
                else repository_url.split("/")[-3]
            )
            payload["gitProviderDetails"]["projectName"] = (
                repository_url.split("/")[-3]
                if "dev.azure.com" in repository_url
                else repository_url.split("/")[-2]
            )
            payload["gitProviderDetails"]["repositoryName"] = repository_url.split("/")[-1].replace(
                ".git", ""
            )
        else:
            # GitHub
            payload["gitProviderDetails"]["ownerName"] = repository_url.split("/")[-2]
            payload["gitProviderDetails"]["repositoryName"] = repository_url.split("/")[-1].replace(
                ".git", ""
            )

        # Add connection details if provided
        if connection:
            if not connection.id:
                raise ValueError("Connection ID is required")
            payload["myGitCredentials"] = {
                "source": "ConfiguredConnection",
                "connectionId": connection.id,
            }

        try:
            self._client.post(f"{self._endpoint}/connect", data=payload)
        except Exception as e:
            # Handle 409 Conflict - workspace already connected to Git
            from .._shared.exceptions import ApiError

            if (isinstance(e, ApiError) and e.status_code == 409) or (
                "409" in str(e) and "already connected" in str(e).lower()
            ):
                # Already connected - fetch existing connection instead
                return self
            raise

        return self

    def disconnect(self) -> "Git":
        """
        Disconnect workspace from Git repository.

        Removes the Git connection from this workspace. Does not affect
        the remote repository or any files.

        Returns:
            Git: Self for method chaining

        Examples:
            >>> workspace.git.disconnect()
        """
        self._client.post(f"{self._endpoint}/disconnect")
        return self

    def initialize(
        self, initialization_strategy: Optional[Union[InitializationStrategy, str]] = None
    ) -> AzResponse:
        """
        Initialize a Git connection for a workspace that's connected to Git.

        This initializes the connection after calling connect(), preparing
        the workspace for sync operations.

        Args:
            initialization_strategy: Strategy when content exists on both sides.
                Can be an InitializationStrategy enum or string:
                - None (default): No strategy defined
                - InitializationStrategy.PREFER_REMOTE or "PreferRemote": Prefer remote Git content
                - InitializationStrategy.PREFER_WORKSPACE or "PreferWorkspace": Prefer workspace content

        Returns:
            AzResponse: Response object (either immediate or long-running)
                For immediate operations (200 response), access via .data dict:
                - data['requiredAction']: Next action needed
                - data['remoteCommitHash']: Remote full SHA commit hash
                - data['workspaceHead']: Full SHA hash that workspace is synced to

        Examples:
            >>> # Using enum (recommended - provides IntelliSense)
            >>> from fabias.fabric.enums import InitializationStrategy
            >>> git.connect(repo_url, "main")
            >>> op = git.initialize(InitializationStrategy.PREFER_REMOTE)
            >>> if not op.longRunning:
            ...     print(f"Required action: {op.data.get('requiredAction')}")

            >>> # Using string (also supported)
            >>> op = git.initialize("PreferRemote")
        """
        payload = {}
        if initialization_strategy:
            # Convert enum to string value if needed
            strategy_value = (
                initialization_strategy.value
                if isinstance(initialization_strategy, InitializationStrategy)
                else initialization_strategy
            )
            payload["initializationStrategy"] = strategy_value

        response = self._client.post(
            f"{self._endpoint}/initializeConnection", data=payload if payload else None
        )

        # Response is already an AzResponse from BaseClient._handle_response()
        return response

    def status(self, force_refresh: bool = False) -> GitStatus:
        """
        Get the current Git sync status.

        Args:
            force_refresh: If True, always fetch from API. If False,
                may return cached status.

        Returns:
            GitStatus: Current sync status with changes list

        Raises:
            FabiasError: If Git credentials not configured or API fails
        """
        if self._status is None or force_refresh:
            response = self._client.get(f"{self._endpoint}/status")
            data = response.json()

            # Check for configuration errors
            if data.get("errorCode") == "GitCredentialsNotConfigured":
                raise FabiasError("Git credentials not configured. Use set_connection() first.")

            self._status = GitStatus(data)

        return self._status

    def pull(
        self,
        conflict_resolution: str = "PreferRemote",
        allow_override: bool = True,
        head: Optional[str] = None,
        remote_commit: Optional[str] = None,
    ) -> AzResponse:
        """
        Update the workspace from Git (pull).

        Initiates a long-running operation to sync the workspace with
        the remote Git repository.

        Args:
            conflict_resolution: How to handle conflicts:
                - "PreferRemote": Use remote version (default)
                - "PreferWorkspace": Keep workspace version
            allow_override: Whether to allow overwriting workspace items
            head: Workspace commit hash (uses current if not provided)
            remote_commit: Remote commit to sync to (uses latest if not provided)

        Returns:
            AzResponse: Long-running operation for tracking progress

        Examples:
            >>> operation = git.pull()
            >>> operation.wait(callback=lambda op: print(f"Progress: {op.percent}%"))
            >>> print("Sync complete!")
        """
        # Get current status if not provided
        if not head or not remote_commit:
            status = self.status(force_refresh=True)
            head = head or status.head
            remote_commit = remote_commit or status.remoteCommitHash

        payload = {
            "workspaceHead": head,
            "remoteCommitHash": remote_commit,
            "conflictResolution": {
                "conflictResolutionType": "Workspace",
                "conflictResolutionPolicy": conflict_resolution,
            },
            "options": {"allowOverrideItems": allow_override},
        }

        response = self._client.post(f"{self._endpoint}/updateFromGit", data=payload)

        # Response is already an AzResponse from BaseClient._handle_response()
        return response

    def commitAndPush(
        self, comment: str, items: Optional[List[GitItem]] = None, head: Optional[str] = None
    ) -> AzResponse:
        """
        Commit workspace changes to Git (push).

        Initiates a long-running operation to commit changes from the
        workspace to the remote Git repository.

        Args:
            comment: Commit message
            items: List of GitItem objects to commit. Typically obtained from git.status().
                   If None, commits all pending changes.
            head: Workspace commit hash (uses current if not provided)

        Returns:
            Operation: Long-running operation for tracking progress

        Examples:
            >>> # Commit all changes
            >>> git.commitAndPush("Update reports")
            >>>
            >>> # Commit specific items
            >>> status = git.status()
            >>> workspace_items = status.workspaceChanges
            >>> git.commitAndPush("Add new datasets", items=workspace_items)
        """
        if not head:
            status = self.status(force_refresh=True)
            head = status.head

        payload: Dict[str, Any] = {
            "mode": "All" if items is None else "Selective",
            "workspaceHead": head,
            "comment": comment,
        }

        if items:
            # Convert GitItem objects to dict format required by API
            payload["items"] = [item.identifier for item in items]

        response = self._client.post(f"{self._endpoint}/commitToGit", data=payload)

        # Response is already an AzResponse from BaseClient._handle_response()
        return response

    def __repr__(self) -> str:
        return f"Git(workspace_id={self._workspace_id})"

Attributes

credentials property

Access Git credentials for this workspace.

Returns:

Name Type Description
GitCredentialsAccessor GitCredentialsAccessor

Accessor for getting and updating credentials

Examples:

Get credentials:

>>> creds = git.credentials()
>>> print(f"Source: {creds.source}")

Update credentials:

>>> conn = fabric.connection("GIT-FLOW-SA")
>>> git.credentials.update(connection=conn)

Functions

__init__(client, workspace_id)

Initialize Git handler for a workspace.

Parameters:

Name Type Description Default
client FabricClient

Authenticated FabricClient

required
workspace_id str

Workspace GUID

required
Source code in src/fabias/_fabric/git.py
def __init__(self, client: "FabricClient", workspace_id: str):
    """
    Initialize Git handler for a workspace.

    Args:
        client: Authenticated FabricClient
        workspace_id: Workspace GUID
    """
    self._client = client
    self._workspace_id = workspace_id
    self._endpoint = f"/workspaces/{workspace_id}/git"

    # Cached status
    self._status: Optional[GitStatus] = None

commitAndPush(comment, items=None, head=None)

Commit workspace changes to Git (push).

Initiates a long-running operation to commit changes from the workspace to the remote Git repository.

Parameters:

Name Type Description Default
comment str

Commit message

required
items Optional[List[GitItem]]

List of GitItem objects to commit. Typically obtained from git.status(). If None, commits all pending changes.

None
head Optional[str]

Workspace commit hash (uses current if not provided)

None

Returns:

Name Type Description
Operation AzResponse

Long-running operation for tracking progress

Examples:

>>> # Commit all changes
>>> git.commitAndPush("Update reports")
>>>
>>> # Commit specific items
>>> status = git.status()
>>> workspace_items = status.workspaceChanges
>>> git.commitAndPush("Add new datasets", items=workspace_items)
Source code in src/fabias/_fabric/git.py
def commitAndPush(
    self, comment: str, items: Optional[List[GitItem]] = None, head: Optional[str] = None
) -> AzResponse:
    """
    Commit workspace changes to Git (push).

    Initiates a long-running operation to commit changes from the
    workspace to the remote Git repository.

    Args:
        comment: Commit message
        items: List of GitItem objects to commit. Typically obtained from git.status().
               If None, commits all pending changes.
        head: Workspace commit hash (uses current if not provided)

    Returns:
        Operation: Long-running operation for tracking progress

    Examples:
        >>> # Commit all changes
        >>> git.commitAndPush("Update reports")
        >>>
        >>> # Commit specific items
        >>> status = git.status()
        >>> workspace_items = status.workspaceChanges
        >>> git.commitAndPush("Add new datasets", items=workspace_items)
    """
    if not head:
        status = self.status(force_refresh=True)
        head = status.head

    payload: Dict[str, Any] = {
        "mode": "All" if items is None else "Selective",
        "workspaceHead": head,
        "comment": comment,
    }

    if items:
        # Convert GitItem objects to dict format required by API
        payload["items"] = [item.identifier for item in items]

    response = self._client.post(f"{self._endpoint}/commitToGit", data=payload)

    # Response is already an AzResponse from BaseClient._handle_response()
    return response

connect(repository_url, branch, directory='/', connection=None, provider_type=None)

Connect workspace to a Git repository and branch.

Parameters:

Name Type Description Default
repository_url str

Full Git repository URL (e.g., https://github.com/owner/repo)

required
branch str

Branch name to connect to

required
directory str

Directory path within repository (default: root "/")

'/'
connection Optional[Connection]

Optional Connection object for authenticated access (e.g., for private repos)

None

Returns:

Name Type Description
Git Git

Self for method chaining

Examples:

>>> # Public repository
>>> git.connect(
...     repository_url="https://github.com/myorg/myrepo",
...     branch="main",
...     directory="/fabric-workspace"
... )
>>> # Private repository with connection
>>> conn = fabric.connection("GIT-FLOW-SA")
>>> git.connect(
...     repository_url="https://github.com/myorg/private-repo",
...     branch="main",
...     connection=conn
... )
Source code in src/fabias/_fabric/git.py
def connect(
    self,
    repository_url: str,
    branch: str,
    directory: str = "/",
    connection: Optional["Connection"] = None,
    provider_type: Optional[Union[GitProviderType, str]] = None,
) -> "Git":
    """
    Connect workspace to a Git repository and branch.

    Args:
        repository_url: Full Git repository URL (e.g., https://github.com/owner/repo)
        branch: Branch name to connect to
        directory: Directory path within repository (default: root "/")
        connection: Optional Connection object for authenticated access (e.g., for private repos)

    Returns:
        Git: Self for method chaining

    Examples:
        >>> # Public repository
        >>> git.connect(
        ...     repository_url="https://github.com/myorg/myrepo",
        ...     branch="main",
        ...     directory="/fabric-workspace"
        ... )

        >>> # Private repository with connection
        >>> conn = fabric.connection("GIT-FLOW-SA")
        >>> git.connect(
        ...     repository_url="https://github.com/myorg/private-repo",
        ...     branch="main",
        ...     connection=conn
        ... )
    """
    # Determine provider type
    if provider_type:
        # Convert enum to string if needed
        provider = (
            provider_type.value if isinstance(provider_type, GitProviderType) else provider_type
        )
    else:
        # Auto-detect from URL
        provider = (
            "AzureDevOps"
            if "dev.azure.com" in repository_url or "visualstudio.com" in repository_url
            else "GitHub"
        )

    payload = {
        "gitProviderDetails": {
            "gitProviderType": provider,
            "branchName": branch,
            "directoryName": directory,
        }
    }

    # Add provider-specific fields
    if "dev.azure.com" in repository_url or "visualstudio.com" in repository_url:
        # Azure DevOps
        payload["gitProviderDetails"]["organizationName"] = (
            repository_url.split("/")[-4]
            if "dev.azure.com" in repository_url
            else repository_url.split("/")[-3]
        )
        payload["gitProviderDetails"]["projectName"] = (
            repository_url.split("/")[-3]
            if "dev.azure.com" in repository_url
            else repository_url.split("/")[-2]
        )
        payload["gitProviderDetails"]["repositoryName"] = repository_url.split("/")[-1].replace(
            ".git", ""
        )
    else:
        # GitHub
        payload["gitProviderDetails"]["ownerName"] = repository_url.split("/")[-2]
        payload["gitProviderDetails"]["repositoryName"] = repository_url.split("/")[-1].replace(
            ".git", ""
        )

    # Add connection details if provided
    if connection:
        if not connection.id:
            raise ValueError("Connection ID is required")
        payload["myGitCredentials"] = {
            "source": "ConfiguredConnection",
            "connectionId": connection.id,
        }

    try:
        self._client.post(f"{self._endpoint}/connect", data=payload)
    except Exception as e:
        # Handle 409 Conflict - workspace already connected to Git
        from .._shared.exceptions import ApiError

        if (isinstance(e, ApiError) and e.status_code == 409) or (
            "409" in str(e) and "already connected" in str(e).lower()
        ):
            # Already connected - fetch existing connection instead
            return self
        raise

    return self

connection()

Get Git connection information for this workspace.

Returns:

Name Type Description
GitConnection GitConnection

Connection details with repository, branch, provider info

Examples:

>>> git = workspace.git
>>> conn = git.connection()
>>> if conn.is_connected:
...     print(f"Repo: {conn.repository}")
...     print(f"Branch: {conn.branch}")
Source code in src/fabias/_fabric/git.py
def connection(self) -> GitConnection:
    """
    Get Git connection information for this workspace.

    Returns:
        GitConnection: Connection details with repository, branch, provider info

    Examples:
        >>> git = workspace.git
        >>> conn = git.connection()
        >>> if conn.is_connected:
        ...     print(f"Repo: {conn.repository}")
        ...     print(f"Branch: {conn.branch}")
    """
    endpoint = f"/workspaces/{self._workspace_id}/git/connection"

    try:
        response = self._client.get(endpoint)
        data = response.json()
        return GitConnection(data)
    except Exception as e:
        if "404" in str(e):
            # Return empty connection (not connected)
            return GitConnection({"gitProviderDetails": {}})
        raise

disconnect()

Disconnect workspace from Git repository.

Removes the Git connection from this workspace. Does not affect the remote repository or any files.

Returns:

Name Type Description
Git Git

Self for method chaining

Examples:

>>> workspace.git.disconnect()
Source code in src/fabias/_fabric/git.py
def disconnect(self) -> "Git":
    """
    Disconnect workspace from Git repository.

    Removes the Git connection from this workspace. Does not affect
    the remote repository or any files.

    Returns:
        Git: Self for method chaining

    Examples:
        >>> workspace.git.disconnect()
    """
    self._client.post(f"{self._endpoint}/disconnect")
    return self

initialize(initialization_strategy=None)

Initialize a Git connection for a workspace that's connected to Git.

This initializes the connection after calling connect(), preparing the workspace for sync operations.

Parameters:

Name Type Description Default
initialization_strategy Optional[Union[InitializationStrategy, str]]

Strategy when content exists on both sides. Can be an InitializationStrategy enum or string: - None (default): No strategy defined - InitializationStrategy.PREFER_REMOTE or "PreferRemote": Prefer remote Git content - InitializationStrategy.PREFER_WORKSPACE or "PreferWorkspace": Prefer workspace content

None

Returns:

Name Type Description
AzResponse AzResponse

Response object (either immediate or long-running) For immediate operations (200 response), access via .data dict: - data['requiredAction']: Next action needed - data['remoteCommitHash']: Remote full SHA commit hash - data['workspaceHead']: Full SHA hash that workspace is synced to

Examples:

>>> # Using enum (recommended - provides IntelliSense)
>>> from fabias.fabric.enums import InitializationStrategy
>>> git.connect(repo_url, "main")
>>> op = git.initialize(InitializationStrategy.PREFER_REMOTE)
>>> if not op.longRunning:
...     print(f"Required action: {op.data.get('requiredAction')}")
>>> # Using string (also supported)
>>> op = git.initialize("PreferRemote")
Source code in src/fabias/_fabric/git.py
def initialize(
    self, initialization_strategy: Optional[Union[InitializationStrategy, str]] = None
) -> AzResponse:
    """
    Initialize a Git connection for a workspace that's connected to Git.

    This initializes the connection after calling connect(), preparing
    the workspace for sync operations.

    Args:
        initialization_strategy: Strategy when content exists on both sides.
            Can be an InitializationStrategy enum or string:
            - None (default): No strategy defined
            - InitializationStrategy.PREFER_REMOTE or "PreferRemote": Prefer remote Git content
            - InitializationStrategy.PREFER_WORKSPACE or "PreferWorkspace": Prefer workspace content

    Returns:
        AzResponse: Response object (either immediate or long-running)
            For immediate operations (200 response), access via .data dict:
            - data['requiredAction']: Next action needed
            - data['remoteCommitHash']: Remote full SHA commit hash
            - data['workspaceHead']: Full SHA hash that workspace is synced to

    Examples:
        >>> # Using enum (recommended - provides IntelliSense)
        >>> from fabias.fabric.enums import InitializationStrategy
        >>> git.connect(repo_url, "main")
        >>> op = git.initialize(InitializationStrategy.PREFER_REMOTE)
        >>> if not op.longRunning:
        ...     print(f"Required action: {op.data.get('requiredAction')}")

        >>> # Using string (also supported)
        >>> op = git.initialize("PreferRemote")
    """
    payload = {}
    if initialization_strategy:
        # Convert enum to string value if needed
        strategy_value = (
            initialization_strategy.value
            if isinstance(initialization_strategy, InitializationStrategy)
            else initialization_strategy
        )
        payload["initializationStrategy"] = strategy_value

    response = self._client.post(
        f"{self._endpoint}/initializeConnection", data=payload if payload else None
    )

    # Response is already an AzResponse from BaseClient._handle_response()
    return response

pull(conflict_resolution='PreferRemote', allow_override=True, head=None, remote_commit=None)

Update the workspace from Git (pull).

Initiates a long-running operation to sync the workspace with the remote Git repository.

Parameters:

Name Type Description Default
conflict_resolution str

How to handle conflicts: - "PreferRemote": Use remote version (default) - "PreferWorkspace": Keep workspace version

'PreferRemote'
allow_override bool

Whether to allow overwriting workspace items

True
head Optional[str]

Workspace commit hash (uses current if not provided)

None
remote_commit Optional[str]

Remote commit to sync to (uses latest if not provided)

None

Returns:

Name Type Description
AzResponse AzResponse

Long-running operation for tracking progress

Examples:

>>> operation = git.pull()
>>> operation.wait(callback=lambda op: print(f"Progress: {op.percent}%"))
>>> print("Sync complete!")
Source code in src/fabias/_fabric/git.py
def pull(
    self,
    conflict_resolution: str = "PreferRemote",
    allow_override: bool = True,
    head: Optional[str] = None,
    remote_commit: Optional[str] = None,
) -> AzResponse:
    """
    Update the workspace from Git (pull).

    Initiates a long-running operation to sync the workspace with
    the remote Git repository.

    Args:
        conflict_resolution: How to handle conflicts:
            - "PreferRemote": Use remote version (default)
            - "PreferWorkspace": Keep workspace version
        allow_override: Whether to allow overwriting workspace items
        head: Workspace commit hash (uses current if not provided)
        remote_commit: Remote commit to sync to (uses latest if not provided)

    Returns:
        AzResponse: Long-running operation for tracking progress

    Examples:
        >>> operation = git.pull()
        >>> operation.wait(callback=lambda op: print(f"Progress: {op.percent}%"))
        >>> print("Sync complete!")
    """
    # Get current status if not provided
    if not head or not remote_commit:
        status = self.status(force_refresh=True)
        head = head or status.head
        remote_commit = remote_commit or status.remoteCommitHash

    payload = {
        "workspaceHead": head,
        "remoteCommitHash": remote_commit,
        "conflictResolution": {
            "conflictResolutionType": "Workspace",
            "conflictResolutionPolicy": conflict_resolution,
        },
        "options": {"allowOverrideItems": allow_override},
    }

    response = self._client.post(f"{self._endpoint}/updateFromGit", data=payload)

    # Response is already an AzResponse from BaseClient._handle_response()
    return response

status(force_refresh=False)

Get the current Git sync status.

Parameters:

Name Type Description Default
force_refresh bool

If True, always fetch from API. If False, may return cached status.

False

Returns:

Name Type Description
GitStatus GitStatus

Current sync status with changes list

Raises:

Type Description
FabiasError

If Git credentials not configured or API fails

Source code in src/fabias/_fabric/git.py
def status(self, force_refresh: bool = False) -> GitStatus:
    """
    Get the current Git sync status.

    Args:
        force_refresh: If True, always fetch from API. If False,
            may return cached status.

    Returns:
        GitStatus: Current sync status with changes list

    Raises:
        FabiasError: If Git credentials not configured or API fails
    """
    if self._status is None or force_refresh:
        response = self._client.get(f"{self._endpoint}/status")
        data = response.json()

        # Check for configuration errors
        if data.get("errorCode") == "GitCredentialsNotConfigured":
            raise FabiasError("Git credentials not configured. Use set_connection() first.")

        self._status = GitStatus(data)

    return self._status

Enums

fabias.WorkspaceRole

Bases: str, Enum

Workspace role assignment types.

Used when managing workspace role assignments.

Source code in src/fabias/_fabric/_shared/enums.py
class WorkspaceRole(str, Enum):
    """
    Workspace role assignment types.

    Used when managing workspace role assignments.
    """

    ADMIN = "Admin"
    MEMBER = "Member"
    CONTRIBUTOR = "Contributor"
    VIEWER = "Viewer"

fabias.ConnectionRole

Bases: str, Enum

Connection role assignment types.

Used when managing connection role assignments.

Source code in src/fabias/_fabric/_shared/enums.py
class ConnectionRole(str, Enum):
    """
    Connection role assignment types.

    Used when managing connection role assignments.
    """

    USER = "User"
    USER_WITH_RESHARE = "UserWithReshare"
    OWNER = "Owner"

fabias.ConnectivityType

Bases: str, Enum

Connection connectivity type enumeration.

Specifies how a connection connects to data sources. Additional connectivity types may be added over time.

Attributes:

Name Type Description
SHAREABLE_CLOUD

Connection through the cloud that can be shared

PERSONAL_CLOUD

Connection through the cloud that cannot be shared

ON_PREMISES_GATEWAY

Connection through an on-premises data gateway

ON_PREMISES_GATEWAY_PERSONAL

Connection through a personal on-premises data gateway

VIRTUAL_NETWORK_GATEWAY

Connection through a virtual network data gateway

AUTOMATIC

Connection through the cloud using implicit data connection (SSO scenarios)

NONE

Connection is not bound

Source code in src/fabias/_fabric/_shared/enums.py
class ConnectivityType(str, Enum):
    """
    Connection connectivity type enumeration.

    Specifies how a connection connects to data sources.
    Additional connectivity types may be added over time.

    Attributes:
        SHAREABLE_CLOUD: Connection through the cloud that can be shared
        PERSONAL_CLOUD: Connection through the cloud that cannot be shared
        ON_PREMISES_GATEWAY: Connection through an on-premises data gateway
        ON_PREMISES_GATEWAY_PERSONAL: Connection through a personal on-premises data gateway
        VIRTUAL_NETWORK_GATEWAY: Connection through a virtual network data gateway
        AUTOMATIC: Connection through the cloud using implicit data connection (SSO scenarios)
        NONE: Connection is not bound
    """

    SHAREABLE_CLOUD = "ShareableCloud"
    PERSONAL_CLOUD = "PersonalCloud"
    ON_PREMISES_GATEWAY = "OnPremisesGateway"
    ON_PREMISES_GATEWAY_PERSONAL = "OnPremisesGatewayPersonal"
    VIRTUAL_NETWORK_GATEWAY = "VirtualNetworkGateway"
    AUTOMATIC = "Automatic"
    NONE = "None"

fabias.PrivacyLevel

Bases: str, Enum

Connection privacy level enumeration.

Specifies the privacy level setting of a connection. Additional privacy levels may be added over time.

Attributes:

Name Type Description
NONE

No privacy level configured

PRIVATE

Sensitive/confidential data, restricted to authorized users

ORGANIZATIONAL

Can fold into private and other organizational connections

PUBLIC

Files, internet, workbook data - visible to everyone

Source code in src/fabias/_fabric/_shared/enums.py
class PrivacyLevel(str, Enum):
    """
    Connection privacy level enumeration.

    Specifies the privacy level setting of a connection.
    Additional privacy levels may be added over time.

    Attributes:
        NONE: No privacy level configured
        PRIVATE: Sensitive/confidential data, restricted to authorized users
        ORGANIZATIONAL: Can fold into private and other organizational connections
        PUBLIC: Files, internet, workbook data - visible to everyone
    """

    NONE = "None"
    PRIVATE = "Private"
    ORGANIZATIONAL = "Organizational"
    PUBLIC = "Public"

fabias.SingleSignOnType

Bases: str, Enum

Single sign-on type enumeration.

Specifies the SSO authentication method. Additional SSO types may be added over time.

Attributes:

Name Type Description
NONE

No single sign-on

KERBEROS

Kerberos SSO

MICROSOFT_ENTRA_ID

Microsoft Entra ID SSO

SAML

Security Assertion Markup Language SSO

KERBEROS_DIRECT_QUERY_AND_REFRESH

Kerberos DirectQuery and Refresh SSO

Source code in src/fabias/_fabric/_shared/enums.py
class SingleSignOnType(str, Enum):
    """
    Single sign-on type enumeration.

    Specifies the SSO authentication method.
    Additional SSO types may be added over time.

    Attributes:
        NONE: No single sign-on
        KERBEROS: Kerberos SSO
        MICROSOFT_ENTRA_ID: Microsoft Entra ID SSO
        SAML: Security Assertion Markup Language SSO
        KERBEROS_DIRECT_QUERY_AND_REFRESH: Kerberos DirectQuery and Refresh SSO
    """

    NONE = "None"
    KERBEROS = "Kerberos"
    MICROSOFT_ENTRA_ID = "MicrosoftEntraID"
    SAML = "SecurityAssertionMarkupLanguage"
    KERBEROS_DIRECT_QUERY_AND_REFRESH = "KerberosDirectQueryAndRefresh"

fabias.ConnectionEncryption

Bases: str, Enum

Connection encryption type enumeration.

Specifies the encryption setting used during connection. Additional encryption values may be added over time.

Attributes:

Name Type Description
ENCRYPTED

Connection uses encryption

ANY

Tries encrypted first, falls back to unencrypted

NOT_ENCRYPTED

Connection does not use encryption

Source code in src/fabias/_fabric/_shared/enums.py
class ConnectionEncryption(str, Enum):
    """
    Connection encryption type enumeration.

    Specifies the encryption setting used during connection.
    Additional encryption values may be added over time.

    Attributes:
        ENCRYPTED: Connection uses encryption
        ANY: Tries encrypted first, falls back to unencrypted
        NOT_ENCRYPTED: Connection does not use encryption
    """

    ENCRYPTED = "Encrypted"
    ANY = "Any"
    NOT_ENCRYPTED = "NotEncrypted"

fabias.ItemType

Bases: str, Enum

Workspace item type enumeration.

Specifies the type of item in a workspace. Additional item types may be added over time.

Attributes:

Name Type Description
DASHBOARD

PowerBI dashboard

REPORT

PowerBI report

SEMANTIC_MODEL

PowerBI semantic model

PAGINATED_REPORT

PowerBI paginated report

DATAMART

PowerBI datamart

LAKEHOUSE

A lakehouse

EVENTHOUSE

An eventhouse

ENVIRONMENT

An environment

KQL_DATABASE

A KQL database

KQL_QUERYSET

A KQL queryset

KQL_DASHBOARD

A KQL dashboard

DATA_PIPELINE

A data pipeline

NOTEBOOK

A notebook

SPARK_JOB_DEFINITION

A spark job definition

ML_EXPERIMENT

A machine learning experiment

ML_MODEL

A machine learning model

WAREHOUSE

A warehouse

EVENTSTREAM

An eventstream

SQL_ENDPOINT

An SQL endpoint

MIRRORED_WAREHOUSE

A mirrored warehouse

MIRRORED_DATABASE

A mirrored database

REFLEX

A Reflex

GRAPHQL_API

An API for GraphQL item

MOUNTED_DATA_FACTORY

A MountedDataFactory

SQL_DATABASE

A SQLDatabase

COPY_JOB

A Copy job

VARIABLE_LIBRARY

A VariableLibrary

DATAFLOW

A Dataflow

APACHE_AIRFLOW_JOB

An ApacheAirflowJob

WAREHOUSE_SNAPSHOT

A Warehouse snapshot

DIGITAL_TWIN_BUILDER

A DigitalTwinBuilder

DIGITAL_TWIN_BUILDER_FLOW

A Digital Twin Builder Flow

MIRRORED_AZURE_DATABRICKS_CATALOG

A mirrored azure databricks catalog

MAP

A Map

ANOMALY_DETECTOR

An Anomaly Detector

USER_DATA_FUNCTION

A User Data Function

GRAPH_MODEL

A GraphModel

GRAPH_QUERY_SET

A Graph QuerySet

SNOWFLAKE_DATABASE

A Snowflake Database

OPERATIONS_AGENT

A OperationsAgent

COSMOS_DB_DATABASE

A Cosmos DB Database

ONTOLOGY

An Ontology

EVENT_SCHEMA_SET

An EventSchemaSet

Source code in src/fabias/_fabric/_shared/enums.py
class ItemType(str, Enum):
    """
    Workspace item type enumeration.

    Specifies the type of item in a workspace.
    Additional item types may be added over time.

    Attributes:
        DASHBOARD: PowerBI dashboard
        REPORT: PowerBI report
        SEMANTIC_MODEL: PowerBI semantic model
        PAGINATED_REPORT: PowerBI paginated report
        DATAMART: PowerBI datamart
        LAKEHOUSE: A lakehouse
        EVENTHOUSE: An eventhouse
        ENVIRONMENT: An environment
        KQL_DATABASE: A KQL database
        KQL_QUERYSET: A KQL queryset
        KQL_DASHBOARD: A KQL dashboard
        DATA_PIPELINE: A data pipeline
        NOTEBOOK: A notebook
        SPARK_JOB_DEFINITION: A spark job definition
        ML_EXPERIMENT: A machine learning experiment
        ML_MODEL: A machine learning model
        WAREHOUSE: A warehouse
        EVENTSTREAM: An eventstream
        SQL_ENDPOINT: An SQL endpoint
        MIRRORED_WAREHOUSE: A mirrored warehouse
        MIRRORED_DATABASE: A mirrored database
        REFLEX: A Reflex
        GRAPHQL_API: An API for GraphQL item
        MOUNTED_DATA_FACTORY: A MountedDataFactory
        SQL_DATABASE: A SQLDatabase
        COPY_JOB: A Copy job
        VARIABLE_LIBRARY: A VariableLibrary
        DATAFLOW: A Dataflow
        APACHE_AIRFLOW_JOB: An ApacheAirflowJob
        WAREHOUSE_SNAPSHOT: A Warehouse snapshot
        DIGITAL_TWIN_BUILDER: A DigitalTwinBuilder
        DIGITAL_TWIN_BUILDER_FLOW: A Digital Twin Builder Flow
        MIRRORED_AZURE_DATABRICKS_CATALOG: A mirrored azure databricks catalog
        MAP: A Map
        ANOMALY_DETECTOR: An Anomaly Detector
        USER_DATA_FUNCTION: A User Data Function
        GRAPH_MODEL: A GraphModel
        GRAPH_QUERY_SET: A Graph QuerySet
        SNOWFLAKE_DATABASE: A Snowflake Database
        OPERATIONS_AGENT: A OperationsAgent
        COSMOS_DB_DATABASE: A Cosmos DB Database
        ONTOLOGY: An Ontology
        EVENT_SCHEMA_SET: An EventSchemaSet
    """

    DASHBOARD = "Dashboard"
    REPORT = "Report"
    SEMANTIC_MODEL = "SemanticModel"
    PAGINATED_REPORT = "PaginatedReport"
    DATAMART = "Datamart"
    LAKEHOUSE = "Lakehouse"
    EVENTHOUSE = "Eventhouse"
    ENVIRONMENT = "Environment"
    KQL_DATABASE = "KQLDatabase"
    KQL_QUERYSET = "KQLQueryset"
    KQL_DASHBOARD = "KQLDashboard"
    DATA_PIPELINE = "DataPipeline"
    NOTEBOOK = "Notebook"
    SPARK_JOB_DEFINITION = "SparkJobDefinition"
    ML_EXPERIMENT = "MLExperiment"
    ML_MODEL = "MLModel"
    WAREHOUSE = "Warehouse"
    EVENTSTREAM = "Eventstream"
    SQL_ENDPOINT = "SQLEndpoint"
    MIRRORED_WAREHOUSE = "MirroredWarehouse"
    MIRRORED_DATABASE = "MirroredDatabase"
    REFLEX = "Reflex"
    GRAPHQL_API = "GraphQLApi"
    MOUNTED_DATA_FACTORY = "MountedDataFactory"
    SQL_DATABASE = "SQLDatabase"
    COPY_JOB = "CopyJob"
    VARIABLE_LIBRARY = "VariableLibrary"
    DATAFLOW = "Dataflow"
    APACHE_AIRFLOW_JOB = "ApacheAirflowJob"
    WAREHOUSE_SNAPSHOT = "WarehouseSnapshot"
    DIGITAL_TWIN_BUILDER = "DigitalTwinBuilder"
    DIGITAL_TWIN_BUILDER_FLOW = "DigitalTwinBuilderFlow"
    MIRRORED_AZURE_DATABRICKS_CATALOG = "MirroredAzureDatabricksCatalog"
    MAP = "Map"
    ANOMALY_DETECTOR = "AnomalyDetector"
    USER_DATA_FUNCTION = "UserDataFunction"
    GRAPH_MODEL = "GraphModel"
    GRAPH_QUERY_SET = "GraphQuerySet"
    SNOWFLAKE_DATABASE = "SnowflakeDatabase"
    OPERATIONS_AGENT = "OperationsAgent"
    COSMOS_DB_DATABASE = "CosmosDBDatabase"
    ONTOLOGY = "Ontology"
    EVENT_SCHEMA_SET = "EventSchemaSet"

fabias.ItemAccess

Bases: str, Enum

Item access permission enumeration.

Specifies individual access permissions that can be combined in lists. Use in List[ItemAccess] for members that can have multiple permissions.

Attributes:

Name Type Description
READ

Item Access Read

WRITE

Item Access Write

RESHARE

Item Access Reshare

EXPLORE

Item Access Explore

EXECUTE

Item Access Execute

READALL

Item Access ReadAll

Examples:

>>> # Single permission
>>> member = Member(memberType=MemberType.ENTRA, access=[ItemAccess.READ])
>>>
>>> # Multiple permissions
>>> member = Member(
...     memberType=MemberType.ENTRA,
...     access=[ItemAccess.READ, ItemAccess.WRITE, ItemAccess.EXECUTE]
... )
Source code in src/fabias/_fabric/_shared/enums.py
class ItemAccess(str, Enum):
    """
    Item access permission enumeration.

    Specifies individual access permissions that can be combined in lists.
    Use in List[ItemAccess] for members that can have multiple permissions.

    Attributes:
        READ: Item Access Read
        WRITE: Item Access Write
        RESHARE: Item Access Reshare
        EXPLORE: Item Access Explore
        EXECUTE: Item Access Execute
        READALL: Item Access ReadAll

    Examples:
        >>> # Single permission
        >>> member = Member(memberType=MemberType.ENTRA, access=[ItemAccess.READ])
        >>>
        >>> # Multiple permissions
        >>> member = Member(
        ...     memberType=MemberType.ENTRA,
        ...     access=[ItemAccess.READ, ItemAccess.WRITE, ItemAccess.EXECUTE]
        ... )
    """

    READ = "Read"
    WRITE = "Write"
    RESHARE = "Reshare"
    EXPLORE = "Explore"
    EXECUTE = "Execute"
    READALL = "ReadAll"

fabias.ReadWrite

Bases: str, Enum

ReadWrite type enumeration.

Specifies the level of permissions to an item. Additional item types may be added over time.

Attributes:

Name Type Description
READ

read-only access

READWRITE

Read and write access

Source code in src/fabias/_fabric/_shared/enums.py
class ReadWrite(str, Enum):
    """
    ReadWrite type enumeration.

    Specifies the level of permissions to an item.
    Additional item types may be added over time.

    Attributes:
        READ: read-only access
        READWRITE: Read and write access
    """

    READ = "Read"
    READWRITE = "ReadWrite"

fabias.NotebookFormat

Bases: str, Enum

Notebook format enumeration.

Specifies the format of a notebook. Additional formats may be added over time.

Attributes:

Name Type Description
IPYN

Databricks notebook format

GITSOURCE

Fabric Git source format

Source code in src/fabias/_fabric/_shared/enums.py
class NotebookFormat(str, Enum):
    """
    Notebook format enumeration.

    Specifies the format of a notebook.
    Additional formats may be added over time.

    Attributes:
        IPYN: Databricks notebook format
        GITSOURCE: Fabric Git source format
    """

    IPYN = "ipynb"
    GITSOURCE = "fabricGitSource"

fabias.PoolType

Bases: str, Enum

Spark pool type enumeration.

Identifies the type of Spark pool assigned as the default for a workspace.

Attributes:

Name Type Description
WORKSPACE

Starter pool provisioned at the workspace level

CAPACITY

Custom pool provisioned at the capacity level

Source code in src/fabias/_fabric/_shared/enums.py
class PoolType(str, Enum):
    """
    Spark pool type enumeration.

    Identifies the type of Spark pool assigned as the default for a workspace.

    Attributes:
        WORKSPACE: Starter pool provisioned at the workspace level
        CAPACITY: Custom pool provisioned at the capacity level
    """

    WORKSPACE = "Workspace"
    CAPACITY = "Capacity"

fabias.GitProviderType

Bases: str, Enum

Git provider type enumeration.

Used when connecting a workspace to a Git repository. Additional provider types may be added over time.

Attributes:

Name Type Description
AZURE_DEVOPS

Azure DevOps provider

GITHUB

GitHub provider

Source code in src/fabias/_fabric/_shared/enums.py
class GitProviderType(str, Enum):
    """
    Git provider type enumeration.

    Used when connecting a workspace to a Git repository.
    Additional provider types may be added over time.

    Attributes:
        AZURE_DEVOPS: Azure DevOps provider
        GITHUB: GitHub provider
    """

    AZURE_DEVOPS = "AzureDevOps"
    GITHUB = "GitHub"

fabias.OperationStatus

Bases: str, Enum

Long-running operation status enumeration.

Represents the current state of an operation. Additional statuses may be added over time.

Attributes:

Name Type Description
UNDEFINED

Status is undefined

NOT_STARTED

Operation not started

RUNNING

Operation is running

SUCCEEDED

Operation completed successfully

FAILED

Operation failed

Source code in src/fabias/_fabric/_shared/enums.py
class OperationStatus(str, Enum):
    """
    Long-running operation status enumeration.

    Represents the current state of an operation.
    Additional statuses may be added over time.

    Attributes:
        UNDEFINED: Status is undefined
        NOT_STARTED: Operation not started
        RUNNING: Operation is running
        SUCCEEDED: Operation completed successfully
        FAILED: Operation failed
    """

    UNDEFINED = "Undefined"
    NOT_STARTED = "NotStarted"
    RUNNING = "Running"
    SUCCEEDED = "Succeeded"
    FAILED = "Failed"