Skip to content

fix: sync user/company to sandbox before creating test API key#203

Merged
roncodes merged 1 commit intodev-v1.6.41from
fix/sandbox-api-key-fk-constraint
Apr 21, 2026
Merged

fix: sync user/company to sandbox before creating test API key#203
roncodes merged 1 commit intodev-v1.6.41from
fix/sandbox-api-key-fk-constraint

Conversation

@roncodes
Copy link
Copy Markdown
Member

Problem

When a user creates a test/sandbox API key (i.e. the request carries the Access-Console-Sandbox: true header), the SetupFleetbaseSession middleware calls Auth::setSandboxSession($request), which switches database.default to sandbox. The subsequent INSERT into api_credentials therefore targets the sandbox database.

However, the user_uuid and company_uuid values populated by fillSessionAttributes() come from the production session — those rows exist in the production users and companies tables but are not guaranteed to exist in the sandbox database (unless the sandbox:sync Artisan command has been run manually).

The sandbox database enforces the same foreign key constraints as production:

CONSTRAINT `api_credentials_user_uuid_foreign`
  FOREIGN KEY (`user_uuid`) REFERENCES `users` (`uuid`)
  ON DELETE CASCADE ON UPDATE CASCADE

Because the referenced users row is absent in the sandbox DB, MySQL raises:

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row:
a foreign key constraint fails (`fleetbase_production_sandbox`.`api_credentials`,
CONSTRAINT `api_credentials_user_uuid_foreign` …)

Root Cause

The creation path for ApiCredential is entirely generic (via HasApiControllerBehavior::createRecordHasApiModelBehavior::createRecordFromRequeststatic::create()). There is no sandbox-aware pre-create step that ensures the referenced users / companies rows exist in the sandbox DB before the insert is attempted.

Fix

Override createRecord() in ApiCredentialController to detect the sandbox header and, before delegating to the parent generic create path, mirror the current user, company, and company_users pivot row from production into the sandbox DB using an on-demand updateOrInsert upsert.

This is the same approach used by the sandbox:sync Artisan command (SyncSandbox), applied on-demand and scoped only to the three rows needed to satisfy the FK constraints:

  1. users — satisfies api_credentials_user_uuid_foreign
  2. companies — satisfies api_credentials_company_uuid_foreign
  3. company_users — keeps the org-membership pivot consistent in sandbox

Foreign key checks are temporarily disabled during the upsert (identical to sandbox:sync) to avoid ordering issues, then re-enabled before the api_credentials insert proceeds.

Files Changed

  • src/Http/Controllers/Internal/v1/ApiCredentialController.php
    • Added createRecord() override that calls syncCurrentSessionToSandbox() when the sandbox header is present.
    • Added syncCurrentSessionToSandbox() — fetches the current user, company, and pivot from production and upserts them into sandbox.
    • Added upsertModelToSandbox() — reusable helper that mirrors a single model record into the sandbox DB (normalises datetimes, JSON-encodes Json-cast columns, upserts by uuid).

Testing

  1. Ensure the sandbox DB exists and has been migrated (sandbox:migrate).
  2. Log in as any user whose user_uuid / company_uuid are not yet in the sandbox DB.
  3. Open the Developers section and create a new Test API key.
  4. Confirm the key is created without the SQLSTATE[23000] error.
  5. Verify that the corresponding users, companies, and company_users rows now exist in the sandbox DB.

When a test/sandbox API key is created the request carries the
Access-Console-Sandbox header, which causes SetupFleetbaseSession to
switch the default database connection to 'sandbox'. The subsequent
INSERT into api_credentials on the sandbox DB references user_uuid and
company_uuid values that come from the production session. Because those
rows do not necessarily exist in the sandbox database the foreign key
constraint api_credentials_user_uuid_foreign (and the companion
company_uuid constraint) fires and the insert fails with SQLSTATE[23000]
1452.

Fix: override createRecord() in ApiCredentialController to detect the
sandbox header and, before delegating to the generic create path, mirror
the current user, company, and company_user pivot row from production
into the sandbox DB using an on-demand upsert (the same pattern used by
the sandbox:sync Artisan command). Foreign key checks are temporarily
disabled during the upsert to avoid ordering issues, then re-enabled
before the api_credentials insert proceeds.
@roncodes roncodes changed the base branch from main to dev-v1.6.41 April 21, 2026 06:59
@roncodes roncodes merged commit d3aa933 into dev-v1.6.41 Apr 21, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant