Skip to content

Commit e66c970

Browse files
authored
feat: OpenAPI documentation for all Slim API routes (#8113)
## Summary - Adds OpenAPI 3.0 annotations to all 36 Slim route files covering ~100 endpoints across `api/routes/`, `admin/routes/api/`, `kiosk/routes/api/`, and `plugins/routes/api/` - Adds `swagger-php ^4.10` + `doctrine/annotations ^2.0` as dev dependencies for spec generation - Adds `build:openapi` npm script that self-installs dev deps, generates both `public-api.yaml` and `private-api.yaml`, and fits into the existing `build:php` pipeline - Adds two GitHub Actions workflows: - `publish-openapi.yml` — triggers on master push when API files change; generates specs, commits YAML, and fires `repository_dispatch` to trigger docs site rebuild - `validate-openapi.yml` — triggers on branch push / PR when API files change; generates specs and uploads as artifact for review - Adds global OpenAPI info files at `src/api/openapi/` - Generated YAML specs committed to `openapi/` ## Two specs | Spec | Routes | Auth | |---|---|---| | `public-api.yaml` | `/public/*` | None | | `private-api.yaml` | All other routes | `x-api-key` header | ## Test plan - [x] `npm run build:openapi` generates both YAML specs cleanly - [x] `npm run build` completes without errors (openapi runs before `--no-dev` strip) - [ ] CI `validate-openapi.yml` passes on this PR - [ ] Docs site rebuilds after merge via `publish-openapi.yml` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents a583996 + 5c5beed commit e66c970

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+8008
-3751
lines changed

.agents/skills/churchcrm/api-development.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,89 @@ const data = await fetchAPIJSON<AvatarInfo>('person/123/avatar');
281281

282282
**Do NOT create API endpoints** if a service method is only called from a legacy page - call the service directly instead.
283283

284+
## OpenAPI Documentation (REQUIRED for all API changes)
285+
286+
ChurchCRM uses `zircote/swagger-php` 4.x to generate OpenAPI 3.0 specs from DocBlock annotations. The generated specs power the public API reference at `docs.churchcrm.io`.
287+
288+
**When adding or updating any API endpoint, you MUST add/update the `@OA\*` annotation.**
289+
290+
### Annotation placement rules
291+
292+
**Named functions** — DocBlock immediately above the `function` definition:
293+
```php
294+
/**
295+
* @OA\Get(
296+
* path="/events",
297+
* operationId="getAllEvents",
298+
* summary="List all events",
299+
* tags={"Calendar"},
300+
* security={{"ApiKeyAuth":{}}},
301+
* @OA\Response(response=200, description="Event list", @OA\JsonContent(...)),
302+
* @OA\Response(response=401, description="Unauthorized")
303+
* )
304+
*/
305+
function getAllEvents(Request $request, Response $response, array $args): Response {
306+
```
307+
308+
**Closures / arrow functions** — standalone DocBlock immediately above the `$group->get(...)` call:
309+
```php
310+
/**
311+
* @OA\Get(
312+
* path="/persons/search/{query}",
313+
* operationId="searchPersons",
314+
* summary="Search persons by name or email",
315+
* tags={"People"},
316+
* security={{"ApiKeyAuth":{}}},
317+
* @OA\Parameter(name="query", in="path", required=true, @OA\Schema(type="string")),
318+
* @OA\Response(response=200, description="Matching persons (max 15)")
319+
* )
320+
*/
321+
$group->get('/search/{query}', function (Request $request, Response $response, array $args): Response {
322+
```
323+
324+
### Auth security by middleware
325+
326+
| Middleware | OpenAPI security | Extra responses |
327+
|---|---|---|
328+
| None (`/public/*`) | omit `security` key ||
329+
| `AuthMiddleware` (global) | `security={{"ApiKeyAuth":{}}}` | 401 |
330+
| Role middleware (Finance, Admin, etc.) | `security={{"ApiKeyAuth":{}}}` | 401 + 403 |
331+
332+
### Tags
333+
334+
Public spec: `Utility`, `Auth`, `Registration`, `Calendar`, `Lookups`
335+
336+
Private spec: `Calendar`, `People`, `Families`, `Groups`, `Properties`, `Finance`, `Users`, `2FA`, `System`, `Admin`, `Cart`, `Search`, `Map`
337+
338+
### Regenerating the spec
339+
340+
After annotating, run from `CRM/src/`:
341+
```bash
342+
composer run openapi-public # → CRM/openapi/public-api.yaml
343+
composer run openapi-private # → CRM/openapi/private-api.yaml
344+
```
345+
346+
Commit the updated YAML files to the CRM repo. The rest is automated:
347+
348+
- **On PR/branch push**: `validate-openapi.yml` generates both specs and uploads them as artifacts for review.
349+
- **On merge to master**: `publish-openapi.yml` regenerates specs, commits any changes to `CRM/openapi/`, then dispatches a `repository_dispatch` event to `docs.churchcrm.io`, which pulls the latest YAMLs and regenerates MDX automatically.
350+
351+
To manually sync the docs site (e.g., during local dev):
352+
```bash
353+
cp CRM/openapi/public-api.yaml docs.churchcrm.io/openapi/
354+
cp CRM/openapi/private-api.yaml docs.churchcrm.io/openapi/
355+
cd docs.churchcrm.io && npm run regen
356+
```
357+
358+
### Global annotations / tags
359+
360+
Defined in `src/api/openapi/openapi-public-info.php` and `src/api/openapi/openapi-private-info.php`. If you add a new tag, add it there first.
361+
284362
## Files
285363

286364
**API Routes:** `src/api/routes/`, `src/admin/routes/api/`
287365
**Utilities:** `src/ChurchCRM/Slim/SlimUtils.php`
288366
**Middleware:** `src/ChurchCRM/Slim/Middleware/`
367+
**OpenAPI info:** `src/api/openapi/openapi-public-info.php`, `src/api/openapi/openapi-private-info.php`
368+
**Generated specs:** `CRM/openapi/public-api.yaml`, `CRM/openapi/private-api.yaml`
369+
**Docs site:** `docs.churchcrm.io/openapi/`, `docs.churchcrm.io/docs/public-api/`, `docs.churchcrm.io/docs/private-api/`
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Publish OpenAPI Specs
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths:
8+
- 'src/api/routes/**'
9+
- 'src/admin/routes/api/**'
10+
- 'src/api/openapi/**'
11+
- 'src/kiosk/routes/api/**'
12+
- 'src/plugins/routes/api/**'
13+
14+
jobs:
15+
generate-and-publish:
16+
name: Generate specs and trigger docs rebuild
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout CRM
21+
uses: actions/checkout@v4
22+
23+
- name: Setup PHP 8.4
24+
uses: shivammathur/setup-php@v2
25+
with:
26+
php-version: '8.4'
27+
extensions: mbstring, pdo, zip
28+
29+
- name: Cache Composer packages
30+
uses: actions/cache@v4
31+
with:
32+
path: src/vendor
33+
key: composer-${{ hashFiles('src/composer.lock') }}
34+
restore-keys: composer-
35+
36+
- name: Install Composer dependencies
37+
run: cd src && composer install --no-interaction --prefer-dist
38+
39+
- name: Generate Public API spec
40+
run: cd src && composer run openapi-public
41+
42+
- name: Generate Private API spec
43+
run: cd src && composer run openapi-private
44+
45+
- name: Commit updated specs
46+
uses: stefanzweifel/git-auto-commit-action@v5
47+
with:
48+
commit_message: 'chore: update OpenAPI specs [skip ci]'
49+
file_pattern: 'openapi/*.yaml'
50+
commit_user_name: 'github-actions[bot]'
51+
commit_user_email: 'github-actions[bot]@users.noreply.github.com'
52+
53+
- name: Trigger docs.churchcrm.io rebuild
54+
uses: peter-evans/repository-dispatch@v3
55+
with:
56+
token: ${{ secrets.DOCS_DEPLOY_TOKEN }}
57+
repository: ChurchCRM/docs.churchcrm.io
58+
event-type: openapi-updated
59+
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Validate OpenAPI Specs
2+
3+
on:
4+
push:
5+
branches-ignore:
6+
- master
7+
paths:
8+
- 'src/api/routes/**'
9+
- 'src/admin/routes/api/**'
10+
- 'src/api/openapi/**'
11+
- 'src/kiosk/routes/api/**'
12+
- 'src/plugins/routes/api/**'
13+
pull_request:
14+
paths:
15+
- 'src/api/routes/**'
16+
- 'src/admin/routes/api/**'
17+
- 'src/api/openapi/**'
18+
- 'src/kiosk/routes/api/**'
19+
- 'src/plugins/routes/api/**'
20+
21+
jobs:
22+
validate:
23+
name: Generate and validate OpenAPI specs
24+
runs-on: ubuntu-latest
25+
26+
steps:
27+
- name: Checkout CRM
28+
uses: actions/checkout@v4
29+
30+
- name: Setup PHP 8.4
31+
uses: shivammathur/setup-php@v2
32+
with:
33+
php-version: '8.4'
34+
extensions: mbstring, pdo, zip
35+
36+
- name: Cache Composer packages
37+
uses: actions/cache@v4
38+
with:
39+
path: src/vendor
40+
key: composer-dev-${{ hashFiles('src/composer.lock') }}
41+
restore-keys: composer-dev-
42+
43+
- name: Install Composer dependencies (including dev)
44+
run: cd src && composer install --no-interaction --prefer-dist
45+
46+
- name: Generate Public API spec
47+
run: cd src && composer run openapi-public
48+
49+
- name: Generate Private API spec
50+
run: cd src && composer run openapi-private
51+
52+
- name: Upload generated specs as artifacts
53+
uses: actions/upload-artifact@v4
54+
with:
55+
name: openapi-specs-${{ github.run_id }}
56+
path: openapi/*.yaml
57+
retention-days: 7

openapi/.gitkeep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Generated OpenAPI specs - do not edit manually

0 commit comments

Comments
 (0)