Skip to content

Commit f33a512

Browse files
committed
Bring the implementers guide up to 0.2
The server section explains declaring transforms (node-owned, reshaping only, mappers declared in discovery, fix-the-shape-first), the client section adds per-node rendering and the never-render-past-a-failed- transform rule, and both checklists gain the 0.2 conformance bullets. The Go demo test-vector table adds the summary mapper endpoints, the identity-default stats partial, and the transform notes on existing rows. The closing scope paragraph now matches the rewritten Design Decision 3 instead of contradicting it. Strict build passes and every new spec anchor resolves in the built HTML.
1 parent fb42204 commit f33a512

1 file changed

Lines changed: 50 additions & 6 deletions

File tree

docs/implementers-guide.md

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,28 @@ npx ajv-cli test --spec=draft2020 \
5050
-s vdp.v0-2.schema.json -d 'views/*.json' --valid -c ajv-formats
5151
```
5252

53+
**Adapt data with transforms (0.2).** When a response's shape differs from a
54+
template's model, declare a [`transform`](specification.md#38-transforms) on
55+
that node — RFC 6901 pointers reshaping the representation into the contract
56+
the template URI implies:
57+
58+
```json
59+
{
60+
"template": "example.com/templates/data-table",
61+
"transform": { "heading": "/dataset/title", "rows": "/data" }
62+
}
63+
```
64+
65+
Keep two rules in mind. The transform belongs to the *descriptor node*, never
66+
to the template — the same template URI must mean the same model everywhere.
67+
And the grammar is deliberately just reshaping: filtering, derivation, and
68+
formatting stay in your handlers, your templates, or client-registered
69+
[`$mapper`](specification.md#383-mapper-references) code, whose URIs your
70+
[discovery document](specification.md#132-well-known-uri) should declare under
71+
`mappers`. Where you control the representation, prefer fixing its shape at
72+
the source over transforming it
73+
([Section 3.8.4](specification.md#384-when-not-to-transform)).
74+
5375
### 2. Choose an identifier form
5476

5577
A template URI is an **identity first** — a stable name and cache key — and a
@@ -174,6 +196,8 @@ The digest is computed over the exact bytes clients will fetch —
174196
- [ ] Absolute template URIs where descriptors cross base-URL contexts
175197
- [ ] Discovery document valid per Section 13.2, served as `application/vdp-discovery+json`
176198
- [ ] Allowlist entries end with `/` and cover every identifier form emitted
199+
- [ ] Transforms valid per the Section 3.8.1 grammar; every `$mapper` URI declared in discovery
200+
177201

178202
---
179203

@@ -294,6 +318,17 @@ the principle **prefer partial rendering over total failure**
294318
- Only a **root** template failure fails the render — fall back to raw data or
295319
an error template.
296320
- Slot names with no matching insertion point are ignored (log them).
321+
- **Render per node (0.2).** A node with a
322+
[`transform`](specification.md#38-transforms) receives exactly the transform
323+
result; without one, the representation unchanged. Evaluate every node's
324+
transform against the **original** response (with `_view`/`_views` stripped)
325+
— never against an ancestor's output
326+
([Section 3.8.2](specification.md#382-evaluation-semantics)). Missing
327+
pointers and wrong-type targets yield `null` and are *not* errors.
328+
- **Never render past a failed transform.** An unrecognized `$mapper` URI is a
329+
slot failure; on the *root* node, show an error template only — falling back
330+
to untransformed data would be silently wrong output, worse than an error
331+
([Section 9.6](specification.md#96-transform-failures)).
297332
- Impose a recursion depth limit (10 is the recommendation); descriptor
298333
references count toward it, and a reference chain that revisits a URL is a
299334
cycle that abandons just that slot.
@@ -310,7 +345,10 @@ and templates are ordinary HTTP resources, keyed by identity.
310345
- [ ] HTTPS enforced for network retrieval (loopback excepted)
311346
- [ ] `integrity` verified when present; mismatch treated as fetch failure
312347
- [ ] Partial rendering on slot failure; fallback on root failure; depth limit and cycle detection
313-
- [ ] Invalid descriptors rejected; unrecognized discovery members ignored
348+
- [ ] Invalid descriptors rejected — including malformed transforms and unrecognized members not prefixed `x-` (Section 3.10); unrecognized discovery members ignored
349+
- [ ] Inline transform evaluation implemented in full; `$mapper` optional, matched verbatim against your registry
350+
- [ ] Transforms evaluated against the original representation; templates receive exactly the transform result
351+
- [ ] JSON objects parsed order-preservingly where `$entries` results render
314352

315353
---
316354

@@ -347,19 +385,25 @@ serves ready-made vectors — run it locally and point your client at:
347385

348386
| Endpoint | Exercises | Your client should |
349387
|----------|-----------|--------------------|
350-
| `/api/dashboard` | Link transport, form (b) refs, §3.7 reference, integrity | Render a four-level tree |
388+
| `/api/dashboard` | Link transport, form (b) refs, §3.7 reference, integrity, per-slot transforms | Render a four-level tree, each node against its own model |
351389
| `/api/dashboard?fail=chart` | One slot's template 404s | Skip the slot, render the rest |
352390
| `/api/dashboard?fail=root` | Root template 404s | Fall back to raw data |
353391
| `/api/dashboard?fail=integrity` | SRI mismatch | Treat as fetch failure, skip the slot |
354392
| `/api/dashboard?untrusted` | Off-allowlist template | Reject **without fetching** |
355393
| `/api/odata/products` | Link + OData annotation, form (c) opaque identifier | Keep the identifier verbatim as cache key |
356394
| `/api/login` | `View-Template` shorthand | Render the single template |
357-
| `/api/products/42?view=compact` | Multiple named views | Select the requested view, default otherwise |
395+
| `/api/products/42?view=compact` | Multiple named views, per-view transform | Select the requested view, default otherwise |
396+
| `/api/summary` | `$mapper` transform, discovery `mappers` | Dispatch to registered mapper code |
397+
| `/api/summary?fail=mapper` | Unregistered `$mapper` on the root | Error template only — **never** the raw data (§9.4 rule 2) |
398+
| `/api/dashboard/stats` | Identity default — representation already matches the contract | Render with no transform declared |
358399

359400
And remember what is deliberately **not** VDP's job — do not build it into
360-
your implementation: conditional slot logic, template parameters, and
361-
data-to-template field mapping all belong to the server's descriptor choice or
362-
the template engine, never to the protocol
401+
your implementation: conditional slot logic and template parameters belong to
402+
the server's descriptor choice or the template engine, never to the protocol.
403+
Data-to-template mapping *is* in scope as of 0.2, but only as declarative
404+
reshaping — if you find yourself wanting filtering, computation, or an
405+
expression language in a descriptor, that logic belongs server-side or in
406+
client-registered `$mapper` code
363407
([Design Decisions](specification.md#design-decisions)).
364408

365409
*[VDP]: View Descriptor Protocol

0 commit comments

Comments
 (0)