feat: add x-plan-decomposition Service Plan metadata extension #214

Merged
roque.caballero merged 1 commit from feat/x-plan-decomposition into main 2026-11-04 13:29:55 +00:00

Proposal — x-plan-decomposition Service Plan metadata extension

This PR enhances the broker schema with the x-plan-decomposition vendor extension. A Service Plan may declare the other Service Plans it is composed of and how many units of each it consumes per one unit of the plan. The extension is generic — the referenced plans are most commonly IaaS plans, but it is not restricted to them.

The composition is informational and platform-internal — consumed by the platform (principally the invoicing module) and not surfaced to end users.

Refs: phoenix/tenant-portal#515

Specification basis

The extension is an extra key inside the Service Plan metadata object, which the specification explicitly allows:

  • spec.md § Service Plan Object: metadata is "an opaque object of metadata … Platforms will treat this as a blob."
  • profile.md § Service Metadata: "the specification does not mandate the property names used in the metadata objects" — the listed names are only RECOMMENDED.
  • spec.md § Vendor Extension Fields: a unique prefix (x-) reduces clashes, and receivers MUST ignore unknown fields.

Shape

services[].plans[].metadata.x-plan-decomposition

"metadata": {
  "displayName": "Chat",
  "bullets": ["Dedicated chat", "8 vCPU", "16 GB memory"],
  "x-plan-decomposition": [
    { "planId": "PHOENIQS_INFRA.STORAGE",     "quantity": 100 },
    { "planId": "PHOENIQS_INFRA.COMPUTE_CPU", "quantity": 8 },
    { "planId": "PHOENIQS_INFRA.MEMORY",      "quantity": 16 }
  ]
}
Field Type Required Description
x-plan-decomposition array of objects no The Service Plans this plan is composed of and the units of each consumed per one unit of this plan.
[].planId string yes A Service Plan id (SERVICE.PLAN convention), e.g. PHOENIQS_INFRA.COMPUTE_CPU.
[].quantity number yes Units consumed per one unit of this plan; > 0; fractional allowed.

Changes

  • open-service-broker-model — new PlanDecomposition record; ServicePlanMetadata gains the optional x-plan-decomposition component and now declares its 6-arg canonical constructor explicitly — the only way to set the extension. A backward-compatible constructor is kept, so existing positional callers (osb-core, osb-catalog, osb-maas, kvant-api, …) keep compiling.
  • open-service-broker-api — no production code change: the extension flows through the shared model. Integration tests added: GET /v2/catalog exposes the extension, and it appears in the generated OpenAPI schema.
  • docs — new proposal docs/proposals/x-plan-decomposition.md, linked from the README.

Module impact

Module Impact
open-service-broker-model New PlanDecomposition record + x-plan-decomposition on ServicePlanMetadata.
open-service-broker-api None (shared model); integration tests assert exposure in the catalog JSON and OpenAPI.
open-service-broker-client None — the client decodes Catalog through the shared model and carries the extension transparently.
open-service-broker-schema-validator Not impacted — it validates the JSON schemas exposed for Service Instance / Binding parameters (schemas.*.parameters), not plan metadata.

Compatibility

  • Optional and additive: a plan that omits the key is unchanged.
  • Opaque container: platforms that do not know the key ignore it.
  • Source compatible for downstream callers (backward-compatible constructor).

Testing

./mvnw verify is green across all modules. New coverage: PlanDecompositionTest (4), ServicePlanMetadataTest (+2 — serialize/deserialize with the extension and fractional quantity), CatalogResourceTest (+2 — catalog exposure and OpenAPI documentation).

## Proposal — `x-plan-decomposition` Service Plan metadata extension This PR enhances the broker schema with the **`x-plan-decomposition`** vendor extension. A Service Plan may declare the **other Service Plans it is composed of** and **how many units of each it consumes per one unit of the plan**. The extension is **generic** — the referenced plans are most commonly IaaS plans, but it is not restricted to them. The composition is *informational and platform-internal* — consumed by the platform (principally the **invoicing** module) and **not** surfaced to end users. Refs: phoenix/tenant-portal#515 ### Specification basis The extension is an extra key inside the Service Plan `metadata` object, which the specification explicitly allows: - `spec.md` § *Service Plan Object*: `metadata` is *"an opaque object of metadata … Platforms will treat this as a blob."* - `profile.md` § *Service Metadata*: *"the specification does not mandate the property names used in the `metadata` objects"* — the listed names are only **RECOMMENDED**. - `spec.md` § *Vendor Extension Fields*: a unique prefix (`x-`) reduces clashes, and receivers **MUST** ignore unknown fields. ### Shape `services[].plans[].metadata.x-plan-decomposition` ```json "metadata": { "displayName": "Chat", "bullets": ["Dedicated chat", "8 vCPU", "16 GB memory"], "x-plan-decomposition": [ { "planId": "PHOENIQS_INFRA.STORAGE", "quantity": 100 }, { "planId": "PHOENIQS_INFRA.COMPUTE_CPU", "quantity": 8 }, { "planId": "PHOENIQS_INFRA.MEMORY", "quantity": 16 } ] } ``` | Field | Type | Required | Description | |---|---|---|---| | `x-plan-decomposition` | array of objects | no | The Service Plans this plan is composed of and the units of each consumed per one unit of this plan. | | `[].planId` | string | yes | A Service Plan id (`SERVICE.PLAN` convention), e.g. `PHOENIQS_INFRA.COMPUTE_CPU`. | | `[].quantity` | number | yes | Units consumed per one unit of this plan; `> 0`; **fractional allowed**. | ### Changes - **`open-service-broker-model`** — new `PlanDecomposition` record; `ServicePlanMetadata` gains the optional `x-plan-decomposition` component and now declares its **6-arg canonical constructor explicitly** — the only way to set the extension. A backward-compatible constructor is kept, so existing positional callers (osb-core, osb-catalog, osb-maas, kvant-api, …) keep compiling. - **`open-service-broker-api`** — no production code change: the extension flows through the shared model. Integration tests added: `GET /v2/catalog` exposes the extension, and it appears in the generated OpenAPI schema. - **`docs`** — new proposal [`docs/proposals/x-plan-decomposition.md`](docs/proposals/x-plan-decomposition.md), linked from the README. ### Module impact | Module | Impact | |---|---| | `open-service-broker-model` | New `PlanDecomposition` record + `x-plan-decomposition` on `ServicePlanMetadata`. | | `open-service-broker-api` | None (shared model); integration tests assert exposure in the catalog JSON and OpenAPI. | | `open-service-broker-client` | None — the client decodes `Catalog` through the shared model and carries the extension transparently. | | `open-service-broker-schema-validator` | **Not impacted** — it validates the JSON *schemas* exposed for Service Instance / Binding parameters (`schemas.*.parameters`), not plan `metadata`. | ### Compatibility - Optional and additive: a plan that omits the key is unchanged. - Opaque container: platforms that do not know the key ignore it. - Source compatible for downstream callers (backward-compatible constructor). ### Testing `./mvnw verify` is green across all modules. New coverage: `PlanDecompositionTest` (4), `ServicePlanMetadataTest` (+2 — serialize/deserialize with the extension and fractional quantity), `CatalogResourceTest` (+2 — catalog exposure and OpenAPI documentation).
feat: add x-plan-decomposition Service Plan metadata extension
All checks were successful
Build / build (pull_request) Successful in 1m17s
996dced939
Add first-class support for the PHOENIQS x-plan-decomposition vendor
extension carried inside the Service Plan metadata object: a plan may
declare the other Service Plans it is composed of and the units of each
it consumes per one unit of the plan (fractional allowed). The extension
is generic — the referenced plans are most commonly IaaS plans but are
not restricted to them.

- model: new PlanDecomposition record; ServicePlanMetadata gains the
  optional x-plan-decomposition key (a backward-compatible constructor
  is kept so existing positional callers keep compiling)
- api: integration tests asserting that GET /v2/catalog exposes the
  extension and that it is documented in the generated OpenAPI schema
- docs: proposal describing the extension, its specification basis,
  semantics and per-module impact

Receivers that do not understand the extension ignore it, per the OSB
specification's opaque-metadata and vendor-extension rules.

Refs: phoenix/tenant-portal#515
roque.caballero force-pushed feat/x-plan-decomposition from 996dced939
All checks were successful
Build / build (pull_request) Successful in 1m17s
to 84e2e33771
All checks were successful
Build / build (pull_request) Successful in 1m2s
2026-11-04 13:08:03 +00:00
Compare
roque.caballero force-pushed feat/x-plan-decomposition from 84e2e33771
All checks were successful
Build / build (pull_request) Successful in 1m2s
to af27b98f6e
All checks were successful
Build / build (pull_request) Successful in 1m13s
2026-11-04 13:17:59 +00:00
Compare
roque.caballero force-pushed feat/x-plan-decomposition from af27b98f6e
All checks were successful
Build / build (pull_request) Successful in 1m13s
to 55239c2303
All checks were successful
Build / build (pull_request) Successful in 57s
2026-11-04 13:21:23 +00:00
Compare
roque.caballero force-pushed feat/x-plan-decomposition from 55239c2303
All checks were successful
Build / build (pull_request) Successful in 57s
to 4d07e552d0
All checks were successful
Build / build (pull_request) Successful in 1m20s
2026-11-04 13:23:23 +00:00
Compare
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
phoenix-oss/open-service-broker!214
No description provided.