API platform: lunarphp/api kernel, admin auth and proving surfaces (spec 0077) - #2714
Draft
glennjacobs wants to merge 6 commits into
Draft
API platform: lunarphp/api kernel, admin auth and proving surfaces (spec 0077)#2714glennjacobs wants to merge 6 commits into
glennjacobs wants to merge 6 commits into
Conversation
…0077) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
New lunarphp/api package: - Kernel: Resource, ResourceExtension, Field, Embed, Filter, Sort, SerializationContext; per-surface registry with resource(), extend() and replace(); JSON:API-style query grammar rejecting anything unregistered with a 422 that lists the allowed values; data/meta/links envelope; one error renderer for the JSON:API error object; _schema endpoint and lunar:api:schema. - Storefront v1: X-Lunar-Channel / X-Lunar-Currency / Accept-Language context middleware, signed stateless X-Lunar-Cart token, CustomerResolver contract; products, brands, collections, collection groups, GET /cart, POST /cart/lines, GET /me behind the host guard. - Admin v1: Lunar-owned ApiKey model and migration, lunar-api-key guard driver and lunar-api guard, abilities gate reusing the staff permission vocabulary, activity-log attribution, lunar:api:key, /api-keys CRUD, read-only products as the proving resource. - 16-locale error and validation strings; api testsuite in phpunit.xml and the CI matrix. Core prerequisites: InvalidatesCache::cacheKey() returns public_id so cache tags correlate with API ids; settings:manage-api-keys, catalog:read, sales:read and settings:read join the permission manifest with labels in all 16 locales. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follow-on to 0077: typed fields and filters, in-code descriptions, per-resource tags, openapi.json and lunar:api:openapi replacing _schema, CI generation and lint. English-only descriptions and @redocly/cli lint agreed in review. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replaces the _schema endpoint and lunar:api:schema with an OpenAPI 3.1 document per surface, served at openapi.json and written by lunar:api:openapi. Adds the Schema value class, type() / nullable() / describe() on the kernel, cast and relation inference, the Operation and Responds attributes, DescribesSchema, rule-to-schema conversion, the generator with tapDocument(), and an openapi CI job that generates both documents through testbench.yaml and lints them with Redocly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements spec 0077 (
specs/0077-api-platform.md): a newlunarphp/apipackage with one kernel and two surfaces, plus the core prerequisites the spec calls out.Kernel (
Lunar\Api):Resource,ResourceExtension,Field,Embed,Filter,Sort,SerializationContext; the per-surface registry (Api::storefront('v1')->resource() / extend() / replace(), extensions keyed by the built-in so they survive a replacement); the JSON:API-style grammar (include,fields[type],filter[name][op],sort,page[number|size|cursor]) with every unregistered value rejected as a 422 listing what is allowed; thedata/meta/linksenvelope; a single error renderer producing JSON:API error objects for the grammar, validation, auth and framework exceptions;_schemaandlunar:api:schema.Storefront v1: context headers (
X-Lunar-Channel,X-Lunar-Currency,Accept-Language, echoed on the response), the signed statelessX-Lunar-Carttoken drivingCartSession::use(), theCustomerResolvercontract; catalogue reads (products, brands, collections, collection groups, all visibility-scoped to the context),GET /cart,POST /cart/lines,GET /mewhen a host guard is configured.Admin v1: Lunar-owned
ApiKey(SHA-256 hash, plaintext shown once, abilities, optional staff owner, expiry, revocation), thelunar-api-keyguard driver, an abilities gate that reuses the staff permission vocabulary, activity-log attribution to the owner or the key,lunar:api:key create|list|revoke,/api-keysendpoints, and read-only products as the proving resource.Core:
InvalidatesCache::cacheKey()now returnspublic_idso cache tags correlate with API ids;settings:manage-api-keys,catalog:read,sales:read,settings:readjoinAuth\Manifestwith labels in all 16 locales.One naming deviation from the spec: the include value class is
Embed, becauseincludeis a reserved word in PHP. The spec is updated to say so.OpenAPI (spec 0081, slice 9 of 0077)
_schemaandlunar:api:schemaare gone. Each surface now serves an OpenAPI 3.1 document atGET /{prefix}/v1/openapi.json, andphp artisan lunar:api:openapi {surface} [--api-version=v1] [--out=] [--yaml]writes the same thing from the console. The document is generated from the registry, so extension fields, filters, sorts and routes show up automatically, and it describes the whole surface with ability gates asx-lunar-requires.Lunar\Api\OpenApi\Schemavalue class;Field::type()/Field::nullable()/Filter::type()/Embed::many(); attribute fields infer from model casts and dates, translatable fields serialise per surface (stringon storefront,TranslationMapon admin), includes reference the target resource with cardinality read from the Eloquent relation. Untyped members degrade tox-lunar-untyped;OpenApiCoverageTestasserts no built-in is.Resource::label()/description(),describe()on fields, filters, includes and sorts,#[Operation]/#[Responds]on controller methods,DescribesSchemafor bespoke bodies,descriptions()on form requests.index()/show()are described by convention. Every built-in field, include, filter, sort and endpoint is described (also asserted).RulesToSchema,SurfaceProfile,ComponentBuilder,OperationBuilder,Generator,DocumentwithSurfaceRegistry::tapDocument(). Operations are tagged by resource type with the label asx-groupfor Mintlify;deepObjectfields/filterparameters; per-surface headers, security schemes and envelope meta; sharedMoney,TranslationMap,Error,ErrorResponse,PaginationMeta,Linkscomponents; unused shared components are pruned. Memoised per registry until it changes.openapijob boots the packages throughtestbench.yaml(no host app, no database), generates both documents, lints them withnpx @redocly/cli@2 lint, uploads them as theopenapiartefact. Both documents lint clean locally; the only warning is the unusedCustomerschema when no storefront guard is configured.fetchclient) against the storefront document: 165 exports, functions named fromoperationId(productsIndex,cartsLinesStore), JSDoc from descriptions, andfiltertyped as an object per filter. Caveat recorded in the spec: Orval's built-in fetch URL builder serialisesdeepObjectone level deep, so the operator form (filter[price][gte]) needs the axios client with aqsserialiser or a mutator; theeqshorthand works out of the box.Not in this repo: the docs-site wiring (
docs.jsontabs and overlays) lives in the docs repo.Left for the child specs
Per the spec's own plan, the remaining storefront writes and checkout (0078), admin catalogue/sales/inventory/settings writes and idempotency keys (0079), webhooks (0080), and the search filter and Stripe intent route (slice 8) are not in this PR. The slice checklist in the spec reflects what shipped.
Test plan
vendor/bin/pest --testsuite api(61 tests, new suite added tophpunit.xmland the CI matrix)vendor/bin/pest --testsuite core --parallel(cache tag assertions moved topublic_id)vendor/bin/pest --testsuite panel --parallel,admin,filamentvendor/bin/phpstan analysevendor/bin/pint --dirty🤖 Generated with Claude Code