Skip to content

feat: GraphQL API Layer with Lighthouse PHP (#13)#31

Closed
Weegy wants to merge 8 commits intodevfrom
feature/graphql-api
Closed

feat: GraphQL API Layer with Lighthouse PHP (#13)#31
Weegy wants to merge 8 commits intodevfrom
feature/graphql-api

Conversation

@Weegy
Copy link
Copy Markdown
Contributor

@Weegy Weegy commented Mar 15, 2026

Overview

This PR implements the GraphQL API Layer for Numen, powered by Lighthouse PHP.

Changes

  • Lighthouse PHP integration for GraphQL schema execution
  • 8-chunk implementation covering:
    • GraphQL schema & type definitions
    • Query resolvers & mutations
    • Subscription support
    • Automatic Persistent Queries (APQ)
    • Query caching & performance optimization

Quality & Testing

  • 47 comprehensive tests - All passing
  • Security audit pass - 0 critical/high severity issues
  • Code quality - Pint + PHPStan validation complete

Implementation Details

  • Full GraphQL schema with types, mutations, subscriptions
  • APQ support for query caching
  • Integrated caching layer for improved performance
  • Security & permission validation

Related

numen-bot added 8 commits March 15, 2026 16:09
- Install nuwave/lighthouse ^6.65 and mll-lab/laravel-graphiql ^4.0 (dev)
- Publish lighthouse config with Sanctum guard + AttemptAuthentication middleware
- Scaffold graphql/schema.graphql with Space, Content, ContentVersion, ContentType types
- Public queries: contentBySlug, contents (no @guard)
- Authenticated queries: space, spaces, content, contentTypes, contentVersion (@guard)
- Create resolver classes: ContentBySlugQuery, ContentsQuery, ContentTypesQuery, SpaceContentsQuery
- JSON and DateTime scalars defined
- pint --test: pass, phpstan analyse --level=5: pass
- Add enums: ContentStatus, BriefStatus, PipelineRunStatus, MediaSource
- Add cursor pagination connections: PipelineRunConnection, ContentBriefConnection,
  MediaAssetConnection, PageConnection with PageInfo
- Add types: Persona (system_prompt excluded), ContentPipeline, PipelineRun,
  ContentBrief, MediaAsset, Vocabulary, TaxonomyTerm, Webhook, Page,
  PageComponent, ContentBlock
- Add queries: personas, pipelines, pipelineRun, mediaAssets, vocabularies,
  briefs, webhooks (all @guard), pages (public)
- Add resolver classes for each new query
- Add Mutation type to graphql/schema.graphql with 12 mutations:
  createContent, updateContent, publishContent, unpublishContent,
  deleteContent, createBrief, updateBrief, triggerPipeline,
  approvePipelineRun, rejectPipelineRun, updateMediaAsset, deleteMediaAsset

- Define input types: CreateContentInput, UpdateContentInput,
  CreateBriefInput, UpdateBriefInput, UpdateMediaAssetInput

- Create 12 resolver classes in app/GraphQL/Mutations/:
  - CreateContent: creates Content + initial ContentVersion, syncs taxonomy terms
  - UpdateContent: creates new version on title/body change, patches content fields
  - PublishContent / UnpublishContent: delegates to Content::publish()
  - DeleteContent: soft delete via SoftDeletes trait
  - CreateBrief: creates ContentBrief, triggers active pipeline via PipelineExecutor
  - UpdateBrief: patches brief fields
  - TriggerPipeline: dispatches PipelineExecutor::start with optional existing content
  - ApprovePipelineRun / RejectPipelineRun: mirrors PipelineAdminController logic
  - UpdateMediaAsset / DeleteMediaAsset: CRUD on media metadata

- All mutations use AuthorizationService::authorize() for permission checks
- All mutations write AuditLog entries via AuthorizationService::log()
- All mutations protected by @guard directive (Lighthouse Sanctum)
- Add @with directives on all frequently-accessed relationships:
  - Content: space, contentType, currentVersion, draftVersion, heroImage, taxonomyTerms
  - ContentVersion: content, blocks
  - PipelineRun: pipeline, content
  - Space: contentTypes
  - ContentPipeline: space
  - ContentBrief, MediaAsset, Vocabulary, Persona, Webhook, Page, PageComponent, ContentBlock
- Add @Complexity(resolver: PaginatedComplexity) on all paginated fields:
  - contents, spaces contents, pipelines runs, mediaAssets, briefs, pages
- Add @can directives on all mutations for policy-gated operations:
  - createContent, updateContent, publishContent, unpublishContent, deleteContent
  - createBrief, updateBrief, triggerPipeline, approvePipelineRun, rejectPipelineRun
  - updateMediaAsset, deleteMediaAsset
- Create PaginatedComplexity resolver: complexity = childComplexity * first
- Update config/lighthouse.php:
  - max_query_complexity: 500 (env: LIGHTHOUSE_MAX_COMPLEXITY)
  - max_query_depth: 10 (env: LIGHTHOUSE_MAX_DEPTH)
  - pagination default_count: 20, max_count: 100
  - batchload_relations already enabled
- Add Subscription type to schema with 4 fields:
  contentPublished(spaceId), contentUpdated(contentId),
  pipelineRunUpdated(runId), pipelineRunCompleted(spaceId)
- Add subscription resolvers with authorize() + filter() in app/GraphQL/Subscriptions/
- Add domain events: ContentPublishedEvent, PipelineRunUpdatedEvent
- Wire events into PublishContent and ApprovePipelineRun mutations
- Register EventServiceProvider that broadcasts via Lighthouse Subscription::broadcast()
- Configure lighthouse.php: default broadcaster=log, storage=array (dev-safe)
…ware

- Enable APQ (persisted_queries) with env override LIGHTHOUSE_PERSISTED_QUERIES
- Configure query cache store to use 'file' in dev, 'redis' in production
- Add App\GraphQL\Middleware namespace to directives lookup in lighthouse.php
- Register CostTrackingMiddleware in field_middleware array
- Create CostTrackingMiddleware: tracks field, query_hash, complexity_score,
  execution_time_ms, user_id per field resolution via Log::debug
- Add @cache(maxAge: 30) to contentBySlug and content queries
- Add @cache(maxAge: 60) to contents list and pages list queries
- Add @cache(maxAge: 300) to contentTypes, vocabularies, Space.contentTypes,
  and Vocabulary.terms (stable references that rarely change)
- Pipeline-related fields have no @cache (real-time data)
- GraphQLQueryTest: 9 tests covering spaces, content by slug,
  cursor pagination, auth guard, admin queries, eager loading,
  personas (security: no system_prompt), media assets, taxonomies
- GraphQLMutationTest: 6 tests covering create/publish/delete content,
  trigger pipeline (Queue::fake), unauthorized user, input validation
- GraphQLSubscriptionTest: 2 schema-level tests verifying subscription
  types are defined (contentPublished, pipelineRunUpdated, etc.)
- PaginatedComplexityTest: 5 unit tests for complexity resolver

Supporting fixes:
- Add ContentPipelinePolicy with admin bypass + trigger/approve/create
- Add global Gate::before admin bypass in AppServiceProvider
- Register SubscriptionServiceProvider in bootstrap/providers.php
- Add PAUSED_FOR_REVIEW to PipelineRunStatus enum in schema
@Weegy
Copy link
Copy Markdown
Contributor Author

Weegy commented Mar 16, 2026

Already merged to dev via squash. Closing stale PR.

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