From 767ddfb3e181ced42597adea8cd7a9b7acf97fcd Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Mon, 28 Sep 2020 14:33:11 -0700 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20AIP-163=20=E2=80=93=20Change=20vali?= =?UTF-8?q?dation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aip/general/0163/aip.md.j2 | 46 ++++++++++++++ aip/general/0163/aip.yaml | 7 +++ aip/general/0163/standard_operation.proto | 74 +++++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 aip/general/0163/aip.md.j2 create mode 100644 aip/general/0163/aip.yaml create mode 100644 aip/general/0163/standard_operation.proto diff --git a/aip/general/0163/aip.md.j2 b/aip/general/0163/aip.md.j2 new file mode 100644 index 00000000..0c050008 --- /dev/null +++ b/aip/general/0163/aip.md.j2 @@ -0,0 +1,46 @@ +# Change validation + +Occasionally, a user wants to validate an intended change to see what the +result will be before actually making the change. For example, a request to +provision new servers in a fleet will have an impact on the overall fleet size +and cost, and could potentially have unexpected downstream effects. + +## Guidance + +APIs **may** provide an option to validate, but not actually execute, a +request, and provide the same response (status code, headers, and response +body) that it would have provided if the request was actually executed. + +To provide this option, the method **should** include a `validate_only` boolean +field: + +```http +POST /v1/publishers/{publisher}/books?validate_only=true HTTP/2 +Host: library.googleapis.com +Accept: application/json +``` + +- Standard operations **must** expose the `validate_only` field on the query + string. +- Custom operations **may** expose the `validate_only` field in the request + body, on the query string, or accept either one. + +The API **must** perform permission checks and any other validation that would +be performed on a "live" request; a request using `validate_only` **must** fail +if it determines that the actual request would fail. + +**Note:** It may occasionally be infeasible to provide the full output. For +example, if creating a resource would create an auto-generated ID, it does not +make sense to do this on validation. APIs **should** omit such fields on +validation requests in this situation. + +## Interface Definitions + +{% tab proto %} + +{% sample 'standard_operation.proto', 'message CreateBookRequest' %} + +- The `validate_only` field **must** use the `bool` type. +- The `validate_only` field **must not** be annotated as `REQUIRED`. + +{% endtabs %} diff --git a/aip/general/0163/aip.yaml b/aip/general/0163/aip.yaml new file mode 100644 index 00000000..5a627fc2 --- /dev/null +++ b/aip/general/0163/aip.yaml @@ -0,0 +1,7 @@ +--- +id: 163 +state: approved +created: 2019-12-16 +placement: + category: design-patterns + order: 90 diff --git a/aip/general/0163/standard_operation.proto b/aip/general/0163/standard_operation.proto new file mode 100644 index 00000000..1825c7f4 --- /dev/null +++ b/aip/general/0163/standard_operation.proto @@ -0,0 +1,74 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; + +service Library { + // Create a single book. + rpc CreateBook(CreateBookRequest) returns (Book) { + option (google.api.http) = { + post: "/v1/{parent=publishers/*}/books" + body: "book" + }; + option (google.api.method_signature) = "parent,book"; + } +} + + +// The request message for creating a book. +message CreateBookRequest { + // The parent collection where this book will be created. + // Format: publishers/{publisher} + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "library.googleapis.com/Book" + }]; + + // The book to create. + Book book = 2 [(google.api.field_behavior) = REQUIRED]; + + // If set, validate the request and preview the review, but do not actually + // post it. + bool validate_only = 3; +} + +// A representation of a single book. +message Book { + option (google.api.resource) = { + type: "library.googleapis.com/Book" + pattern: "publishers/{publisher}/books/{book}" + }; + + // The name of the book. + // Format: publishers/{publisher}/books/{book} + string name = 1; + + // The ISBN (International Standard Book Number) for this book. + string isbn = 2; + + // The title of the book. + string title = 3; + + // The author or authors of the book. + repeated string authors = 4; + + // The rating assigned to the book. + float rating = 5; +} From a0afbd9d76252bfd0e88b0d30711266417ef8cf2 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Tue, 5 Jan 2021 11:58:39 -0800 Subject: [PATCH 2/3] Incorporate Mike and Dan feedback. --- aip/general/0163/aip.md.j2 | 62 ++++++++++++++++------- aip/general/0163/standard_operation.proto | 2 +- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/aip/general/0163/aip.md.j2 b/aip/general/0163/aip.md.j2 index 0c050008..2ff50ee2 100644 --- a/aip/general/0163/aip.md.j2 +++ b/aip/general/0163/aip.md.j2 @@ -7,32 +7,54 @@ and cost, and could potentially have unexpected downstream effects. ## Guidance -APIs **may** provide an option to validate, but not actually execute, a -request, and provide the same response (status code, headers, and response -body) that it would have provided if the request was actually executed. - -To provide this option, the method **should** include a `validate_only` boolean -field: +Operations **may** provide an field to validate, but not actually execute, the +request. To provide this option, the operation **should** include a `dry_run` +boolean field: ```http -POST /v1/publishers/{publisher}/books?validate_only=true HTTP/2 +PATCH /v1/publishers/{publisher}/books/{book}?dryRun=true HTTP/2 Host: library.googleapis.com Accept: application/json ``` -- Standard operations **must** expose the `validate_only` field on the query - string. -- Custom operations **may** expose the `validate_only` field in the request - body, on the query string, or accept either one. +- Standard operations **must** expose the `dry_run` field on the query string. +- Custom operations **may** expose the `dry_run` field in the request body, on + the query string, or accept either one. + +### Responses + +An operation **may** return a `204 No Content` response when asked to perform a +dry run, which **must** be an empty response. -The API **must** perform permission checks and any other validation that would -be performed on a "live" request; a request using `validate_only` **must** fail -if it determines that the actual request would fail. +Alternatively, an operation **may** provide the same response (status code, +headers, and response body) that it would have provided if the request was +actually executed. -**Note:** It may occasionally be infeasible to provide the full output. For +Under this approach, it may be infeasible to provide the full output. For example, if creating a resource would create an auto-generated ID, it does not -make sense to do this on validation. APIs **should** omit such fields on -validation requests in this situation. +make sense to do this on validation. The operation **should** prefer to use +`204 No Content` in this scenario, but if a populated response is needed, the +operation **should** omit such fields on the response. + +**Note:** When representing fields that can not be populated, non-zero or +non-empty sentinel values **must not** be used. + +### Validation scope + +When performing a dry run, the service **should** check all of the following: + +- The validity of the request according to the schema. +- Referential integrity of values in the request, if applicable. +- Whether the user has permission to make the request. +- Whether the user has sufficient quota to make the request. + +A request using `dry_run` **must** fail if it can be determined that the actual +request would fail. + +**Note:** An operation **may** elect not to exhaustively check certain +implementation-specific details if validating them would be expensive or pose a +security concern. Therefore, a successful dry run indicates that an equivalent +live request is likely to succeed, but is not a full guarantee of success. ## Interface Definitions @@ -40,7 +62,9 @@ validation requests in this situation. {% sample 'standard_operation.proto', 'message CreateBookRequest' %} -- The `validate_only` field **must** use the `bool` type. -- The `validate_only` field **must not** be annotated as `REQUIRED`. +- The `dry_run` field **must** use the `bool` type. +- The `dry_run` field **must not** be annotated as `REQUIRED`. +- In protocol buffers, the same response type is always used for a single RPC. + The operation **may** return the response message with no fields populated. {% endtabs %} diff --git a/aip/general/0163/standard_operation.proto b/aip/general/0163/standard_operation.proto index 1825c7f4..dbfcd487 100644 --- a/aip/general/0163/standard_operation.proto +++ b/aip/general/0163/standard_operation.proto @@ -46,7 +46,7 @@ message CreateBookRequest { // If set, validate the request and preview the review, but do not actually // post it. - bool validate_only = 3; + bool dry_run = 3; } // A representation of a single book. From 827266d9e56d038f18b2b5b4223c26eb93a60cda Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Sun, 30 Oct 2022 09:34:25 -0500 Subject: [PATCH 3/3] Add oas example of change validation --- aip/general/0163/aip.md.j2 | 8 +++ aip/general/0163/standard_operation.oas.yaml | 68 ++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 aip/general/0163/standard_operation.oas.yaml diff --git a/aip/general/0163/aip.md.j2 b/aip/general/0163/aip.md.j2 index 2ff50ee2..68c1d33a 100644 --- a/aip/general/0163/aip.md.j2 +++ b/aip/general/0163/aip.md.j2 @@ -67,4 +67,12 @@ live request is likely to succeed, but is not a full guarantee of success. - In protocol buffers, the same response type is always used for a single RPC. The operation **may** return the response message with no fields populated. +{% tab operations %} + +{% sample 'standard_operation.oas.yaml', 'paths' %} + +- The `dryRun` query parameter **must** be `type: boolean`. +- The `dryRun` query parameter **must not** be `required: true`. +- createBook could also return a 204 response if `dryRun` is `true`. + {% endtabs %} diff --git a/aip/general/0163/standard_operation.oas.yaml b/aip/general/0163/standard_operation.oas.yaml new file mode 100644 index 00000000..710e3923 --- /dev/null +++ b/aip/general/0163/standard_operation.oas.yaml @@ -0,0 +1,68 @@ +openapi: 3.0.3 +info: + title: Library + version: 1.0.0 +paths: + /publishers/{publisherId}/books/{bookId}: + parameters: + - name: publisherId + in: path + description: The id of the book publisher. + required: true + schema: + type: string + - name: bookId + in: path + description: The id of the book. + required: true + schema: + type: string + patch: + operationId: createBook + description: Create a single book. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Book' + parameters: + - name: dryRun + in: query + description: | + If set, validate the request and preview the book, + but do not actually post it. + schema: + type: boolean + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Book' +components: + schemas: + Book: + description: A representation of a single book. + type: object + properties: + name: + type: string + description: | + The name of the book. + Format: publishers/{publisher}/books/{book} + isbn: + type: string + description: | + The ISBN (International Standard Book Number) for this book. + title: + type: string + description: The title of the book. + authors: + type: array + items: + type: string + description: The author or authors of the book. + rating: + type: number + description: The rating assigned to the book.