Re-synced controllers, configs, libraries, seeds, and docs with the latest API expectations and response helpers.
153 lines
8.8 KiB
Markdown
Executable File
153 lines
8.8 KiB
Markdown
Executable File
# AGENTS.md - Code Guidelines for CLQMS
|
||
|
||
> **CLQMS (Clinical Laboratory Quality Management System)** – headless REST API backend built on CodeIgniter 4 with a focus on laboratory workflows, JWT authentication, and synchronized OpenAPI documentation.
|
||
|
||
---
|
||
|
||
## Repository Snapshot
|
||
- `app/` holds controllers, models, filters, and traits wired through PSR-4 `App\` namespace.
|
||
- `tests/` relies on CodeIgniter's testing helpers plus Faker for deterministic fixtures.
|
||
- Shared response helpers and ValueSet lookups live under `app/Libraries` and `app/Traits` and should be reused before introducing new helpers.
|
||
- Environment values, secrets, and database credentials live in `.env` but are never committed; treat the file as a reference for defaults.
|
||
|
||
---
|
||
|
||
## Build, Lint & Test
|
||
All commands run from the repository root.
|
||
|
||
```bash
|
||
# Run the entire PHPUnit suite
|
||
./vendor/bin/phpunit
|
||
|
||
# Target a single test file (fast verification)
|
||
./vendor/bin/phpunit tests/feature/Patients/PatientCreateTest.php
|
||
|
||
# Run one test case by method
|
||
./vendor/bin/phpunit --filter testCreatePatientSuccess tests/feature/Patients/PatientCreateTest.php
|
||
|
||
# Generate scaffolding (model, controller, migration)
|
||
php spark make:model <Name>
|
||
php spark make:controller <Name>
|
||
php spark make:migration <name>
|
||
|
||
# Database migrations
|
||
php spark migrate
|
||
php spark migrate:rollback
|
||
|
||
# After OpenAPI edits
|
||
node public/bundle-api-docs.js
|
||
```
|
||
|
||
Use `php spark test --filter <Class>::<method>` when filtering more than one test file is cumbersome.
|
||
|
||
---
|
||
|
||
## Agent Rules Scan
|
||
- No `.cursor/rules/*` or `.cursorrules` directory detected; continue without Cursor-specific constraints.
|
||
- No `.github/copilot-instructions.md` present; Copilot behaviors revert to general GitHub defaults.
|
||
|
||
---
|
||
|
||
## Coding Standards
|
||
|
||
### Language & Formatting
|
||
- PHP 8.1+ is the baseline; enable `declare(strict_types=1)` at the top of new files when practical.
|
||
- Follow PSR-12 for spacing, line length (~120), and brace placement; prefer 4 spaces and avoid tabs.
|
||
- Use short arrays `[]`, and wrap multiline arguments/arrays with one-per-line items.
|
||
- Favor expression statements that return early (guard clauses) and keep nested logic shallow.
|
||
- Keep methods under ~40 lines when possible; extract private helpers for repeated flows.
|
||
|
||
### Naming & Types
|
||
- Classes, controllers, libraries, and traits: PascalCase (e.g., `PatientImportController`).
|
||
- Methods, services, traits: camelCase (`fetchActivePatients`).
|
||
- Properties: camelCase for new code; legacy snake_case may persist but avoid new snake_case unless mirroring legacy columns.
|
||
- Constants: UPPER_SNAKE_CASE.
|
||
- DTOs/array shapes: Use descriptive names (`$patientInput`, `$validatedPayload`).
|
||
- Type hints required for method arguments/returns; use union/nullables (e.g., `?string`) instead of doc-only comments.
|
||
- Prefer PHPDoc only when type inference fails (complex union or array shapes) but still keep method summaries concise.
|
||
|
||
### Imports & Structure
|
||
- Namespace declarations at the very top followed by grouped `use` statements.
|
||
- Import order: Core framework (`CodeIgniter`), then `App\`, then third-party packages (Firebase, Faker, etc.). Keep each group alphabetical.
|
||
- No inline `use` statements inside methods.
|
||
- Keep `use` statements de-duplicated; rely on IDE or `phpcbf` to reorder.
|
||
|
||
### Controller Structure
|
||
- Controllers orchestrate request validation, delegates to services/models, and return `ResponseTrait` responses; avoid direct DB queries here.
|
||
- Inject models/services via constructor when they are reused. When instantiating on the fly, reference FQCN (`new \App\Models\...`).
|
||
- Map HTTP verbs to semantic methods (`index`, `show`, `create`, `update`, `delete`). Keep action methods under 30 lines by delegating heavy lifting to models or libraries.
|
||
- Always respond through `$this->respond()` or `$this->respondCreated()` so JSON structure stays consistent.
|
||
|
||
### Response & Error Handling
|
||
- All responses follow `{ status, message, data }`. `status` values: `success`, `failed`, or `error`.
|
||
- Use `$this->respondCreated()`, `$this->respondNoContent()`, or `$this->respond()` with explicit HTTP codes.
|
||
- Wrap JWT/external calls in try/catch. Log unexpected exceptions with `log_message('error', $e->getMessage())` before responding with a sanitized failure.
|
||
- For validation failures, return HTTP 400 with detailed message; unauthorized access returns 401. Maintain parity with existing tests.
|
||
|
||
### Database & Transactions
|
||
- Use Query Builder or Model methods; enable `use App\Models\BaseModel` which handles UTC conversions.
|
||
- Always call `helper('utc')` when manipulating timestamps.
|
||
- Wrap multi-table changes in `$this->db->transStart()` / `$this->db->transComplete()` and check `transStatus()` to abort if false.
|
||
- Run `checkDbError()` (existing helper) after saves when manual queries are necessary.
|
||
|
||
### Service Helpers & Libraries
|
||
- Encapsulate complex lookups (ValueSet, encryption) inside `app/Libraries` or Traits.
|
||
- Reuse `App\Libraries\Lookups` for consistent label/value translations.
|
||
- Keep shared logic (e.g., response formatting, JWT decoding) inside Traits and import them via `use`.
|
||
|
||
### Testing & Coverage
|
||
- Place feature tests under `tests/Feature`, unit tests under `tests/Unit`.
|
||
- Test class names should follow `ClassNameTest`; methods follow `test<Action><Scenario><Result>` (e.g., `testCreatePatientValidationFail`).
|
||
- Use `FeatureTestTrait` and `CIUnitTestCase` for API tests; prefer `withBodyFormat('json')->post()` flows.
|
||
- Assert status codes: 200 for GET/PATCH, 201 for POST, 400 for validation, 401 for auth, 404 for missing resources, 500 for server errors.
|
||
- Run targeted tests during development, full suite before merging.
|
||
|
||
### Documentation & API Sync
|
||
- Whenever a controller or route changes, update `public/paths/<resource>.yaml` and matching `public/components/schemas`. Add tags or schema refs in `public/api-docs.yaml`.
|
||
- After editing OpenAPI files, regenerate the bundled docs with `node public/bundle-api-docs.js`. Check `public/api-docs.bundled.yaml` into version control.
|
||
- Keep the controller-to-YAML mapping table updated to reflect new resources.
|
||
|
||
### Routing Conventions
|
||
- Keep route definitions grouped inside `$routes->group('api/<resource>')` blocks in `app/Config/Routes.php`.
|
||
- Prefer nested controllers (e.g., `Patient\PatientController`) for domain partitioning.
|
||
- Use RESTful verbs (GET: index/show, POST: create, PATCH: update, DELETE: delete) to keep behavior predictable.
|
||
- Document side effects (snapshots, audit logs) directly in the corresponding OpenAPI `paths` file.
|
||
|
||
### Environment & Secrets
|
||
- Use `.env` as the source of truth for database/jwt settings. Do not commit production credentials.
|
||
- Sample values are provided in `.env`; copy to `.env.local` or CI secrets with overrides.
|
||
- `JWT_SECRET` must be treated as sensitive and rotated via environment updates only.
|
||
|
||
### Workflows & Misc
|
||
- Use `php spark migrate`/`migrate:rollback` for schema changes.
|
||
- For seeding or test fixtures, prefer factories (Faker) seeded in `tests/Support` when available.
|
||
- Document major changes in `issues.md` or dedicated feature docs under `docs/` before merging.
|
||
|
||
### Security & Filters
|
||
- Apply the `auth` filter to every protected route, and keep `ApiKey` or other custom filters consolidated under `app/Filters`.
|
||
- Sanitize user inputs via `filter_var`, `esc()` helpers, or validated entities before they hit the database.
|
||
- Always use parameterized queries/Model `save()` methods to prevent SQL injection, especially with legacy PascalCase columns.
|
||
- Respond 401 for missing tokens, 403 when permissions fail, and log sanitized details for ops debugging.
|
||
|
||
### Legacy Field Naming & ValueSets
|
||
- Databases use PascalCase columns such as `PatientID`, `NameFirst`, `CreatedAt`. Keep migration checks aware of these names.
|
||
- ValueSet lookups centralize label translation: `Lookups::get('gender')`, `Lookups::getLabel('gender', '1')`, `Lookups::transformLabels($payload, ['Sex' => 'gender'])`.
|
||
- Prefer `App\Libraries\Lookups` or `app/Traits/ValueSetTrait` to avoid ad-hoc mappings.
|
||
|
||
### Nested Data Handling
|
||
- For entities that carry related collections (`PatIdt`, `PatCom`, `PatAtt`), extract nested arrays before filtering and validating.
|
||
- Use transactions whenever multi-table inserts/updates occur so orphan rows are avoided.
|
||
- Guard against empty/null arrays by normalizing to `[]` before iterating.
|
||
|
||
### Observability & Logging
|
||
- Use `log_message('info', ...)` for happy-path checkpoints and `'error'` for catch-all failures.
|
||
- Avoid leaking sensitive values (tokens, secrets) in logs; log IDs or hash digests instead.
|
||
- Keep `writable/logs` clean by rotating or pruning stale log files with automation outside the repo.
|
||
|
||
---
|
||
|
||
## Final Notes for Agents
|
||
- This repo has no UI layer; focus exclusively on REST interactions.
|
||
- Always pull `public/api-docs.bundled.yaml` in after running `node public/bundle-api-docs.js` so downstream services see the latest contract.
|
||
- When in doubt, align with existing controller traits and response helpers to avoid duplicating logic.
|