From 8f403446a7c40e079c827be726f5263f0a186c5e Mon Sep 17 00:00:00 2001 From: Kevin Delgado Date: Wed, 22 Jun 2022 23:15:21 +0000 Subject: [PATCH] sketch open api --- .../1027-api-unions/README.md | 127 ++++++++++++++++-- 1 file changed, 115 insertions(+), 12 deletions(-) diff --git a/keps/sig-api-machinery/1027-api-unions/README.md b/keps/sig-api-machinery/1027-api-unions/README.md index 8287d4060fd0..01a8418e9149 100644 --- a/keps/sig-api-machinery/1027-api-unions/README.md +++ b/keps/sig-api-machinery/1027-api-unions/README.md @@ -247,13 +247,19 @@ kubebuilder types): discriminator values). This field MUST be required if there is no default option, omitempty if the default option is the empty string, or optional and omitempty if a default value is specified with the `// +default` marker. -- `// +unionMember= before a field means that this +- `// +unionMember=, before a field means that this field is a member of a union. The `` is the name of the field that will be set as the discriminator value. It MUST correspond to one of the valid enum values of the discriminator's enum type. It defaults to the go (i.e `CamelCase`) representation of the field name if not specified. `` should only be set if authors want to customize how the fields are represented in the discriminator field. `` should match the - serialized JSON name of the field case-insensitively. + serialized JSON name of the field case-insensitively. The comma separated + optional value determines whether or not the member field must be set when the + discriminator selects it. Meaning, when `optional` is present on a field, the + discriminator can select the field even if the field is not set. If optional + is not present in the `unionMember` tag, then the object will fail validation + if the discriminator selects the field but it is nil. A field can be marked as + optional without specifying memberName via `// +unionMember,optional`. - `// +unionDiscriminatedBy=` before a member field identifies which discriminator (and thus which union) the member field belongs to. Optional unless there are multiple unions/discriminators in a single struct. If used, @@ -423,16 +429,113 @@ for validation, but is "on-top" of this proposal. A new extension is created in the openapi to describe the behavior: `x-kubernetes-unions`. -This is a list of unions that are part of this structure/object. Here is what -each list item is made of: -- `discriminator: ` is set to the name of the discriminator - field, if present, -- `fields-to-discriminateBy: {"": ""}` is a map of - fields that belong to the union to their discriminated names. The - discriminatedValue will typically be set to the name of the Go variable. +This is a list of unions that are part of this structure/object. Each item in +the list represents a discriminator for the union, a list of valid discriminator +unions that do not correspond to member fields, and for each member field, +the discriminator value of that field and whether or not that field is optional. Conversion between OpenAPI v2 and OpenAPI v3 will preserve these fields. +The following is an example of what the generated OpenAPI definition will look +like for a given go type. + +``` +const ( + FieldA Union1Type = "FieldA" + FieldB Union1Type = "FieldB" + FieldC Union1Type = "FieldC" + FieldD Union1Type = "FieldD" + FieldNone Union1Type = "" +) +const ( + Alpha Union2Type = "ALPHA" + Beta Union2Type = "BETA" + Gamma Union2Type = "GAMMA" + Null Union2Type = "NULL" +) + +// This will generate one union, with two fields and a discriminator. +type Union struct { + // +unionDiscriminator + // +required + Union1 Union1Type `json:"union1"` + + // +unionMember + // +unionDiscriminatedBy=Union1 + // +optional + FieldA int `json:"fieldA"` + // +unionMember,optional + // +unionDiscriminatedBy=Union1 + // +optional + FieldB int `json:"fieldB"` + + // +unionDiscriminator + // +required + Union2 Union2Type `json:"union2"` + + // +unionMember=ALPHA, + // +unionDiscriminatedBy=Union2 + // +optional + Alpha int `json:"alpha"` + // +unionMember=BETA,optional + // +unionDiscriminatedBy=Union2 + // +optional + Beta int `json:"beta"` +} +``` + +turns into: +``` +OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + ... // schema props omitted + }, + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-unions": []interface{}{ + map[string]interface{}{ + "discriminator": "Union1", + "emptyMembers":[]string{ + "FieldC", + "FieldD", + "", + }, + "fields-to-discriminateBy": map[string]interface{}{ + "FieldA": map[string]interface{}{ + "discriminatorValue": "FieldA", + "optional": false, + } + "FieldB": map[string]interface{}{ + "discriminatorValue": "FieldB", + "optional": true, + } + } + }, + map[string]interface{}{ + "discriminator": "Union2", + "emptyMembers":[]string{ + "GAMMA", + "NULL", + }, + "fields-to-discriminateBy": map[string]interface{}{ + "Alpha": map[string]interface{}{ + "discriminatorValue": "ALPHA", + "optional": false, + } + "Beta": map[string]interface{}{ + "discriminatorValue": "BETA", + "optional": true, + } + } + } + } + } + } + } +} +``` + ### Normalization and Validation #### Normalization @@ -462,11 +565,11 @@ called by the request handlers shortly after mutating admission occurs. Objects must be validated AFTER the normalization process. Some validation situations specific to unions are: -1. When multiple union fields are set and the discriminator is not set we should +1. When multiple union fields are set and the discriminator has not been modified we should error loudly that the client must change the discriminator if it changes any union member fields. -2. When the server receiveds a request with a discriminator set to a given - field, but that given field is empty, the server should fail with a clear +2. When the server receives a request with a discriminator set to a given + field, but that given field is empty and not marked as optional, the server should fail with a clear error message. Note this does not apply to discriminator values that do not correspond to any field (as in the "empty union members case").