Skip to content

Conversation

@jsell-rh
Copy link
Collaborator

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

Summary by CodeRabbit

Release Notes

  • New Features

    • Added workspace management capabilities with full CRUD operations (create, retrieve, list, delete) within IAM.
    • Workspace hierarchy support through parent-child relationships.
    • Built-in validation for workspace names and duplicate prevention.
    • Appropriate error handling for restricted operations.
  • Tests

    • Added comprehensive unit test coverage for workspace endpoints and authentication scenarios.

…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 22 commits January 27, 2026 17:41
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…dd route documentation

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…th TDD

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

coderabbitai bot commented Feb 9, 2026

Walkthrough

This pull request introduces a new workspace API feature to the IAM presentation layer. It adds a FastAPI router with four CRUD endpoints (create, get, list, delete) for workspace management. The changes include API request/response models with Pydantic validation, route handlers with authentication and authorization checks, error handling mapped to HTTP status codes, and comprehensive unit test coverage for the endpoints.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 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 accurately and specifically describes the main changes: introduction of workspace API routes and DTO/model objects to the IAM presentation layer.
Docstring Coverage ✅ Passed Docstring coverage is 96.77% 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-145-workspace-api-dto

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

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: 1

🤖 Fix all issues with AI agents
In `@src/api/iam/presentation/workspaces/routes.py`:
- Around line 104-129: Replace the direct constructor call
WorkspaceId(workspace_id) in get_workspace with
WorkspaceId.from_string(workspace_id) so the ID is validated, and add an except
ValueError handler that raises HTTPException(status_code=400, detail=f"Invalid
workspace id: {workspace_id}") to return Bad Request for malformed IDs; keep the
existing HTTPException re-raise and the generic Exception -> 500 behavior
intact.

Comment on lines +104 to +129
async def get_workspace(
workspace_id: str,
current_user: Annotated[CurrentUser, Depends(get_current_user)],
service: Annotated[WorkspaceService, Depends(get_workspace_service)],
) -> WorkspaceResponse:
"""Get workspace by ID."""
try:
workspace_id_obj = WorkspaceId(workspace_id)

workspace = await service.get_workspace(workspace_id_obj)

if workspace is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Workspace {workspace_id} not found",
)

return WorkspaceResponse.from_domain(workspace)

except HTTPException:
raise
except Exception:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve workspace",
)
Copy link

@coderabbitai coderabbitai bot Feb 9, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find WorkspaceId class definition
fd -t f -e py | xargs rg "class WorkspaceId" -A 10

Repository: openshift-hyperfleet/kartograph

Length of output: 669


🏁 Script executed:

# Also check for WorkspaceId imports in the routes file
cat -n src/api/iam/presentation/workspaces/routes.py | head -20

Repository: openshift-hyperfleet/kartograph

Length of output: 857


🏁 Script executed:

# Search for WorkspaceId definition more broadly
rg "class WorkspaceId" -B 2 -A 15

Repository: openshift-hyperfleet/kartograph

Length of output: 1056


🏁 Script executed:

# Find BaseId class definition
rg "class BaseId" -B 2 -A 20

Repository: openshift-hyperfleet/kartograph

Length of output: 1482


🏁 Script executed:

# Get the entire value_objects.py file to see complete BaseId implementation
wc -l src/api/iam/domain/value_objects.py

Repository: openshift-hyperfleet/kartograph

Length of output: 114


🏁 Script executed:

# Read the value_objects.py file to see BaseId __post_init__ and any validation
cat -n src/api/iam/domain/value_objects.py | head -100

Repository: openshift-hyperfleet/kartograph

Length of output: 3086


🏁 Script executed:

# Read the rest of the file to see if there's __post_init__ validation
cat -n src/api/iam/domain/value_objects.py | tail -100

Repository: openshift-hyperfleet/kartograph

Length of output: 2926


🏁 Script executed:

# Check the exact code at lines 104-129 in the routes file
sed -n '104,129p' src/api/iam/presentation/workspaces/routes.py

Repository: openshift-hyperfleet/kartograph

Length of output: 917


🏁 Script executed:

# Check if from_string() is used elsewhere in the codebase with IDs
rg "\.from_string\(" src/api/iam/presentation/ -B 2 -A 2

Repository: openshift-hyperfleet/kartograph

Length of output: 4007


🏁 Script executed:

# Check how other endpoints instantiate ID objects
rg "WorkspaceId\(" src/api/iam/presentation/ -B 2 -A 2

Repository: openshift-hyperfleet/kartograph

Length of output: 1230


🏁 Script executed:

# Check one of the tenant routes to see exactly how from_string is handled
sed -n '1,150p' src/api/iam/presentation/tenants/routes.py | grep -A 8 "from_string"

Repository: openshift-hyperfleet/kartograph

Length of output: 377


Use WorkspaceId.from_string() to validate ID format and return 400 for invalid IDs.

The direct constructor WorkspaceId(workspace_id) accepts any string without validation. Other routes (tenants, groups, API keys) use .from_string() which validates ULID format and raises ValueError for malformed IDs. Replace the direct instantiation with .from_string() and add a dedicated ValueError handler to return 400, consistent with the rest of the codebase.

🛠️ Suggested fix
     try:
-        workspace_id_obj = WorkspaceId(workspace_id)
+        workspace_id_obj = WorkspaceId.from_string(workspace_id)

         workspace = await service.get_workspace(workspace_id_obj)

         if workspace is None:
             raise HTTPException(
                 status_code=status.HTTP_404_NOT_FOUND,
                 detail=f"Workspace {workspace_id} not found",
             )

         return WorkspaceResponse.from_domain(workspace)

+    except ValueError as e:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=f"Invalid workspace ID format: {e}",
+        ) from e
     except HTTPException:
         raise
     except Exception:
🤖 Prompt for AI Agents
In `@src/api/iam/presentation/workspaces/routes.py` around lines 104 - 129,
Replace the direct constructor call WorkspaceId(workspace_id) in get_workspace
with WorkspaceId.from_string(workspace_id) so the ID is validated, and add an
except ValueError handler that raises HTTPException(status_code=400,
detail=f"Invalid workspace id: {workspace_id}") to return Bad Request for
malformed IDs; keep the existing HTTPException re-raise and the generic
Exception -> 500 behavior intact.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

accepting as OK for now.

Copy link

Choose a reason for hiding this comment

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

@jsell-rh, understood. Thank you for the feedback!

@jsell-rh jsell-rh merged commit c5573d3 into main Feb 9, 2026
11 checks passed
@jsell-rh jsell-rh deleted the jsell/feat/AIHCM-145-workspace-api-dto branch February 9, 2026 21:46
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