Skip to content

feat(api.iam): AIHCM-143 workspace repo infra#202

Merged
jsell-rh merged 68 commits intomainfrom
jsell/feat/AIHCM-143-workspace-repo-infra
Feb 6, 2026
Merged

feat(api.iam): AIHCM-143 workspace repo infra#202
jsell-rh merged 68 commits intomainfrom
jsell/feat/AIHCM-143-workspace-repo-infra

Conversation

@jsell-rh
Copy link
Collaborator

@jsell-rh jsell-rh commented Feb 6, 2026

Summary by CodeRabbit

  • New Features
    • Workspace management: persistent workspace entities, repository with CRUD and outbox event support, and observability probes.
    • Configurable default workspace name (falls back to tenant name).
  • Migrations
    • Added migration to create workspaces table and a migration to change group tenant FK to RESTRICT.
  • Tests
    • Added comprehensive unit test suite covering workspace repository behavior and event emission.

…lembic

- Add SQLAlchemy 2.0 with asyncpg for async database operations
- Add Alembic for schema migrations
- Add python-ulid for ULID support instead of UUID
- Create read/write engine separation with connection pooling
- Create FastAPI dependency injection for database sessions
- Create SQLAlchemy declarative base with timestamp mixin
- Initialize Alembic with async migration support
- Create initial migration for teams table (ULID primary key)
- Add comprehensive unit tests for engines and dependencies
- Configure Alembic to use settings module for database URL
- Enable ruff post-write hook for migration formatting

Refs: AIHCM-121
- Add authzed library for SpiceDB integration
- Add python-ulid for ULID support
- Create ResourceType, RelationType, Permission enums (using Group not Team)
- Create AuthorizationProvider protocol for swappable implementations
- Implement SpiceDBClient with async methods for relationships and permissions
- Create SpiceDB schema (.zed) with Tenant→Workspace→Group hierarchy
- Create AuthorizationProbe for domain-oriented observability
- Move ObservationContext to shared_kernel (fix architectural boundary)
- Add 35 unit tests for types and probes
- All 410 tests passing

Refs: AIHCM-122
Resolved conflicts in authorization files by accepting remote changes:
- shared_kernel/authorization/types.py (docstring fix)
- shared_kernel/authorization/spicedb/client.py (_parse_reference helper)
jsell-rh and others added 20 commits January 27, 2026 15:52
…nstraints

- Fix groups FK from CASCADE to RESTRICT to force application-level
  cascading and ensure domain events are emitted for SpiceDB cleanup
- Create workspaces table with RESTRICT FK constraints and partial
  unique index for root workspace per tenant
- Add WorkspaceModel with self-referential parent/child relationships
- Define IWorkspaceRepository protocol with save, get_by_id, get_by_name,
  get_root_workspace, list_by_tenant, and delete methods
- Implement WorkspaceRepository following TenantRepository pattern with
  transactional outbox for domain events
- Add WorkspaceRepositoryProbe for domain-oriented observability
- Add default_workspace_name setting to IAMSettings (defaults to None,
  falls back to tenant name)
- Wire workspace repository into FastAPI dependency injection
- Add 27 unit tests covering all repository methods, protocol compliance,
  outbox events, observability probes, and edge cases

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
@jsell-rh jsell-rh self-assigned this Feb 6, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

Walkthrough

This PR adds workspace support to the IAM bounded context. It introduces new SQLAlchemy ORM model files (WorkspaceModel, TenantModel, GroupModel, UserModel, APIKeyModel) and a models package export; an Alembic migration to create the workspaces table and a migration to change groups FK behavior; a PostgreSQL-backed WorkspaceRepository that uses a transactional outbox to persist domain events; observability probes for workspace repository operations; a FastAPI dependency factory get_workspace_repository; an IAM settings field default_workspace_name; and comprehensive unit tests for the WorkspaceRepository.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant WorkspaceRepo as WorkspaceRepository
participant DB as AsyncSession (Postgres)
participant Outbox as OutboxRepository
participant Probe as WorkspaceRepositoryProbe
participant Serializer as IAMEventSerializer

Client->>WorkspaceRepo: save(workspace)
WorkspaceRepo->>DB: upsert WorkspaceModel (within transaction)
WorkspaceRepo->>Serializer: serialize(workspace.events)
WorkspaceRepo->>Outbox: append(serialized events) (same transaction)
WorkspaceRepo->>DB: flush/commit
DB-->>WorkspaceRepo: commit success
WorkspaceRepo->>Probe: workspace_saved(workspace.id, tenant_id)
WorkspaceRepo-->>Client: return

mermaid
sequenceDiagram
participant Client
participant WorkspaceRepo as WorkspaceRepository
participant DB as AsyncSession (Postgres)
participant Probe as WorkspaceRepositoryProbe

Client->>WorkspaceRepo: get_by_id(id)
WorkspaceRepo->>DB: query WorkspaceModel by id
DB-->>WorkspaceRepo: model | none
alt model found
WorkspaceRepo->>Probe: workspace_retrieved(id)
WorkspaceRepo-->>Client: Workspace domain object
else not found
WorkspaceRepo->>Probe: workspace_not_found(id=id)
WorkspaceRepo-->>Client: None
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: introducing workspace repository infrastructure for the IAM module, with appropriate conventional commit format and ticket reference.
Docstring Coverage ✅ Passed Docstring coverage is 98.67% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jsell/feat/AIHCM-143-workspace-repo-infra

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-02-06 21:37 UTC

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/api/iam/infrastructure/models/workspace.py`:
- Around line 49-54: WorkspaceModel currently redefines created_at and
updated_at, which strips TimestampMixin's insert_default and onupdate behavior;
remove the created_at and updated_at mapped_column overrides from WorkspaceModel
so it inherits the mixin's timestamp defaults and callbacks (refer to
WorkspaceModel, created_at, updated_at, and
TimestampMixin/insert_default/onupdate) to restore consistent automatic
timestamp population like TenantModel and UserModel.

In `@src/api/iam/infrastructure/observability/repository_probe.py`:
- Around line 314-318: Update the module-level docstring to mention workspaces
in addition to groups, users, and tenants so it accurately reflects the new
WorkspaceRepositoryProbe; revise the top-level description to state that the
module covers group/user/tenant/workspace repository operations and that it
records domain events during workspace persistence operations (aligning with the
existing WorkspaceRepositoryProbe class and related probe implementations).
🧹 Nitpick comments (2)
src/api/tests/unit/iam/infrastructure/test_workspace_repository.py (1)

652-664: Consider avoiding direct access to private attribute _serializer.

The test accesses repository._serializer (line 664), which is an implementation detail. This couples the test to the internal structure of WorkspaceRepository.

Consider verifying the default serializer behavior indirectly, such as by checking that serialization produces expected output when saving a workspace, rather than inspecting the private attribute.

♻️ Alternative approach
     def test_uses_default_serializer_when_not_injected(
-        self, mock_session, mock_outbox, mock_probe
+        self, mock_session, mock_outbox, mock_probe, tenant_id
     ):
-        """Should create default serializer when not injected."""
-        from iam.infrastructure.outbox import IAMEventSerializer
-
+        """Should use default serializer behavior when not injected."""
         repository = WorkspaceRepository(
             session=mock_session,
             outbox=mock_outbox,
             probe=mock_probe,
         )
 
-        assert isinstance(repository._serializer, IAMEventSerializer)
+        workspace = Workspace.create_root(name="Test", tenant_id=tenant_id)
+
+        mock_result = MagicMock()
+        mock_result.scalar_one_or_none.return_value = None
+        mock_session.execute.return_value = mock_result
+
+        # Save should succeed with default serializer
+        # (would fail if serializer was None or broken)
+        await repository.save(workspace)
+        mock_outbox.append.assert_called_once()
src/api/iam/infrastructure/workspace_repository.py (1)

144-189: Consider probe consistency for not-found cases.

get_by_id calls probe.workspace_not_found() when the workspace doesn't exist, but get_by_name and get_root_workspace silently return None. This inconsistency may be intentional (these methods are often used for existence checks where not-found is expected), but worth confirming the observability requirements.

jsell-rh and others added 2 commits February 6, 2026 16:16
…e consistency

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…m:openshift-hyperfleet/kartograph into jsell/feat/AIHCM-143-workspace-repo-infra
@jsell-rh jsell-rh merged commit 9502ff5 into main Feb 6, 2026
12 checks passed
@jsell-rh jsell-rh deleted the jsell/feat/AIHCM-143-workspace-repo-infra branch February 6, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant