Skip to content

Release v0.1.19 — Phase 19: API Foundation#273

Merged
menvil merged 46 commits into
mainfrom
release/v0.1.19-phase19-api-foundation
Jun 3, 2026
Merged

Release v0.1.19 — Phase 19: API Foundation#273
menvil merged 46 commits into
mainfrom
release/v0.1.19-phase19-api-foundation

Conversation

@menvil

@menvil menvil commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Phase 19 — API Foundation (CONV-291–CONV-312)

Summary

Adds the first working REST API layer for File Converter, enabling programmatic access via API keys.

What's included

  • API v1 route group (/api/v1) with public health endpoint
  • Standard JSON error contract{error: {code, message, details}} shape with stable error codes
  • Domain exception mapping — all domain exceptions map to stable API error codes (402/422/413/429/403/401/404)
  • api_keys table with SHA-256 hash storage (plain token shown once, never stored)
  • ApiKey model with User relation and revocation support
  • API key generation servicefc_ prefixed tokens, SHA-256 hashed
  • Bearer token authentication middleware — updates last_used_at, rejects revoked keys
  • api_access feature gate — Free plan blocked, Pro/Max allowed (via FeatureAccessService)
  • Baseline rate limitingapi-v1 limiter, configurable per plan
  • Business endpoints:
    • GET /api/v1/converters — list all converters
    • GET /api/v1/converters/{source}/{target}/schema — options schema
    • POST /api/v1/files — upload file (uses StoreUploadedFileAction)
    • GET /api/v1/files/{file}/targets — target formats for file
    • POST /api/v1/conversions/estimate — cost estimate (no job created)
    • POST /api/v1/conversions — create conversion (uses CreateConversionJobAction)
    • GET /api/v1/conversions/{conversion} — status
    • GET /api/v1/conversions/{conversion}/download — download result
    • GET /api/v1/credits/balance — credits balance
  • API Resource DTOs — internal fields (stored_path, token_hash, options_json) never exposed
  • Ownership guardsApiOwnershipGuard centralizes file/conversion ownership enforcement
  • Happy path test — full flow from upload through conversion status
  • Final smoke tests — verifies all non-health endpoints require auth, free user blocked

Architecture

  • API controllers are thin adapters — no business logic duplicated
  • Uses same CreateConversionJobAction, StoreUploadedFileAction, CreditLedger, FeatureAccessService as web UI
  • ApiExceptionMapper + bootstrap/app.php render callback handles all exception → stable error code mapping

Test count: 601 total (from 519 before Phase 19)

Pre-merge checklist

  • composer test passes (601 tests)
  • composer lint passes
  • npm run build passes
  • php artisan migrate:fresh --seed passes
  • php artisan route:list --path=api/v1 reviewed — 10 routes, no accidental public endpoints

🤖 Generated with Claude Code


Summary by cubic

Introduces the first REST API for File Converter with API key auth, stable error mapping, per‑plan rate limits, and endpoints for upload and conversions. Adds exists:files,id validation for file_id to return 422 on unknown IDs. Implements Phase 19 per Linear CONV-291–CONV-312.

  • New Features

    • API v1 at /api/v1 with public health check.
    • Bearer token auth via api_keys (SHA‑256 hashed; revoke support; last_used_at updates).
    • api_access feature gate: Free blocked; Pro/Max allowed.
    • Rate limiting api-v1, configurable per plan; 429 uses standard error shape and preserves rate limit headers.
    • Standard JSON error shape with exception mapping (401/403/404/402/422/413/429); validation errors include field details; file_id uses exists:files,id.
    • Ownership guards for files/conversions; auth enforced before model binding on ID routes.
    • Business endpoints: converters list/schema; file upload/targets; conversion estimate/create/status/download; credits balance.
    • Estimate endpoint returns amount, breakdown, and has_enough_credits without creating a job.
    • Download endpoint error codes: not ready (422), not found (404), expired (410).
    • API resources hide internal fields (stored_path, token_hash, options_json); shared request base removes duplicate rules.
  • Migration

    • Run migrations to create api_keys.
    • Provision API keys for Pro/Max users; tokens are shown once.
    • Optional: tune per‑plan rate limits via FeatureAccessService.

Written for commit df6b79a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Versioned REST API (v1) with API key authentication
    • File upload and full conversion workflow: estimate costs, create jobs, track progress, download results
    • Converter discovery and conversion options schema endpoints
    • User credit balance information
    • Plan-based access control and per-user rate limiting

menvil and others added 30 commits June 3, 2026 16:21
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ute-group

CONV-291: Create API v1 route group
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-error-contract

CONV-292: Add standard API error contract
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…xception-mapping

CONV-293: Add API domain exception mapping
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…odel-and-relations

CONV-295: Create ApiKey model and relations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eneration-service

CONV-296: Create API key generation service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ntication-middleware

CONV-297: Create API authentication middleware
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ss-feature-gate

CONV-298: Enforce API access feature gate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…iting-baseline

CONV-299: Add API rate limiting baseline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ndex-endpoint

CONV-300: Add converters index endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hema-endpoint

CONV-301: Add converter schema endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oad-endpoint

CONV-302: Add API file upload endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…formats-endpoint

CONV-303: Add file target formats endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ost-estimate-endpoint

CONV-304: Add conversion cost estimate endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rsion-endpoint

CONV-305: Add create conversion endpoint
menvil and others added 14 commits June 3, 2026 16:40
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tatus-endpoint

CONV-306: Add conversion status endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…esult-download-endpoint

CONV-307: Add conversion result download endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nce-endpoint

CONV-308: Add credits balance endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-response-dtos

CONV-309: Add API resource response DTOs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…p-guards

CONV-310: Add API ownership guards
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…th-feature-test

CONV-311: Add API happy path feature test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on-final-smoke-tests

CONV-312: Add API foundation final smoke tests
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a complete Laravel API v1 with bearer-token authentication, feature-based access control, structured error handling, and full CRUD operations for file conversion workflows. It includes API key management, rate limiting per user plan, ownership validation, and endpoints for uploading files, discovering converters, estimating conversion costs, creating jobs, polling status, and downloading results.

Changes

API v1 Core Implementation

Layer / File(s) Summary
API Key Authentication System
app/Models/ApiKey.php, app/Models/User.php, app/Http/Middleware/AuthenticateApiKey.php, app/Services/Api/ApiKeyGenerator.php, app/Services/Api/GeneratedApiKey.php, database/migrations/2026_06_03_132511_create_api_keys_table.php, database/factories/ApiKeyFactory.php, tests/Feature/Models/ApiKeyTest.php, tests/Feature/Services/Api/ApiKeyGeneratorTest.php
Bearer-token middleware hashes incoming tokens and looks up revoked-safe API keys, updating last_used_at and binding the user to auth. ApiKeyGenerator creates tokens with fc_ prefix, persists SHA-256 hashes, and returns plain tokens. ApiKey model holds user relationships and revocation state; User exposes hasMany relationship.
Error Handling & Response Framework
app/Support/Api/ApiErrorResponseFactory.php, app/Support/Api/ApiExceptionMapper.php, app/Support/Api/MappedApiError.php, tests/Unit/Support/Api/ApiErrorResponseFactoryTest.php, tests/Unit/Support/Api/ApiExceptionMapperTest.php
Central exception-to-error mapping: ApiExceptionMapper converts domain exceptions (insufficient credits, unsupported conversion, invalid options, storage limit) and framework exceptions (auth, authz, throttle, 401/403/404) to structured MappedApiError with code, message, status, and optional details. ApiErrorResponseFactory returns JSON error responses.
Access Control & Rate Limiting
app/Http/Middleware/EnsureApiAccessIsAllowed.php, app/Providers/AppServiceProvider.php, app/Support/Api/ApiOwnershipGuard.php, tests/Feature/Api/V1/ApiAccessFeatureGateTest.php, tests/Feature/Api/V1/ApiRateLimitTest.php, tests/Feature/Api/V1/ApiOwnershipGuardTest.php, tests/Feature/Api/V1/ApiFoundationSmokeTest.php
Feature-gating middleware enforces api_access entitlement via FeatureAccessService, returning 403 if missing. Rate limiter computes per-user minute limits from feature service (default 60), falling back to 30 for unauthenticated. Ownership guard validates user_id on FileRecord and ConversionJob, throwing authorization exceptions on mismatch.
File Upload & Target Format Discovery
app/Http/Controllers/Api/V1/FileController.php, app/Http/Requests/Api/V1/UploadFileRequest.php, app/Http/Resources/Api/V1/FileResource.php, app/Http/Resources/Api/V1/TargetFormatResource.php, tests/Feature/Api/V1/FileUploadEndpointTest.php, tests/Feature/Api/V1/FileTargetsEndpointTest.php
FileController uploads files via StoreUploadedFileAction, returning FileResource with HTTP 201. targets endpoint validates ownership, queries ConverterRegistry by extension, and returns TargetFormatResource collection. Request validates file as required upload; responses omit stored_path and checksum.
Converter Registry & Schema Endpoints
app/Http/Controllers/Api/V1/ConverterController.php, app/Http/Resources/Api/V1/ConverterResource.php, tests/Feature/Api/V1/ConvertersIndexEndpointTest.php, tests/Feature/Api/V1/ConverterSchemaEndpointTest.php
ConverterController::index() returns all converters as ConverterResource collection. schema(source, target) looks up converter by pair, throws UnsupportedConversionException if missing, otherwise returns JSON with source/target and converter options schema. Responses include only key, source_format, target_format, label, description fields.
Conversion Job Workflow
app/Http/Controllers/Api/V1/ConversionController.php, app/Http/Requests/Api/V1/EstimateConversionRequest.php, app/Http/Requests/Api/V1/CreateConversionRequest.php, app/Http/Resources/Api/V1/ConversionResource.php, tests/Feature/Api/V1/ConversionEstimateEndpointTest.php, tests/Feature/Api/V1/CreateConversionEndpointTest.php, tests/Feature/Api/V1/ConversionStatusEndpointTest.php, tests/Feature/Api/V1/ConversionDownloadEndpointTest.php, tests/Feature/Api/V1/ApiResourceShapeTest.php
estimate computes cost via EstimateConversionCostAction, checks credit balance, returns amount/breakdown/sufficiency without creating jobs. store creates queued jobs via CreateConversionJobAction, returns 201 with ConversionResource. show returns current job state with ownership enforcement. download enforces ownership, returns 422 if not completed, 404/410 for missing/expired results, otherwise streams file. Resource omits internal fields (converter_key, options_json, user_id).
Credit Balance Endpoint
app/Http/Controllers/Api/V1/CreditController.php, tests/Feature/Api/V1/CreditBalanceEndpointTest.php
Simple endpoint returning authenticated user's CreditLedger::balance() under data.balance with data.unit = "credits".
Route Wiring & Bootstrap Configuration
routes/api.php, bootstrap/app.php, phpunit.xml
routes/api.php defines /api/v1 prefix with public /health endpoint and middleware-scoped groups for api.key, api.access, and throttle:api-v1. Testing-only /auth-test and /access-test endpoints validate authentication/authorization. bootstrap/app.php registers middleware aliases, configures exception rendering for api/* requests, and wires ApiExceptionMapper. phpunit.xml increases memory limit to 512M.
Integration & Happy Path Tests
tests/Feature/Api/V1/ApiHealthTest.php, tests/Feature/Api/V1/ApiAuthenticationTest.php, tests/Feature/Api/V1/ApiErrorMappingTest.php, tests/Feature/Api/V1/ApiHappyPathTest.php, tests/Feature/Database/ApiKeysSchemaTest.php
Health endpoint returns {status: ok, version: v1}. Authentication tests verify bearer token handling, revocation, and last_used_at updates. Error mapping tests assert exception-to-code mappings. Happy path test covers upload → list targets → estimate → create job → poll status workflow. Schema test verifies api_keys table columns.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • menvil/FileConverter#205: Introduces EstimateConversionCostAction, CreateConversionJobAction, and CreditLedger that the new API v1 conversion endpoints directly depend on for cost calculation and credit enforcement.
  • menvil/FileConverter#59: Introduces ConverterRegistry and converter option schema validation types that the new API endpoints use to resolve converter pairs and validate requested options.
  • menvil/FileConverter#76: Introduces StoreUploadedFileAction and FileRecord that the new API file upload endpoint delegates to for persisting and returning file metadata.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Release v0.1.19 — Phase 19: API Foundation' is fully related to the main change, which implements the complete REST API v1 foundation layer with comprehensive features including authentication, rate limiting, error handling, and business endpoints.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/v0.1.19-phase19-api-foundation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the release Triggers AI code review (CodeRabbit, Cubic) label Jun 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
app/Http/Requests/Api/V1/CreateConversionRequest.php (1)

11-17: ⚡ Quick win

Extract the shared conversion payload rules.

CreateConversionRequest and EstimateConversionRequest currently carry the same validation contract. Pulling these rules into a shared base request/helper will keep the estimate/create endpoints from drifting on file_id, target_format, or options semantics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Http/Requests/Api/V1/CreateConversionRequest.php` around lines 11 - 17,
Extract the shared validation array from CreateConversionRequest::rules() into a
single reusable place (either a base request class like ConversionRequestBase
with a protected conversionRules(): array method or a ConversionValidation trait
exposing conversionRules()). Replace both CreateConversionRequest::rules() and
EstimateConversionRequest::rules() to call and return $this->conversionRules()
(or merge with any specific rules), keeping the same keys
('file_id','target_format','options') and validation definitions; update class
declarations to extend the new base or use the trait so both requests share the
canonical rules.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Http/Controllers/Api/V1/ConversionController.php`:
- Around line 31-56: The controller's estimate() (and similarly download())
currently contains business flow: loading FileRecord, ownership checks via
ownershipGuard, converter lookup via registry->find, option validation, cost
estimation via EstimateConversionCostAction, and balance check—move this
orchestration into a new Action class (e.g., EstimateConversionFlowAction) under
app/Actions; the controller should call that Action with the request user,
file_id, target_format and options and simply return its result as JSON. Ensure
the new Action encapsulates: fetching FileRecord, calling
ownershipGuard->ensureFileOwner, resolving converter with registry->find
(throwing UnsupportedConversionException as needed), validating options via
converter->validateOptions, invoking EstimateConversionCostAction->handle, and
computing has_enough_credits using creditLedger->balance; keep controller
estimate() as a thin adapter that only transforms request to Action input and
formats the Action output to the JSON response.

In `@app/Support/Api/ApiExceptionMapper.php`:
- Around line 23-81: Add a match arm for
Illuminate\Validation\ValidationException in the ApiExceptionMapper::map (the
match (true) block) that returns a MappedApiError with status 422, a succinct
code like 'validation_failed', a message (e.g. $e->getMessage() or 'Validation
failed.'), and populate details using $e->errors(); place this arm alongside the
other instanceof checks (before the default) so validation exceptions are
returned in the API {error:{code,message,details}} envelope.

In `@bootstrap/app.php`:
- Around line 33-50: When handling API exceptions inside the exceptions->render
closure, preserve rate-limit headers from ThrottleRequestsException by detecting
if $e is an instance of ThrottleRequestsException, calling $e->getHeaders(), and
copying those headers onto the outgoing JsonResponse rather than dropping them;
also use the existing ApiErrorResponseFactory (instead of manually constructing
response()->json) to build the response body from the mapped MappedApiError
returned by app(ApiExceptionMapper::class)->map($e), then attach the exception
headers (if any) to that response before returning it so Retry-After and other
throttling headers are preserved.

In `@routes/api.php`:
- Around line 23-39: Move the rate-limit middleware before API key checks and
stop implicit model binding for the sensitive routes: reorder the group
middleware to ['throttle:api-v1', 'api.key', 'api.access'], and for the routes
using implicit models (/files/{file}/targets and /conversions/{conversion} and
/conversions/{conversion}/download) disable SubstituteBindings (e.g.
->withoutMiddleware(\Illuminate\Routing\Middleware\SubstituteBindings::class))
and change those route handlers (FileController::targets,
ConversionController::show, ConversionController::download) to accept an
identifier (e.g. $fileId / $conversionId) or resolve the model inside the
controller after performing api.key/api.access authorization checks so model
resolution/authorization happens post-auth and returns 401/403 instead of 404.

In `@tests/Feature/Api/V1/ApiRateLimitTest.php`:
- Around line 19-32: The test currently hits /api/v1/health which is not behind
the throttle:api-v1 middleware, so the custom RateLimiter::for('api-v1-test',
...) is never exercised; update the test to call a real throttled route (one
defined under the protected/throttle:api-v1 group in routes/api.php) instead of
/api/v1/health or programmatically apply the throttle:api-v1 middleware to the
request so the 'api-v1-test' limiter is invoked; also stop relying on the unused
X-RateLimit-Test-Key header and ensure the request is made with the same user/id
used in RateLimiter::for so the by($user->id) key is hit and you can assert a
429 after exceeding the perMinute(2) limit.

---

Nitpick comments:
In `@app/Http/Requests/Api/V1/CreateConversionRequest.php`:
- Around line 11-17: Extract the shared validation array from
CreateConversionRequest::rules() into a single reusable place (either a base
request class like ConversionRequestBase with a protected conversionRules():
array method or a ConversionValidation trait exposing conversionRules()).
Replace both CreateConversionRequest::rules() and
EstimateConversionRequest::rules() to call and return $this->conversionRules()
(or merge with any specific rules), keeping the same keys
('file_id','target_format','options') and validation definitions; update class
declarations to extend the new base or use the trait so both requests share the
canonical rules.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8273d6e5-62b8-4795-93de-a6649cf5484a

📥 Commits

Reviewing files that changed from the base of the PR and between 72f71d5 and 6411076.

📒 Files selected for processing (50)
  • app/Http/Controllers/Api/V1/ConversionController.php
  • app/Http/Controllers/Api/V1/ConverterController.php
  • app/Http/Controllers/Api/V1/CreditController.php
  • app/Http/Controllers/Api/V1/FileController.php
  • app/Http/Middleware/AuthenticateApiKey.php
  • app/Http/Middleware/EnsureApiAccessIsAllowed.php
  • app/Http/Requests/Api/V1/CreateConversionRequest.php
  • app/Http/Requests/Api/V1/EstimateConversionRequest.php
  • app/Http/Requests/Api/V1/UploadFileRequest.php
  • app/Http/Resources/Api/V1/ConversionResource.php
  • app/Http/Resources/Api/V1/ConverterResource.php
  • app/Http/Resources/Api/V1/FileResource.php
  • app/Http/Resources/Api/V1/TargetFormatResource.php
  • app/Models/ApiKey.php
  • app/Models/User.php
  • app/Providers/AppServiceProvider.php
  • app/Services/Api/ApiKeyGenerator.php
  • app/Services/Api/GeneratedApiKey.php
  • app/Support/Api/ApiErrorResponseFactory.php
  • app/Support/Api/ApiExceptionMapper.php
  • app/Support/Api/ApiOwnershipGuard.php
  • app/Support/Api/MappedApiError.php
  • bootstrap/app.php
  • database/factories/ApiKeyFactory.php
  • database/migrations/2026_06_03_132511_create_api_keys_table.php
  • phpunit.xml
  • routes/api.php
  • tests/Feature/Api/V1/ApiAccessFeatureGateTest.php
  • tests/Feature/Api/V1/ApiAuthenticationTest.php
  • tests/Feature/Api/V1/ApiErrorMappingTest.php
  • tests/Feature/Api/V1/ApiFoundationSmokeTest.php
  • tests/Feature/Api/V1/ApiHappyPathTest.php
  • tests/Feature/Api/V1/ApiHealthTest.php
  • tests/Feature/Api/V1/ApiOwnershipGuardTest.php
  • tests/Feature/Api/V1/ApiRateLimitTest.php
  • tests/Feature/Api/V1/ApiResourceShapeTest.php
  • tests/Feature/Api/V1/ConversionDownloadEndpointTest.php
  • tests/Feature/Api/V1/ConversionEstimateEndpointTest.php
  • tests/Feature/Api/V1/ConversionStatusEndpointTest.php
  • tests/Feature/Api/V1/ConverterSchemaEndpointTest.php
  • tests/Feature/Api/V1/ConvertersIndexEndpointTest.php
  • tests/Feature/Api/V1/CreateConversionEndpointTest.php
  • tests/Feature/Api/V1/CreditBalanceEndpointTest.php
  • tests/Feature/Api/V1/FileTargetsEndpointTest.php
  • tests/Feature/Api/V1/FileUploadEndpointTest.php
  • tests/Feature/Database/ApiKeysSchemaTest.php
  • tests/Feature/Models/ApiKeyTest.php
  • tests/Feature/Services/Api/ApiKeyGeneratorTest.php
  • tests/Unit/Support/Api/ApiErrorResponseFactoryTest.php
  • tests/Unit/Support/Api/ApiExceptionMapperTest.php

Comment thread app/Http/Controllers/Api/V1/ConversionController.php
Comment thread app/Support/Api/ApiExceptionMapper.php
Comment thread bootstrap/app.php Outdated
Comment thread routes/api.php Outdated
Comment thread tests/Feature/Api/V1/ApiRateLimitTest.php Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/Http/Requests/Api/V1/CreateConversionRequest.php">

<violation number="1" location="app/Http/Requests/Api/V1/CreateConversionRequest.php:14">
P1: Missing `exists` validation rule on `file_id`. Without `exists:uploaded_files,id`, a non-existent file ID passes validation and causes a downstream failure (ModelNotFoundException or null reference). The API returns a 404 or 500 instead of a clean 422 validation error with a field-specific message. This is a standard Laravel validation pattern for foreign key references.</violation>
</file>

<file name="app/Http/Requests/Api/V1/EstimateConversionRequest.php">

<violation number="1" location="app/Http/Requests/Api/V1/EstimateConversionRequest.php:14">
P2: `file_id` validated as `integer` but missing `exists:file_records,id` rule — non-existent file IDs pass validation, causing a ModelNotFoundException downstream instead of a clean 422 validation error.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

public function rules(): array
{
return [
'file_id' => ['required', 'integer'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Missing exists validation rule on file_id. Without exists:uploaded_files,id, a non-existent file ID passes validation and causes a downstream failure (ModelNotFoundException or null reference). The API returns a 404 or 500 instead of a clean 422 validation error with a field-specific message. This is a standard Laravel validation pattern for foreign key references.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/Http/Requests/Api/V1/CreateConversionRequest.php, line 14:

<comment>Missing `exists` validation rule on `file_id`. Without `exists:uploaded_files,id`, a non-existent file ID passes validation and causes a downstream failure (ModelNotFoundException or null reference). The API returns a 404 or 500 instead of a clean 422 validation error with a field-specific message. This is a standard Laravel validation pattern for foreign key references.</comment>

<file context>
@@ -0,0 +1,19 @@
+    public function rules(): array
+    {
+        return [
+            'file_id' => ['required', 'integer'],
+            'target_format' => ['required', 'string'],
+            'options' => ['sometimes', 'array'],
</file context>

Comment thread routes/api.php Outdated
public function rules(): array
{
return [
'file_id' => ['required', 'integer'],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: file_id validated as integer but missing exists:file_records,id rule — non-existent file IDs pass validation, causing a ModelNotFoundException downstream instead of a clean 422 validation error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/Http/Requests/Api/V1/EstimateConversionRequest.php, line 14:

<comment>`file_id` validated as `integer` but missing `exists:file_records,id` rule — non-existent file IDs pass validation, causing a ModelNotFoundException downstream instead of a clean 422 validation error.</comment>

<file context>
@@ -0,0 +1,19 @@
+    public function rules(): array
+    {
+        return [
+            'file_id' => ['required', 'integer'],
+            'target_format' => ['required', 'string'],
+            'options' => ['sometimes', 'array'],
</file context>

Comment thread tests/Feature/Api/V1/ApiRateLimitTest.php Outdated
menvil and others added 2 commits June 3, 2026 17:15
…eption, preserve throttle headers, fix model binding auth order, fix rate limit test, deduplicate request rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Non-existent file IDs now fail with 422 validation error instead of
causing a downstream ModelNotFoundException (404) or 500.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@menvil menvil merged commit 50a5f98 into main Jun 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Triggers AI code review (CodeRabbit, Cubic)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant