clqms-be/AGENTS.md
mahdahar ece101b6d2 Add audit logging plan documentation and update test infrastructure
- Add audit-logging-plan.md with comprehensive logging implementation guide

- Update AGENTS.md with project guidelines

- Refactor test models: remove RefTHoldModel, RefVSetModel, TestDefTechModel

- Update TestDefSiteModel and related migrations

- Update seeder and test data files

- Update API documentation (OpenAPI specs)
2026-02-19 13:20:24 +07:00

329 lines
7.4 KiB
Markdown

# AGENTS.md - Code Guidelines for CLQMS
> **CLQMS (Clinical Laboratory Quality Management System)** - A headless REST API backend for clinical laboratory workflows built with CodeIgniter 4.
---
## Build, Test & Lint Commands
### Running Tests
```bash
# Run all tests
./vendor/bin/phpunit
# Run a specific test file
./vendor/bin/phpunit tests/feature/Patients/PatientCreateTest.php
# Run a specific test method
./vendor/bin/phpunit --filter testCreatePatientSuccess tests/feature/Patients/PatientCreateTest.php
# Run tests with coverage
./vendor/bin/phpunit --coverage-html build/logs/html
# Run tests by suite
./vendor/bin/phpunit --testsuite App
```
### CodeIgniter CLI Commands
```bash
# Run spark commands
php spark <command>
# Generate migration
php spark make:migration <name>
# Generate model
php spark make:model <name>
# Generate controller
php spark make:controller <name>
# Generate seeder
php spark make:seeder <name>
# Run migrations
php spark migrate
# Rollback migrations
php spark migrate:rollback
```
### Composer Commands
```bash
# Install dependencies
composer install
# Run tests via composer
composer test
# Update autoloader
composer dump-autoload
```
---
## Code Style Guidelines
### PHP Standards
- **PHP Version**: 8.1+
- **PSR-4 Autoloading**: `App\` maps to `app/`, `Config\` maps to `app/Config/`
- **PSR-12 Coding Style** (follow where applicable)
### Naming Conventions
| Element | Convention | Example |
|---------|-----------|---------|
| Classes | PascalCase | `PatientController` |
| Methods | camelCase | `createPatient()` |
| Properties | snake_case (legacy) / camelCase (new) | `$patient_id` / `$patientId` |
| Constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
| Tables | snake_case | `patient_visits` |
| Columns | PascalCase (legacy) | `PatientID`, `NameFirst` |
| JSON fields | PascalCase | `"PatientID": "123"` |
### File Organization
```
app/
├── Config/ # Configuration files
├── Controllers/ # API controllers (grouped by feature)
│ ├── Patient/
│ ├── Organization/
│ └── Specimen/
├── Models/ # Data models
├── Filters/ # Request filters (Auth, CORS)
├── Traits/ # Reusable traits
├── Libraries/ # Custom libraries
├── Helpers/ # Helper functions
└── Database/
├── Migrations/
└── Seeds/
```
### Imports & Namespaces
- Always use fully qualified namespaces at the top
- Group imports: Framework first, then App, then external
- Use statements must be in alphabetical order within groups
```php
<?php
namespace App\Controllers;
use CodeIgniter\Controller;
use CodeIgniter\HTTP\ResponseInterface;
use App\Traits\ResponseTrait;
use Firebase\JWT\JWT;
```
### Controller Structure
```php
<?php
namespace App\Controllers;
use App\Traits\ResponseTrait;
class ExampleController extends BaseController
{
use ResponseTrait;
protected $model;
public function __construct()
{
$this->model = new \App\Models\ExampleModel();
}
// GET /example
public function index()
{
// Implementation
}
// POST /example
public function create()
{
// Implementation
}
}
```
### Response Format
All API responses must use the standardized format:
```php
// Success response
return $this->respond([
'status' => 'success',
'message' => 'Operation completed',
'data' => $data
], 200);
// Error response
return $this->respond([
'status' => 'failed',
'message' => 'Error description',
'data' => []
], 400);
```
### Error Handling
- Use try-catch for JWT operations and external calls
- Return structured error responses with appropriate HTTP status codes
- Log errors using CodeIgniter's logging: `log_message('error', $message)`
```php
try {
$decoded = JWT::decode($token, new Key($key, 'HS256'));
} catch (\Firebase\JWT\ExpiredException $e) {
return $this->respond(['status' => 'failed', 'message' => 'Token expired'], 401);
} catch (\Exception $e) {
return $this->respond(['status' => 'failed', 'message' => 'Invalid token'], 401);
}
```
### Database Operations
- Use CodeIgniter's Query Builder or Model methods
- Prefer parameterized queries over raw SQL
- Use transactions for multi-table operations
```php
$this->db->transStart();
// ... database operations
$this->db->transComplete();
if ($this->db->transStatus() === false) {
return $this->respond(['status' => 'error', 'message' => 'Transaction failed'], 500);
}
```
### Testing Guidelines
#### Test Structure
```php
<?php
namespace Tests\Feature\Patients;
use CodeIgniter\Test\FeatureTestTrait;
use CodeIgniter\Test\CIUnitTestCase;
class PatientCreateTest extends CIUnitTestCase
{
use FeatureTestTrait;
protected $endpoint = 'api/patient';
public function testCreatePatientSuccess()
{
$payload = [...];
$result = $this->withBodyFormat('json')
->post($this->endpoint, $payload);
$result->assertStatus(201);
}
}
```
#### Test Naming
- Use descriptive method names: `test<Action><Scenario><ExpectedResult>`
- Example: `testCreatePatientValidationFail`, `testCreatePatientSuccess`
#### Test Status Codes
- 200: Success (GET, PATCH)
- 201: Created (POST)
- 400: Validation Error
- 401: Unauthorized
- 404: Not Found
- 500: Server Error
### API Design
- **Base URL**: `/api/`
- **Authentication**: JWT token via HttpOnly cookie
- **Content-Type**: `application/json`
- **HTTP Methods**:
- `GET` - Read
- `POST` - Create
- `PATCH` - Update (partial)
- `DELETE` - Delete
### Routes Pattern
```php
$routes->group('api/patient', function ($routes) {
$routes->get('/', 'Patient\PatientController::index');
$routes->post('/', 'Patient\PatientController::create');
$routes->get('(:num)', 'Patient\PatientController::show/$1');
$routes->patch('/', 'Patient\PatientController::update');
$routes->delete('/', 'Patient\PatientController::delete');
});
```
### Security Guidelines
- Always use the `auth` filter for protected routes
- Sanitize all user inputs
- Use parameterized queries to prevent SQL injection
- Store JWT secret in `.env` file
- Never commit `.env` files
---
## Project-Specific Conventions
### Legacy Field Naming
Database uses PascalCase for column names (legacy convention):
- `PatientID`, `NameFirst`, `NameLast`
- `Birthdate`, `CreatedAt`, `UpdatedAt`
### ValueSet System
Use the `App\Libraries\Lookups` class for static dropdown values:
```php
use App\Libraries\Lookups;
$genders = Lookups::get('gender');
$options = Lookups::getOptions('gender');
```
### Models
Extend `BaseModel` for automatic UTC date handling:
```php
<?php
namespace App\Models;
class PatientModel extends BaseModel
{
protected $table = 'patients';
protected $primaryKey = 'PatientID';
protected $allowedFields = ['NameFirst', 'NameLast', ...];
}
```
---
## Environment Configuration
### Database (`.env`)
```ini
database.default.hostname = localhost
database.default.database = clqms01
database.default.username = root
database.default.password = adminsakti
database.default.DBDriver = MySQLi
```
### JWT Secret (`.env`)
```ini
JWT_SECRET = '5pandaNdutNdut'
```
---
## Additional Notes
- **API-Only**: No view layer - this is a headless REST API
- **Frontend Agnostic**: Any client can consume these APIs
- **Stateless**: JWT-based authentication per request
- **UTC Dates**: All dates stored in UTC, converted for display
*© 2025 5Panda Team. Engineering Precision in Clinical Diagnostics.*