diff --git a/ipa/general/0124.mdx b/ipa/general/0124.mdx index f7c6f16..a8b956a 100644 --- a/ipa/general/0124.mdx +++ b/ipa/general/0124.mdx @@ -11,32 +11,519 @@ reduced when clients need to modify the lists. ## Guidance -- Repeated fields **must** use a plural field name -- Repeated fields **should** have an enforced upper bound that will not cause a - single resource payload to become too large - - A good rule of thumb is 100 elements - - If repeated data has the chance of being too large, the API **should** use a - sub-resource instead -- [Client-owned](0111.mdx#single-owner-fields) repeated fields **should** be - respected by the server - - The server **should not** modify the order of elements or remove duplicates, - without explicit documentation that the server will do so + + + + +Repeated fields **must** use a plural field name. + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + tags: + type: array + items: + type: string +``` + + + A plural name signals at a glance that the field holds many values, so a + reader expects an array before reading the type. + + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + tag: + type: array + items: + type: string +``` + + + A singular name on an array field is misleading: the name says one value while + the type says many. Naming and cardinality should agree. + + + + + + + For each schema under `components.schemas`, list every property whose `type` + is `array`, including arrays nested inside objects and array items. + + + For each array property, inspect the field name and decide whether it is + grammatically plural (`tags`, `members`, `items`) rather than singular + (`tag`, `member`, `item`). + + + Report any array property whose name is singular as a violation. + + + + + + + + + +Repeated fields **should** have an enforced upper bound that will not cause a +single resource payload to become too large. A good rule of thumb is 100 +elements. + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + members: + type: array + maxItems: 100 + items: + type: string +``` + + + `maxItems` caps the array, so a single resource payload cannot grow without + limit no matter how many members are added. + + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + members: + type: array + items: + type: string +``` + + + With no `maxItems`, the array is unbounded and the resource payload can grow + arbitrarily large as members accumulate. + + + + + + + + + + +If repeated data has the chance of being too large, the API **should** use a +sub-resource instead. + + + + + +```yaml +paths: + /projects/{projectId}/members: + get: + operationId: listMembers + responses: + "200": + description: A page of members. +``` + + + A potentially large list is exposed as its own collection, so it can be paged + through rather than embedded whole in the parent resource payload. + + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + members: + type: array + maxItems: 100000 + items: + type: string +``` + + + The data is expected to grow large, yet it is embedded as an inline array with + a huge bound. Every read of the parent resource carries the entire list, which + a sub-resource collection would avoid. + + + + + + + For each array property in `components.schemas`, estimate the realistic + maximum element count from the field name, description, and any documented + limits. + + + Decide whether the data can grow large enough that embedding the whole list + in every parent payload is a concern (for example, an upper bound well above + the 100-element rule of thumb, or growth driven by end-user activity). + + + For such a property, check whether the list is instead exposed as a paged + sub-resource collection under the parent path. + + + Report any list that can grow large but is embedded inline rather than + modeled as a sub-resource. + + + + + + + + + +[Client-owned](0111.mdx#single-owner-fields) repeated fields **should** be +respected by the server. + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + labels: + type: array + items: + type: string + description: Client-owned. Stored and returned exactly as supplied. +``` + + + The field is client-owned, and the server stores and returns the array as + supplied, so a read returns the same list a write sent. + + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + labels: + type: array + items: + type: string + description: Client-owned. The server sorts and de-dupes it. +``` + + + The field is client-owned, but the server quietly reorders and de-duplicates + it, so the value read back differs from the value written and a declarative + client sees drift. + + + + + + + Identify the repeated fields that are client-owned per IPA-111: those not + documented as server-owned or `readOnly`. + + + For each, inspect the implementing source or run the create and read + operations to observe what the server stores and returns. + + + Confirm a written list is returned with the same elements, in the same + order, with duplicates preserved. + + + Report any client-owned repeated field that the server silently alters on + write or read. + + + + + + + + + +The server **should not** modify the order of elements or remove duplicates, +without explicit documentation that the server will do so. + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + labels: + type: array + items: + type: string + description: + The server normalizes this list, sorting and de-duplicating it. +``` + + + The server does reorder and de-duplicate the list, and the schema documents + that behavior, so the difference between write and read is expected rather + than surprising. + + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + labels: + type: array + items: + type: string + description: A list of labels. +``` + + + The server reorders and de-duplicates the list at runtime, but the schema says + nothing about it, so the transformation is undocumented and a consumer cannot + anticipate the changed value. + + + + + + + For each repeated field, send a write with a known element order that + includes a deliberate duplicate. + + + Read the resource back and compare the returned array to the written one for + reordering or removed duplicates. + + + When the list is transformed, check whether the field description states + that the server reorders elements or removes duplicates. + + + Report any repeated field the server reorders or de-duplicates without + documenting that behavior. + + + + + + + + ## Update Strategy -- A resource **may** use one of two strategies to enable updating a repeated - field: - - Direct update using the standard [Update](0107.mdx) method - - A standard `Update` method is only able to update the entire list - - Custom `Add` and `Remove` methods - - When choosing a [custom method](0109.mdx) approach, API consumers **must - not** be able to set the field via [Create](0106.mdx) or - [Update](0107.mdx) operations + + + + +A resource **may** use one of two strategies to enable updating a repeated +field: direct update using the standard [Update](0107.mdx) method, which can +only update the entire list; or custom `Add` and `Remove` methods. + + + + + +When choosing a [custom method](0109.mdx) approach, the field **must not** be +settable via [Create](0106.mdx) or [Update](0107.mdx) operations. -:::note + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + members: + type: array + readOnly: true + items: + type: string +``` + + + The repeated field is managed only through the custom add and remove methods, + so it is marked `readOnly` and the create and update request bodies cannot set + it. + + + + + + +```yaml +components: + schemas: + Project: + type: object + properties: + members: + type: array + items: + type: string +``` + + + Custom add and remove methods manage the list, yet the field is also writable + through create and update, leaving two competing ways to set it and an + ambiguous source of truth. + + + + + + + Determine which repeated fields are managed by custom `Add` and `Remove` + methods by scanning for custom-method operations that target a list. + + + For each such field, locate its property in the create and update request + body schemas. + + + Confirm the field is `readOnly` or absent from those request bodies, so the + standard create and update operations cannot set it. + + + Report any custom-method-managed repeated field that remains settable + through create or update. + + + + + + + + Declarative-friendly resources **must** use the standard [Update](0107.mdx) method, and not introduce `Add` and `Remove` methods. To learn more, see [IPA-127](0127.mdx). -::: + + + + +```yaml +paths: + /projects/{projectId}: + put: + operationId: updateProject + requestBody: + content: + application/json: + schema: + type: object + properties: + members: + type: array + items: + type: string +``` + + + A declarative-friendly resource updates the whole list through the standard + update method, so the desired state is expressed in one place that a + declarative client can reconcile. + + + + + + +```yaml +paths: + /projects/{projectId}:members:add: + post: + operationId: addProjectMembers + /projects/{projectId}:members:remove: + post: + operationId: removeProjectMembers +``` + + + A declarative-friendly resource exposes imperative add and remove methods, + which express list changes as deltas a declarative client cannot reconcile + against a desired state. + + + + + + + Identify the resources documented as declarative-friendly per IPA-127. + + + For each, list the operations that modify its repeated fields. + + + Confirm those fields are updated through the standard update method, and + that no custom `Add` or `Remove` methods exist for them. + + + Report any declarative-friendly resource that exposes `Add` or `Remove` + methods for a repeated field. + + + + + + + +