Commit 37f0508d authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

fix: clarify error handling and internal error model in app instance and registration flows

parent 276586ec
Loading
Loading
Loading
Loading
+7 −2
Changes for oeg/app-instance-flow.md: 7 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -440,7 +440,12 @@ event.srm.operation.completed (DataBus — published by SRM)
```

> **Note:** SRM event `status` values are lowercase: `"completed"`, `"partially_completed"`,
> `"failed"`. Error objects follow RFC 7807.
> `"failed"`. Error objects in the event are RFC 7807 (SRM's internal format, per
> [srm/interface-contract.md](/srm/interface-contract.md) §E) — OEG stores this as-is in
> `operations.error` (§4.1 below). This is OEG's internal error model; it is not exposed to the
> external CAMARA client (`GET /platform/v1/operations/{operationId}` is internal/operator-only,
> [oeg/interface-contract.md](/oeg/interface-contract.md) §K.2). Only synchronous request-validation
> errors returned directly in OEG's HTTP responses are mapped to CAMARA's `ErrorInfo` (ADR-0030).

---

@@ -454,7 +459,7 @@ Join key: `event.operation_id` → `operations.operation_id`
|---|---|---|---|
| `status` | `PENDING` | `COMPLETED` | `FAILED` |
| `result` | `null` | `{"instances": [{"app_instance_id": "9a3f1c22-...", "edge_cloud_zone_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "state": "ready"}]}` | `null` |
| `error` | `null` | `null` | RFC 7807 object from event |
| `error` | `null` | `null` | RFC 7807 object from event, stored as-is (OEG-internal; ADR-0030) |
| `completed_at` | `null` | `2026-07-04T10:02:35Z` | `2026-07-04T10:02:35Z` |
| `updated_at` | `...` | `2026-07-04T10:02:35Z` | `2026-07-04T10:02:35Z` |

+8 −3
Changes for oeg/app-registration-flow.md: 8 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -342,10 +342,15 @@ Per the vendored spec this is the `SubmittedApp` schema (`201 Created`).
## 9a. Read & Delete (coverage note)

- **`GET /apps`** and **`GET /apps/{appId}`** return `AppManifest` data. The manifest lives in
  SRM (`service_specification` + `service_deployment_unit`) — OEG resolves the caller's
  `app_registrations` rows and proxies the SRM catalog read
  SRM (`service_specification` + `service_deployment_units[]` — a catalog entry may carry more
  than one unit, and not every unit need carry its own `artifact_ref`) — OEG resolves the
  caller's `app_registrations` rows and proxies the SRM catalog read
  (`GET /internal/catalog/service-specifications`), translating back to the CAMARA manifest
  shape. `oeg_db` alone cannot serve these reads (it stores only the summary row, §8).
  shape. `oeg_db` alone cannot serve these reads (it stores only the summary row, §8). If no
  unit in the entry carries an `artifact_ref` — the only source for the CAMARA-mandatory
  `appRepo.imagePath` — the read fails with `DownstreamServiceException` (503 `UNAVAILABLE`)
  rather than omitting the entry: `getApps`' `200` has no partial-success shape, so a single
  unmappable entry fails the whole list instead of silently shrinking it.
- **`DELETE /apps/{appId}`** removes the specification from the SRM catalog and marks the
  `app_registrations` row `DELETED`. Per the vendored spec it returns `409 ABORTED` while the
  app still has running instances (`app_instances` rows not terminated).
+1 −0
Changes for oeg/interface-contract.md: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -208,6 +208,7 @@ Response header: `X-Correlator: req-abc-123`
- OEG does not terminate EWBI and does not make direct SBI (Southbound Interface) calls.
- Callbacks are CloudEvents (`application/cloudevents+json`), not proprietary JSON bodies; auth uses `sinkCredential` bearer tokens, not a platform shared secret (ADR-0008).
- `GET /platform/v1/operations/{operationId}` exposes `status` (not `state`) — 1:1 with `operations.status`. Do not confuse with `event.srm.operation.status`, whose payload field is named `state` per [srm/interface-contract.md](/srm/interface-contract.md) §C.1.
- `GET /apps` and `GET /edge-cloud-zones` have no partial-success shape — their `200` is a bare array with no metadata slot for a skipped-item count. One SRM entry that can't be mapped (e.g. a catalog entry with no `artifact_ref` anywhere, or a zone with a malformed id) fails the whole read with `503`/`UNAVAILABLE`; it is never silently omitted from the list.

## L. Eventing / Integration Design

+4 −4
Changes for oeg/persistence-model.md: 4 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -176,7 +176,7 @@ in `app_instances.state`).
| `app_registration_id` | UUID FK -> `app_registrations` | NULLABLE (not all operations concern an app); resolved from the request's `appId` (ADR-0010) |
| `subject` | VARCHAR(128) | Published DataBus subject, e.g. `command.srm.service.deploy` |
| `result` | JSONB | NULLABLE; populated from `event.srm.operation.completed`. Holds the `instances[]` list — one entry per resulting app instance: `{app_instance_id, edge_cloud_zone_id, state}`, matching the `app_instances` column names (ADR-0005, ADR-0013). `app_instance_id` is OEG's own enrichment during the write — the raw event only carries `service_instance_id`; OEG maps it to the `app_instance_id` it minted. Example: `{"instances": [{"app_instance_id": "9a3f1c22-...", "edge_cloud_zone_id": "f47ac10b-...", "state": "ready"}]}` |
| `error` | JSONB | NULLABLE; RFC 7807 structure |
| `error` | JSONB | NULLABLE; RFC 7807 structure, stored as-is from the SRM event — this is OEG's internal error model, not exposed to the external CAMARA client (only Portal/operator tooling reads it, via `GET /platform/v1/operations/{operationId}` §K.2). Only synchronous request-validation errors returned directly in OEG's HTTP responses are CAMARA `ErrorInfo` (ADR-0030) |
| `metadata` | JSONB | DEFAULT `{}`; request context (name, zone/domain pins, ...) |
| `created_at` | TIMESTAMPTZ | |
| `updated_at` | TIMESTAMPTZ | Set on every status transition |
@@ -385,7 +385,7 @@ The baseline Helm chart sets `250m` CPU and `512Mi` memory. HPA is a Phase 2 con

## S. Testing Strategy

- **Unit tests** cover domain policies, idempotency logic, and RFC 7807 mapping.
- **Unit tests** cover domain policies, idempotency logic, and the error model split: CAMARA `ErrorInfo` on synchronous validation responses vs. RFC 7807 stored as-is in `operations.error`.
- **Integration tests** use PostgreSQL, Redis, and NATS testcontainers, publish a fake `event.srm.operation.completed`, and assert the resulting database state.
- **Contract tests** validate OpenAPI behavior with Schemathesis and validate shared `command.srm.service.deploy` JSON Schemas from `oop-commons`.
- **Fixtures** can use `oop_commons.testing` fake JWTs with roles.
@@ -424,7 +424,7 @@ phase above.
1. Epic: OEG FastAPI skeleton plus `oop-commons` integration
2. Epic: Operations persistence plus NATS publish and consume
3. Epic: Rate limiting plus usage metering
4. Epic: TMF (TM Forum) and CAMARA routes plus RFC 7807 handling
4. Epic: TMF (TM Forum) and CAMARA routes plus `ErrorInfo` handling
5. Epic: Callback delivery plus retry mechanism (TBD — see OQ-03)
6. Epic: Integration tests plus CI contracts

@@ -438,7 +438,7 @@ phase above.
## X. Final Recommendations

**Recommended MVP**  
Ship JWT validation, RFC 7807, one async deploy path, `GET /platform/v1/operations/{operationId}`, `event.srm.operation.completed`, and metering rows first. Defer the broader TMF facade surface, federation-aware routing, and real-time SBI-CHF integration.
Ship JWT validation, `ErrorInfo`, one async deploy path, `GET /platform/v1/operations/{operationId}`, `event.srm.operation.completed`, and metering rows first. Defer the broader TMF facade surface, federation-aware routing, and real-time SBI-CHF integration.

**First five actions**

+2 −2
Changes for srm/canonical-parameters-schema.md: 2 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -165,7 +165,7 @@ a defined canonical object rather than scattered columns.
    "accelerator": {                     // present only if GPU/VPU/FPGA required
      "type": "gpu",                     // gpu | vpu | fpga
      "units": 1,                        // CAMARA gpuPool.numGPU / GSMA numGPU
      "memory_mb": 32768                 // CAMARA gpuPool.gpuMemory / GSMA gpuMemory
      "memory_mb": 32000                 // CAMARA gpuPool.gpuMemory x1000 (decimal GB) / GSMA gpuMemory
    },
    "storage": [                         // CAMARA additionalStorage (persistent); k8s manifests
                                         // carry a "NNGB"/"NNMB" string parsed by OEG (name/
@@ -180,7 +180,7 @@ a defined canonical object rather than scattered columns.
    "min_nodes": 2,
    "min_node_cpu_millicores": 1000,
    "min_node_memory_mb": 1024,
    "min_node_gpu_memory_mb": 32768      // gpuPool.topology.minNodeGpuMemory
    "min_node_gpu_memory_mb": 32000      // gpuPool.topology.minNodeGpuMemory x1000 (decimal GB)
  },

  // exposed interfaces (CAMARA componentSpec[].networkInterfaces[])
Loading