Skip to content

Hybrid org context + Servers CRUD #5

@fbarrento

Description

@fbarrento

What to build

First org-scoped resource. The design's hybrid org-context model lands here for the first time: $forge->org($slug)->... chain override, with an OrganizationNotSetException guard when no org is bound or chained. Implements the full Servers CRUD across the v0.1 surface.

Endpoints (consult docs/forge.openapi.json for the full set, including actions):

  • GET /orgs/{org}/servers
  • POST /orgs/{org}/servers
  • GET /orgs/{org}/servers/{server}
  • PATCH /orgs/{org}/servers/{server}
  • DELETE /orgs/{org}/servers/{server}
  • POST /orgs/{org}/servers/{server}/actions (start, stop, restart, etc.)

Source design: see project_sdk_design.md in the project's memory.

Acceptance criteria

Org context

  • $forge->org($slug): Forge returns a new Forge instance bound to that slug (immutable — does not mutate the original)
  • Org-scoped resources throw OrganizationNotSetException when neither the constructor nor a chain has set an organization
  • Non-org-scoped accessors (me(), organizations(), providers()) keep working regardless of org-context state
  • OrganizationNotSetException extends ForgeException (NOT ApiException — this is a client-side guard, not an HTTP error)

Servers slice

  • Server output DTO (readonly, JsonSerializable, ::from(array))
  • CreateServerData input DTO: required fields per spec (name, provider, region, size, php_version, etc.); enums for closed sets; toArray() strips nulls and converts enums
  • UpdateServerData input DTO with all fields optional
  • ListServersOptions DTO: pagination + sort + filter[ip_address|name|region|size|provider|ubuntu_version|php_version|database_type]
  • Enums: ServerProvider, PhpVersion, DatabaseType, UbuntuVersion (case names match Forge's string values; consult spec)
  • Request classes: GetServersRequest, GetServerRequest, CreateServerRequest, UpdateServerRequest, DeleteServerRequest (+ a ServerActionRequest for the /actions endpoint)
  • ServersResource: all(?ListServersOptions): Page<Server>, iterate(?ListServersOptions): Generator<Server>, create(CreateServerData): Server
  • ServerResource: get(): Server, update(UpdateServerData): Server, delete(): void, plus action methods (e.g. restart(), reboot())
  • $forge->servers() and $forge->server($id) wired

Tests

  • Each CRUD method round-trips correctly against a MockClient
  • $forge->org('other')->servers()->all() hits /api/orgs/other/servers (verifies chain override)
  • OrganizationNotSetException thrown when org-scoped methods are called without context
  • ListServersOptions::toQuery() emits filter[provider]=aws, sort=-created_at, page[size]=... correctly
  • DTO factories (via fbarrento/data-factory) for CreateServerData / UpdateServerData live in tests/Factories/
  • composer test green

Blocked by

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions