// Валидация REST API эндпоинтов на соответствие OpenAPI схеме и консистентность параметров. Использовать при реализации эндпоинтов, ревью кода или перед слиянием изменений API.
| name | endpoint-validator |
| description | Валидация REST API эндпоинтов на соответствие OpenAPI схеме и консистентность параметров. Использовать при реализации эндпоинтов, ревью кода или перед слиянием изменений API. |
| allowed-tools | Bash, Read, Grep, Glob |
Validate MikoPBX REST API endpoints for OpenAPI compliance, parameter consistency, and proper implementation.
Use this skill when:
# Find DataStructure and SaveRecordAction for a resource
RESOURCE="Extensions" # Or: Providers, IncomingRoutes, etc.
find src/PBXCoreREST/Lib -name "DataStructure.php" | grep -i "$RESOURCE"
find src/PBXCoreREST/Lib -name "SaveRecordAction.php" | grep -i "$RESOURCE"
Expected locations:
src/PBXCoreREST/Lib/{Resource}/DataStructure.phpsrc/PBXCoreREST/Lib/{Resource}/SaveRecordAction.phpCheck for required structure:
class DataStructure
{
public static function getParameterDefinitions(): array
{
return [
'request' => [
'param_name' => [
'type' => 'string', // Required
'description' => '...', // Required
'example' => 'example_value', // Required
'required' => true, // Required
// Optional constraints:
'maxLength' => 255,
'pattern' => '^regex$',
'enum' => ['value1', 'value2'],
'default' => 'default_value',
],
],
'response' => [
// Similar structure for response fields
],
];
}
}
Quick Checks:
getParameterDefinitions() methodgetParametersConfig() method (legacy)ParameterSanitizationExtractor usageSee: Complete DataStructure Specification
Check for 7-phase pattern compliance:
class SaveRecordAction extends BaseSaveAction
{
public function __invoke(ServerRequestInterface $request): ResponseInterface
{
// PHASE 1: Load DataStructure definitions
$definitions = DataStructure::getParameterDefinitions();
// PHASE 2: Parse request & detect HTTP method
$requestData = $this->parseRequest($request);
$httpMethod = $request->getMethod();
// PHASE 3: Validate ID if present
$id = $requestData['id'] ?? null;
// PHASE 4: Load existing record for PUT/PATCH/DELETE
$existingRecord = null;
if (in_array($httpMethod, ['PUT', 'PATCH', 'DELETE']) && $id) {
$existingRecord = $this->findRecordById($id);
}
// PHASE 5: Sanitize & apply defaults (POST only!)
$sanitized = [];
foreach ($requestDefs as $param => $def) {
if (isset($requestData[$param])) {
$sanitized[$param] = $this->sanitizeValue(...);
} elseif ($httpMethod === 'POST' && isset($def['default'])) {
$sanitized[$param] = $def['default']; // Only on POST!
}
}
// PHASE 6: Validate parameters
$errors = $this->validateParameters($sanitized, $requestDefs, $httpMethod);
// PHASE 7: Business logic & persistence
if ($httpMethod === 'POST') {
return $this->respondSuccess($this->createRecord($sanitized), 201);
}
// ... PUT/PATCH/DELETE handling
}
}
Critical Checks:
See: Complete 7-Phase Pattern Guide
Problem:
// ❌ WRONG - Applies defaults on PATCH too!
$value = $requestData[$param] ?? $def['default'] ?? null;
Why Bad: PATCH is for partial updates. Defaults overwrite existing values.
Fix:
// ✅ CORRECT - Only on POST
if ($httpMethod === 'POST' && !isset($requestData[$param]) && isset($def['default'])) {
$sanitized[$param] = $def['default'];
}
See: All Anti-Patterns
Problem:
// ❌ WRONG - Validates required on all methods
if ($def['required'] && !isset($data[$param])) {
$errors[] = "Required";
}
Why Bad: PATCH allows partial updates. User should update one field without sending all.
Fix:
// ✅ CORRECT - Skip required check for PATCH
if (in_array($httpMethod, ['POST', 'PUT']) && $def['required'] && !isset($data[$param])) {
$errors[] = "Required";
}
Problem: Validation rules in SaveRecordAction instead of DataStructure.
Fix: Move all validation constraints to DataStructure:
// In DataStructure.php
'number' => [
'type' => 'string',
'pattern' => '^\d{2,8}$', // Define here
'maxLength' => 8,
]
// SaveRecordAction uses these constraints automatically
Problems:
getParametersConfig() instead of getParameterDefinitions()ParameterSanitizationExtractor classFix: Remove legacy code, use modern approach.
Problem: Parameters missing min/max, pattern, enum constraints.
Fix: Add constraints to DataStructure:
'email' => [
'type' => 'string',
'format' => 'email', // Add format
'maxLength' => 255, // Add length limit
]
Read through files looking for:
getParameterDefinitions() ✅$httpMethod) ✅CONTAINER_ID=$(docker ps --filter "ancestor=mikopbx/mikopbx" --format "{{.ID}}" | head -1)
# Get auth token
TOKEN="your-bearer-token"
# Test POST (should apply defaults)
curl -X POST "https://mikopbx-php83.localhost:8445/pbxcore/api/v3/extensions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"number": "201", "type": "SIP"}' \
-k -v
# Test PATCH (should NOT apply defaults)
curl -X PATCH "https://mikopbx-php83.localhost:8445/pbxcore/api/v3/extensions/{id}" \
-H "Authorization: Bearer $TOKEN" \
-d '{"number": "202"}' \
-k -v
# Verify: status should remain unchanged, not reset to default
Use the report template to document findings:
Essential tests to verify endpoint behavior:
See: Complete Validation Checklist
Always provide structured compliance reports:
📋 API Endpoint Validation Report
==================================
🎯 Endpoint: POST /pbxcore/api/v3/extensions
📁 Resource: Extensions
📂 Files Analyzed:
✅ DataStructure.php: src/PBXCoreREST/Lib/Extensions/DataStructure.php
✅ SaveRecordAction.php: src/PBXCoreREST/Lib/Extensions/SaveRecordAction.php
---
## 🔍 DataStructure Analysis
✅ Structure: COMPLIANT
⚠️ Missing constraints on 3 parameters:
- user_username: Add pattern validation
- email: Add format => 'email'
DataStructure Score: 85/100 ✅
---
## 🔍 SaveRecordAction Analysis
❌ CRITICAL ISSUES:
1. Defaults applied on PATCH (Line 87)
Impact: Partial updates overwrite existing values
Fix: Wrap default logic in `if ($httpMethod === 'POST')`
2. Missing required validation for PUT (Line 145)
Impact: PUT allows missing required fields
Fix: Change to `if (in_array($httpMethod, ['POST', 'PUT']))`
SaveRecordAction Score: 55/100 ❌
---
## 📊 Overall Compliance Score
Total Score: 72/100 ⚠️ Needs Improvement
---
## ✅ Recommendations (Priority Order)
🔴 CRITICAL (Must Fix):
1. Fix PATCH defaults bug (Line 87) - 15 min
2. Add PUT required validation (Line 145) - 10 min
🟡 HIGH PRIORITY:
3. Add validation constraints to DataStructure - 15 min
---
📝 Action Items:
- [ ] Fix PATCH defaults bug
- [ ] Add PUT required validation
- [ ] Add missing constraints
- [ ] Add regression test for PATCH
- [ ] Run full test suite
Full Template: report-template.md
$httpMethod is actually used# Search entire codebase
find src -name "DataStructure.php" | grep -i "YourResource"
# If not found, check if resource exists
ls -la src/PBXCoreREST/Lib/ | grep -i "YourResource"
# Check if enabled
docker exec $CONTAINER_ID env | grep SCHEMA_VALIDATION
# If not set, add to docker-compose.yml:
# environment:
# SCHEMA_VALIDATION_STRICT: 1
# Check container logs for validation errors
docker exec $CONTAINER_ID tail -100 /storage/usbdisk1/mikopbx/log/php/error.log
Complete Documentation:
Related Skills:
mikopbx-openapi-analyzer - Fetch and analyze OpenAPI specmikopbx-api-test-generator - Generate pytest tests from specmikopbx-docker-restart-tester - Test after container restartValidation is successful when:
| Check | DataStructure | SaveRecordAction |
|---|---|---|
| Has modern structure | getParameterDefinitions() | 7-phase pattern |
| No legacy code | No getParametersConfig() | No ParameterSanitizationExtractor |
| Complete metadata | type, description, example | WHY comments |
| Validation constraints | min/max, pattern, enum | Uses DataStructure rules |
| Defaults handling | Defined in request section | Only applied on POST |
| Required validation | Marked with required: true | POST & PUT only, not PATCH |
# 1. Locate files
RESOURCE="Extensions"
find src/PBXCoreREST/Lib -path "*/$RESOURCE/DataStructure.php"
# Output: src/PBXCoreREST/Lib/Extensions/DataStructure.php
# 2. Check DataStructure structure
grep -n "getParameterDefinitions" src/PBXCoreREST/Lib/Extensions/DataStructure.php
# Output: Line 15: public static function getParameterDefinitions(): array
# 3. Check for legacy methods
grep -n "getParametersConfig\|ParameterSanitizationExtractor" \
src/PBXCoreREST/Lib/Extensions/DataStructure.php
# Output: (none) - Good!
# 4. Check SaveRecordAction for PATCH defaults bug
grep -A 2 "httpMethod.*POST" src/PBXCoreREST/Lib/Extensions/SaveRecordAction.php
# Look for: if ($httpMethod === 'POST' && isset($def['default']))
# 5. Run PATCH test
curl -X PATCH "https://mikopbx-php83.localhost:8445/pbxcore/api/v3/extensions/123" \
-H "Authorization: Bearer $TOKEN" \
-d '{"number": "202"}' -k | jq '.data.status'
# Verify status didn't change to default value