Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,23 @@ myservice_router = Router(myservice_routes)
app.mount("/api/env/{env_id}/services/myservice", myservice_router)
```

**5. Write a seed script** in `backend/utils/seed_myservice_template.py` that:
**5. Register the service with the platform API** in `src/platform/api/models.py`.
Add it to the `Service` enum so `initEnv` (and other platform endpoints that take
a `templateService`) will accept the new value:

```python
class Service(str, Enum):
slack = "slack"
linear = "linear"
calendar = "calendar"
box = "box"
myservice = "myservice" # ← add this
```

If you skip this step, `POST /api/platform/initEnv` returns a Pydantic enum
validation error even though the template rows exist and the router is mounted.

**6. Write a seed script** in `backend/utils/seed_myservice_template.py` that:
- Creates the PostgreSQL schema (e.g. `myservice_default`)
- Uses `Base.metadata.create_all()` to create tables
- Inserts seed data from a JSON file
Expand All @@ -419,10 +435,10 @@ app.mount("/api/env/{env_id}/services/myservice", myservice_router)
Follow `seed_slack_template.py` as a reference — it shows the full pattern including
schema creation, table ordering, and template registration.

**6. Add seed data** in `examples/myservice/seeds/myservice_default.json` and copy to
**7. Add seed data** in `examples/myservice/seeds/myservice_default.json` and copy to
`backend/seeds/myservice/` for Docker builds.

**7. Register the seed script** in the Docker startup command in `ops/docker-compose.yml`:
**8. Register the seed script** in the Docker startup command in `ops/docker-compose.yml`:

```yaml
command: >
Expand Down
16 changes: 9 additions & 7 deletions backend/src/platform/api/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from contextlib import asynccontextmanager

from sqlalchemy import create_engine
from sqlalchemy.pool import NullPool
from src.platform.isolationEngine.session import SessionManager
Expand Down Expand Up @@ -35,7 +37,13 @@


def create_app():
app = Starlette()
@asynccontextmanager
async def lifespan(app_):
yield
if getattr(app_.state, "replication_service", None):
app_.state.replication_service.stop()

app = Starlette(lifespan=lifespan)
db_url = environ["DATABASE_URL"]

# Use NullPool when using Neon's PgBouncer (-pooler) to avoid double pooling
Expand Down Expand Up @@ -143,12 +151,6 @@ def create_app():

app.mount("/api/env/{env_id}/services/linear", linear_graphql)

@app.on_event("shutdown")
async def shutdown_event():
# Stop replication service if running (it's on-demand now)
if app.state.replication_service:
app.state.replication_service.stop()

return app


Expand Down
1 change: 1 addition & 0 deletions backend/src/platform/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Service(str, Enum):
linear = "linear"
calendar = "calendar"
box = "box"
github = "github"


class Visibility(str, Enum):
Expand Down
Loading