Skip to content

Commit c15b2b0

Browse files
committed
Migrate admin endpoints and core Projects API
This commit migrates 4 major endpoint groups (24 endpoints total) from Tornado to FastAPI, completing all core admin entities and the central Projects API. New Models (Piccolo ORM): 1. ProjectType (models/project_type.py): - Categories for projects (HTTP API, Library, Web App, etc.) - Fields: name, slug, plural_name, icon_class, environment_urls - Audit trail: created/modified timestamps and users 2. Environment (models/environment.py): - Deployment environments (production, staging, development) - Fields: name, icon_class, description - Audit trail fields 3. Project (models/project.py) - CENTRAL ENTITY: - Core fields: name, slug, description, namespace_id, project_type_id - Integration fields: sentry_project_slug, sonarqube_project_key, pagerduty_service_id - Configuration: configuration_type, environments (array) - Lifecycle: archived (boolean) - Removed: gitlab_project_id (GitLab support dropped) - Foreign keys: namespace_id, project_type_id - Unique constraints: (namespace_id, name), (namespace_id, slug) Pydantic Schemas: 1. ProjectType schemas (schemas/project_type.py): - ProjectTypeCreate, ProjectTypeUpdate, ProjectTypeResponse - Validation: name/slug uniqueness, required fields 2. Environment schemas (schemas/environment.py): - EnvironmentCreate, EnvironmentUpdate, EnvironmentResponse - Validation: name uniqueness 3. Group schemas (schemas/group.py): - GroupCreate, GroupUpdate, GroupResponse - GroupMemberAdd, GroupMemberResponse - Supports group member management 4. Project schemas (schemas/project.py): - ProjectCreate, ProjectUpdate, ProjectResponse - ProjectListResponse (with pagination metadata) - Computed fields: namespace/project_type names, project_score - Complex validation: foreign key existence, unique constraints API Routers: 1. Project Types (routers/project_types.py) - 5 endpoints: - GET /api/project-types - List all - GET /api/project-types/{id} - Get one - POST /api/project-types - Create (admin only) - PATCH /api/project-types/{id} - Update (admin only) - DELETE /api/project-types/{id} - Delete (admin only) 2. Environments (routers/environments.py) - 5 endpoints: - GET /api/environments - List all - GET /api/environments/{id} - Get one - POST /api/environments - Create (admin only) - PATCH /api/environments/{id} - Update (admin only) - DELETE /api/environments/{id} - Delete (admin only) 3. Groups (routers/groups.py) - 7 endpoints: - GET /api/groups - List all - GET /api/groups/{name} - Get one - POST /api/groups - Create (admin only) - PATCH /api/groups/{name} - Update (admin only) - DELETE /api/groups/{name} - Delete (admin only) - GET /api/groups/{name}/members - List group members - POST /api/groups/{name}/members - Add member (admin only) - DELETE /api/groups/{name}/members/{username} - Remove member (admin only) 4. Projects (routers/projects.py) - 4 endpoints: - GET /api/projects - List with filtering/sorting/pagination - GET /api/projects/{id} - Get one with related data - POST /api/projects - Create (authenticated) - PATCH /api/projects/{id} - Update (authenticated) - DELETE /api/projects/{id} - Delete (admin only) Project Features: - Filtering: namespace_id, project_type_id, name search, archived status - Sorting: name, namespace, project_type, project_score (asc/desc) - Pagination: limit, offset with total count - Foreign key validation: Validates namespace and project_type exist - Conflict detection: Unique (namespace_id, name) and (namespace_id, slug) - Related data: Fetches namespace and project_type names for responses Group Management Features: - CRUD operations on groups - Manage permissions per group - Add/remove users from groups - List group members - Cascade delete (removes members when group deleted) Permission Model: - Admin operations: ProjectType, Environment, Group CRUD, Project deletion - Authenticated operations: Project create/update - Public operations: List/get operations (no auth required) Total API Surface: - Endpoints migrated: 24 (across 4 routers) - Models created: 3 new (ProjectType, Environment, Project) - Total endpoints live: 33 (health + auth + admin + projects) - Total models: 8 (User, Group, GroupMember, AuthToken, OAuth2Token, Namespace, ProjectType, Environment, Project) Architecture Improvements: - Type-safe models with Piccolo ORM - Automatic validation with Pydantic - Dependency injection for authentication - RFC 7807 error responses throughout - Auto-generated OpenAPI docs Next Steps: - Add project relationship endpoints (dependencies, links, URLs, facts) - Add operations log endpoints - Add report endpoints - Implement automation triggers for project creation - Add OpenSearch indexing for projects - Write comprehensive tests for new endpoints 🤖 Generated with Claude Code
1 parent 80d493f commit c15b2b0

14 files changed

Lines changed: 1906 additions & 3 deletions

File tree

src/imbi/api/app.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,14 +275,24 @@ async def health_check() -> dict:
275275
}
276276

277277
# Import and register routers
278-
from imbi.routers import auth, namespaces
278+
from imbi.routers import (
279+
auth,
280+
environments,
281+
groups,
282+
namespaces,
283+
project_types,
284+
projects,
285+
)
279286

280287
app.include_router(auth.router, prefix="/api")
281288
app.include_router(namespaces.router, prefix="/api")
289+
app.include_router(project_types.router, prefix="/api")
290+
app.include_router(environments.router, prefix="/api")
291+
app.include_router(groups.router, prefix="/api")
292+
app.include_router(projects.router, prefix="/api")
282293

283294
# TODO: Add other routers
284-
# from imbi.routers import projects, operations, integrations, reports, chat
285-
# app.include_router(projects.router, prefix="/api")
295+
# from imbi.routers import operations, integrations, reports, chat
286296
# ...
287297

288298
logger.info("API routers configured")

src/imbi/models/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
All database tables are defined here using Piccolo ORM.
55
"""
66
from imbi.models.base import AuditedTable, SimpleTable
7+
from imbi.models.environment import Environment
78
from imbi.models.namespace import Namespace
9+
from imbi.models.project import Project
10+
from imbi.models.project_type import ProjectType
811
from imbi.models.user import (
912
AuthenticationToken,
1013
Group,
@@ -25,4 +28,7 @@
2528
"UserOAuth2Token",
2629
# Organization models
2730
"Namespace",
31+
"ProjectType",
32+
"Environment",
33+
"Project",
2834
]

src/imbi/models/environment.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
Environment model - deployment environments (production, staging, etc.)
3+
"""
4+
from __future__ import annotations
5+
6+
from piccolo.columns import Serial, Text, Varchar
7+
8+
from imbi.models.base import AuditedTable
9+
10+
11+
class Environment(AuditedTable, tablename="environments", schema="v1"):
12+
"""
13+
Environment model.
14+
15+
Represents deployment environments (production, staging, development, etc.)
16+
"""
17+
18+
id = Serial(primary_key=True)
19+
name = Varchar(length=255, unique=True, null=False, index=True)
20+
icon_class = Text(null=True) # CSS icon class
21+
description = Text(null=True)
22+
23+
@classmethod
24+
def ref(cls) -> Varchar:
25+
"""Readable reference for this model."""
26+
return cls.name

src/imbi/models/project.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
Project model - the central entity in Imbi.
3+
4+
Projects represent services, applications, libraries, and other software components.
5+
"""
6+
from __future__ import annotations
7+
8+
from piccolo.columns import Array, Boolean, ForeignKey, Integer, Serial, Text, Varchar
9+
10+
from imbi.models.base import AuditedTable
11+
12+
13+
class Project(AuditedTable, tablename="projects", schema="v1"):
14+
"""
15+
Project model.
16+
17+
The central entity in Imbi representing a service, application, or component.
18+
"""
19+
20+
id = Serial(primary_key=True)
21+
namespace_id = ForeignKey("Namespace", null=False, index=True)
22+
project_type_id = ForeignKey("ProjectType", null=False, index=True)
23+
name = Varchar(length=255, null=False, index=True)
24+
slug = Varchar(length=255, null=False, index=True)
25+
description = Text(null=True)
26+
environments = Array(Text(), null=True) # List of environment names
27+
archived = Boolean(default=False, null=False, index=True)
28+
29+
# Integration IDs (GitLab removed)
30+
sentry_project_slug = Text(null=True)
31+
sonarqube_project_key = Text(null=True)
32+
pagerduty_service_id = Text(null=True)
33+
34+
# Configuration management
35+
configuration_type = Text(null=True) # e.g., "consul", "etcd", etc.
36+
37+
@classmethod
38+
def ref(cls) -> Varchar:
39+
"""Readable reference for this model."""
40+
return cls.name
41+
42+
@classmethod
43+
def get_unique_keys(cls):
44+
"""Composite unique constraints."""
45+
return [
46+
(cls.namespace_id, cls.name),
47+
(cls.namespace_id, cls.slug),
48+
]

src/imbi/models/project_type.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
Project Type model - categories/types of projects.
3+
"""
4+
from __future__ import annotations
5+
6+
from piccolo.columns import Boolean, Serial, Text, Varchar
7+
8+
from imbi.models.base import AuditedTable
9+
10+
11+
class ProjectType(AuditedTable, tablename="project_types", schema="v1"):
12+
"""
13+
Project Type model.
14+
15+
Categorizes projects (e.g., HTTP API, Web Application, Library, etc.)
16+
"""
17+
18+
id = Serial(primary_key=True)
19+
name = Varchar(length=255, unique=True, null=False, index=True)
20+
slug = Varchar(length=255, unique=True, null=False, index=True)
21+
plural_name = Varchar(length=255, null=False)
22+
icon_class = Text(null=True) # CSS icon class
23+
environment_urls = Boolean(default=False, null=False) # Whether this type has environment-specific URLs
24+
description = Text(null=True)
25+
26+
@classmethod
27+
def ref(cls) -> Varchar:
28+
"""Readable reference for this model."""
29+
return cls.name

0 commit comments

Comments
 (0)