Skip to content

Contributing

Contributing to fabias

Thank you for your interest in contributing to fabias! This document provides guidelines and instructions for contributing.

Development Setup

Prerequisites

  • Python 3.9 or higher
  • Git

Installation

  1. Clone the repository:

    git clone https://github.com/fullsteam-corp-shared/pypi-pkg-fabias.git
    cd pypi-pkg-fabias
    

  2. Create a virtual environment:

    python -m venv .venv
    .venv\Scripts\activate  # Windows
    # or
    source .venv/bin/activate  # macOS/Linux
    

  3. Install in development mode with dev dependencies:

    pip install -e ".[dev]"
    

  4. (Optional) Install documentation dependencies:

    pip install -e ".[docs]"
    

Project Structure

fabias/
├── src/fabias/         # Source code
│   ├── _shared/        # Internal shared utilities (auth, client, exceptions)
│   ├── fabric/         # Microsoft Fabric API
│   ├── datafactory/    # Azure Data Factory API
│   ├── teams/          # Microsoft Teams API
│   ├── secrets/        # Azure Key Vault API
│   └── cards/          # Adaptive Cards builder
├── tests/              # Test files
├── docs/               # Documentation (MkDocs)
└── pyproject.toml      # Project configuration

Code Style

Naming Conventions

  • Methods: camelCase (e.g., workspace.pipelines(), role.toJson())
  • Properties: snake_case (e.g., workspace.capacity_id, connection.privacy_level)
  • Classes: PascalCase (e.g., FabricClient, ServicePrincipalAuth)
  • Module functions: snake_case (e.g., fabric.workspace())

Type Hints

All public APIs must have type annotations:

def workspace(workspace_id: Optional[str] = None) -> Workspace:
    """Get a workspace by name or ID."""
    ...

Docstrings

Use Google-style docstrings with Args, Returns, Raises, and Examples:

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

    Raises:
        NotFoundError: If pipeline not found

    Examples:
        >>> pipeline = workspace.pipeline("Daily ETL")
        >>> job = pipeline.run()
    """

Testing

Running Tests

# Run all unit tests (integration tests excluded by default)
pytest

# Run with verbose output
pytest -v

# Run specific test file
pytest tests/test_fabric_unit.py

# Run integration tests (requires credentials)
pytest -m integration

# Run with coverage
pytest --cov=src/fabias --cov-report=term-missing

Test Organization

  • Unit tests: Use responses library to mock HTTP calls. Fast and safe.
  • Integration tests: Real API calls. Marked with @pytest.mark.integration.

Writing Tests

import pytest
import responses
import fabias.fabric as fabric

class TestMyFeature:
    @responses.activate
    def test_my_feature_works(self, mock_auth):
        """Test that my feature works correctly."""
        # Mock the API response
        responses.add(
            responses.GET,
            "https://api.fabric.microsoft.com/v1/workspaces",
            json={"value": [{"id": "ws-1", "displayName": "Test"}]},
            status=200
        )

        fabric.client(auth=mock_auth)
        workspaces = fabric.workspaces()

        assert len(workspaces) == 1
        assert workspaces[0].name == "Test"

Type Checking

Run mypy to check type hints:

python -m mypy src/fabias --ignore-missing-imports

Pull Request Process

  1. Fork the repository and create a feature branch
  2. Write tests for any new functionality
  3. Run the test suite and ensure all tests pass
  4. Run mypy and fix any type errors
  5. Update documentation if needed
  6. Submit a pull request with a clear description

PR Checklist

  • [ ] Tests added for new functionality
  • [ ] All tests passing (pytest)
  • [ ] Type hints added (mypy passes)
  • [ ] Docstrings added for public APIs
  • [ ] CHANGELOG.md updated (if applicable)

API Design Patterns

Pattern 1: Single Retrieval

workspace = fabric.workspace("GENESIS")      # By name
workspace = fabric.workspace(guid)           # By GUID

Pattern 2: List Retrieval

workspaces = fabric.workspaces()             # List all
workspaces = fabric.workspaces(name="DEV")   # Filtered

Pattern 3: Create via Accessor

new_ws = fabric.workspaces.add("Analytics", capacity_id="...")

Lazy Loading

All resource objects should support lazy loading:

class MyResource:
    def __init__(self, client, identifier=None, resource_data=None):
        self._client = client
        self._data_fetched = False

        if resource_data:
            # Pre-populated - no API call needed
            self._populate_from_data(resource_data)
            self._data_fetched = True
        elif GUID.valid(identifier):
            # GUID provided - store, don't fetch yet
            self._id = identifier
        else:
            # Name provided - resolve now
            self._resolve_identifier(identifier)

Questions?

Open an issue on GitHub or reach out to the maintainers.