Skip to content

Migrate to utopia-php/http resources/context API#12220

Open
loks0n wants to merge 6 commits into1.9.xfrom
feat/utopia-http-resources-context
Open

Migrate to utopia-php/http resources/context API#12220
loks0n wants to merge 6 commits into1.9.xfrom
feat/utopia-http-resources-context

Conversation

@loks0n
Copy link
Copy Markdown
Member

@loks0n loks0n commented May 5, 2026

Summary

Adopts the new split DI containers in utopia-php/http (PR: dev-feat-resources-and-context):

  • resources() — long-lived container, shared across every request. Used at boot for config, clients, and shared services.
  • context() — per-request child container, falling through to resources(). The library writes request, response, route, error into it.

Replaces all the removed APIs (getResource(), setResource(), getContainer(), getResources()) across the HTTP entry point, controllers, GraphQL layer, and installer.

Changes

  • Dependency bumps (composer.json/lock):
    • utopia-php/http: dev-feat-resources-and-context as 0.34.25 (aliased to satisfy platform 0.13.2's exact pin)
    • utopia-php/servers: 0.3.*0.4.*
    • utopia-php/queue: 0.17.*0.18.*
    • Pulled-along: cli 0.23.3, platform 0.13.2, database 5.6.0, migration 1.9.7, storage 2.0.2
  • app/http.php:
    • Server(... container:)Server(... resources:)
    • $swooleAdapter->getContainer() (boot/request) → $swooleAdapter->context()
    • $app->getResource(name)$app->context()->get(name) (8 sites)
    • Renamed $registerRequestResources to $bindContext; collapsed $requestContainer intermediate
  • app/controllers/general.php: 3 defensive $utopia->getResource(...)$utopia->context()->get(...) (kept under try/catch since they live inside Http::error())
  • src/Appwrite/Platform/Installer/Server.php: $adapter->getContainer()$adapter->resources() (boot-time)
  • GraphQL (Resolvers.php, Schema.php, Mapper.php): ~22 getResource(...)context()->get(...) inside per-query coroutines; getResources(array) (removed) reimplemented inline via array_map
  • app/init/resources/request.php: collapsed single-return factories to arrow functions (~40 lines saved); no behavior changes

Caveat on the alias

The dev-feat-resources-and-context as 0.34.25 shim makes Composer treat the dev branch as functionally equivalent to released 0.34.25 — needed because utopia-php/platform 0.13.2 exact-pins http at 0.34.25. Once a real platform release targets the new http API, drop the as 0.34.25 and pin to a stable tag.

Test plan

  • composer analyze (PHPStan level 3) — no errors
  • docker compose up -d --force-recreate --build — all 14 workers + main HTTP container start cleanly, databases create on boot
  • docker compose exec appwrite test tests/unit/ — 532 tests pass
  • docker compose exec appwrite test tests/e2e/Services/Health — 22 tests pass (exercises queue resources, DB, storage)
  • curl /v1/health/version200 {"version":"1.9.3"}
  • Validation error path (POST /v1/account/sessions/email w/ short password) → 400 with proper error body, exercising the Http::error() defensive cors/user lookups without crashing
  • CI

🤖 Generated with Claude Code

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 5, 2026

Greptile Summary

  • Mechanical migration of getResource()/getContainer()/getResources() to the new resources()/context()->get() split-container API across the HTTP entry point, GraphQL layer, installer, and error handler. No behavioral changes are intended beyond adopting the new library API.
  • The long-lived $container (resources) correctly sources dbForPlatform, getLogsDB, authorization, and riskyDomains from app/init/resources.php, so the EVENT_START and EVENT_TASK handlers are valid.
  • The __utopia__ coroutine-context key in Swoole.php must match what utopia-php/http's context() reads from Coroutine::getContext(); a mismatch silently breaks all GraphQL resolver coroutines. The test plan has no GraphQL e2e coverage to confirm this.

Confidence Score: 4/5

Safe to merge for all non-GraphQL paths; GraphQL correctness depends on the Swoole coroutine-context key matching the updated library's internal key, which is untested.

All mechanically-replaced call sites are correct and verified by existing tests. The single P1 concern is the utopia key in Swoole.php — it is an intentional coordinated change with the library but lacks a GraphQL e2e test to prove it.

src/Appwrite/Promises/Swoole.php — the REQUEST_CONTAINER_CONTEXT_KEY rename is the only unverified runtime assumption in the PR.

Important Files Changed

Filename Overview
src/Appwrite/Promises/Swoole.php REQUEST_CONTAINER_CONTEXT_KEY renamed from __utopia_http_request_container to utopia — this key must exactly match what utopia-php/http stores in Coroutine::getContext() when context() is called; a mismatch silently breaks context inheritance for all GraphQL resolver coroutines. No GraphQL e2e test in the test plan to validate this.
app/http.php Core HTTP entrypoint migrated from getResource()/getContainer() to context()->get(); EVENT_START and EVENT_TASK handlers now correctly use the long-lived $container (which registers dbForPlatform/getLogsDB in init/resources.php). The 'bus' factory snapshot concern (flagged elsewhere) remains, but is pre-existing.
src/Appwrite/GraphQL/Resolvers.php getResource() calls replaced with context()->get(); removed getResolverContainer() helper; resolve() still mutates context under a lock, which is correct. All resolver coroutines depend on the Swoole coroutine-context key matching the library.
src/Appwrite/GraphQL/Types/Mapper.php getResources(array) replaced with array_map($utopia->context()->get(...), $injections) — semantically equivalent since call_user_func_array uses positional args and array_map preserves input order.
app/init/resources/request.php Parameter renamed from $container to $context; factories collapsed to arrow functions where single-expression; no behavioral changes. Clean refactor.
app/controllers/general.php Three getResource() calls in Http::error() replaced with context()->get(); all wrapped in try/catch as before.
src/Appwrite/Platform/Installer/Server.php getContainer() → resources() for boot-time installer container setup; correct use of long-lived resources.
src/Appwrite/GraphQL/Schema.php Single getResource('response') → context()->get('response') call in Schema::api(); straightforward.

Reviews (5): Last reviewed commit: "Avoid incidental bumps to utopia-php/dat..." | Re-trigger Greptile

Comment thread app/http.php Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

✨ Benchmark results

Comparing 1.9.x (before) to feat/utopia-http-resources-context (after).

Before

Scenario P50 (ms) P95 (ms) Requests RPS
API total 13.68 131.19 185 35.87
Account 23.48 165.92 35 7.49
TablesDB 12.9 20.52 35 9.01
Storage 11.12 58.95 75 18.85
Functions 19.27 28.59 40 10.17

After

Scenario P50 (ms) P95 (ms) Requests RPS
API total 13 134.62 185 36.59
Account 23.51 206.35 35 7.53
TablesDB 13.03 24.83 35 9.07
Storage 9.21 53.2 75 19.13
Functions 17.15 25.34 40 10.34

Delta

Scenario P95 delta (ms)
API total +3.43
Account +40.44
TablesDB +4.31
Storage -5.75
Functions -3.26
Top API waits
API request Max wait (ms)
account.sessions.email.create 648.83
account.password.update 194.27
account.create 149.71

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

🔄 PHP-Retry Summary

Flaky tests detected across commits:

Commit 49ebfca - 2 flaky tests
Test Retries Total Time Details
UsageTest::testStorageStats 1 10.28s Logs
FunctionsCustomServerTest::testErrorPages 1 173ms Logs
Commit 4218528 - 26 flaky tests
Test Retries Total Time Details
UsageTest::testStorageStats 1 10.29s Logs
WebhooksCustomServerTest::testDeleteDeployment 1 14ms Logs
WebhooksCustomServerTest::testDeleteFunction 1 10ms Logs
WebhooksCustomServerTest::testCreateCollection 1 10ms Logs
WebhooksCustomServerTest::testCreateAttributes 1 11ms Logs
WebhooksCustomServerTest::testCreateDocument 1 12ms Logs
WebhooksCustomServerTest::testUpdateDocument 1 10ms Logs
WebhooksCustomServerTest::testDeleteDocument 1 10ms Logs
WebhooksCustomServerTest::testCreateTable 1 10ms Logs
WebhooksCustomServerTest::testCreateColumns 1 10ms Logs
WebhooksCustomServerTest::testCreateRow 1 11ms Logs
WebhooksCustomServerTest::testUpdateRow 1 11ms Logs
WebhooksCustomServerTest::testDeleteRow 1 11ms Logs
WebhooksCustomServerTest::testCreateStorageBucket 1 6ms Logs
WebhooksCustomServerTest::testUpdateStorageBucket 1 10ms Logs
WebhooksCustomServerTest::testCreateBucketFile 1 23ms Logs
WebhooksCustomServerTest::testUpdateBucketFile 1 6ms Logs
WebhooksCustomServerTest::testDeleteBucketFile 1 6ms Logs
WebhooksCustomServerTest::testDeleteStorageBucket 1 13ms Logs
WebhooksCustomServerTest::testCreateTeam 1 7ms Logs
WebhooksCustomServerTest::testUpdateTeam 1 9ms Logs
WebhooksCustomServerTest::testUpdateTeamPrefs 1 10ms Logs
WebhooksCustomServerTest::testDeleteTeam 1 5ms Logs
WebhooksCustomServerTest::testCreateTeamMembership 1 13ms Logs
WebhooksCustomServerTest::testDeleteTeamMembership 1 10ms Logs
WebhooksCustomServerTest::testWebhookAutoDisable 1 25ms Logs
Commit 822c740 - 26 flaky tests
Test Retries Total Time Details
UsageTest::testStorageStats 1 10.33s Logs
WebhooksCustomServerTest::testDeleteDeployment 1 19ms Logs
WebhooksCustomServerTest::testDeleteFunction 1 21ms Logs
WebhooksCustomServerTest::testCreateCollection 1 14ms Logs
WebhooksCustomServerTest::testCreateAttributes 1 12ms Logs
WebhooksCustomServerTest::testCreateDocument 1 9ms Logs
WebhooksCustomServerTest::testUpdateDocument 1 10ms Logs
WebhooksCustomServerTest::testDeleteDocument 1 10ms Logs
WebhooksCustomServerTest::testCreateTable 1 9ms Logs
WebhooksCustomServerTest::testCreateColumns 1 10ms Logs
WebhooksCustomServerTest::testCreateRow 1 9ms Logs
WebhooksCustomServerTest::testUpdateRow 1 9ms Logs
WebhooksCustomServerTest::testDeleteRow 1 9ms Logs
WebhooksCustomServerTest::testCreateStorageBucket 1 5ms Logs
WebhooksCustomServerTest::testUpdateStorageBucket 1 9ms Logs
WebhooksCustomServerTest::testCreateBucketFile 1 9ms Logs
WebhooksCustomServerTest::testUpdateBucketFile 1 5ms Logs
WebhooksCustomServerTest::testDeleteBucketFile 1 5ms Logs
WebhooksCustomServerTest::testDeleteStorageBucket 1 12ms Logs
WebhooksCustomServerTest::testCreateTeam 1 6ms Logs
WebhooksCustomServerTest::testUpdateTeam 1 8ms Logs
WebhooksCustomServerTest::testUpdateTeamPrefs 1 9ms Logs
WebhooksCustomServerTest::testDeleteTeam 1 4ms Logs
WebhooksCustomServerTest::testCreateTeamMembership 1 12ms Logs
WebhooksCustomServerTest::testDeleteTeamMembership 1 8ms Logs
WebhooksCustomServerTest::testWebhookAutoDisable 1 25ms Logs
Commit 11ba023 - 2 flaky tests
Test Retries Total Time Details
UsageTest::testStorageStats 1 10.57s Logs
TeamsConsoleClientTest::testUpdateMembershipWithSession 1 439ms Logs
Commit da1768b - 2 flaky tests
Test Retries Total Time Details
UsageTest::testStorageStats 1 10.43s Logs
FunctionsScheduleTest::testCreateScheduledAtExecution 1 126.47s Logs

Note: Flaky test results are tracked for the last 5 commits

@blacksmith-sh

This comment has been minimized.

loks0n and others added 6 commits May 5, 2026 16:51
Adopts the new split DI containers in utopia-php/http: `resources()` for
boot-time wiring (shared across requests) and `context()` for per-request
state. Replaces the removed `getResource()`/`setResource()`/`getContainer()`
helpers throughout the HTTP entry point, controllers, GraphQL layer, and
installer.

Bumps the dependency chain accordingly: utopia-php/http to the dev branch
(aliased to 0.34.25 to satisfy platform's exact pin), servers 0.4.*,
queue 0.18.*, and pulled-along cli/platform/database upgrades.

Also tightens app/init/resources/request.php by collapsing single-return
factories to arrow functions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both callbacks were doing `new Http($swoole, 'UTC')` purely as a vehicle
to reach DI -- never configuring it, never calling ->run(). Now that the
adapter exposes resources() directly, drop the Http construction and use
the resources container straight. createDatabase() takes Container
instead of Http to match.

Also rename $swooleAdapter to $swoole.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
$swoole->resources() returns the same object we passed in via the Server
constructor's resources: param. No need to round-trip through the adapter
when the container is already in scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Promises\Swoole was already propagating the request container into child
coroutines, but using its own pre-existing key (__utopia_http_request_container)
rather than the new utopia-php/http key (__utopia__). With the keys
mismatched, the propagation was dead code and resolvers spawned from
webonyx coroutines saw an empty context, failing every GraphQL query
with 'Dependency utopia:graphql not found'.

Align the key so child coroutines actually inherit the request's
container (wrapped as a child Container, so request-scoped overrides
in the resolver can't bleed back into the outer request).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the dev-branch alias for utopia-php/http now that 2.0.0-rc1 is
released, and bump utopia-php/platform to 1.0.0-rc1 which targets the
new resources/context API.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The composer update -W cycles to land utopia-php/http and platform also
pulled forward database 5.4.2 -> 5.6.0, storage 2.0.1 -> 2.0.2, and
migration 1.9.6 -> 1.9.7. Pin them back to the 1.9.x baseline -- those
upgrades aren't part of this PR's scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@loks0n loks0n force-pushed the feat/utopia-http-resources-context branch from 749b80a to da1768b Compare May 5, 2026 15:51
class Swoole extends Promise
{
private const REQUEST_CONTAINER_CONTEXT_KEY = '__utopia_http_request_container';
private const REQUEST_CONTAINER_CONTEXT_KEY = '__utopia__';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Coroutine context key must match the library's internal key

Swoole::execute() reads Coroutine::getContext()['__utopia__'] to get the parent request container and writes a child container under the same key in every spawned coroutine. For this to work, Http::context() in utopia-php/http must resolve the per-request container by reading Coroutine::getContext()['__utopia__']. If the library uses a different key, $parentContainer will always be null and every GraphQL resolver coroutine spawned via Swoole will fail to find request-scoped resources — silently returning nothing or throwing at ->get(...) call sites. The test plan doesn't include any GraphQL e2e coverage to exercise this path; adding a basic GraphQL query test would confirm the key is correct.

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