Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AIP-163 – Change validation #12

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions aip/general/0163/aip.md.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# 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

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
PATCH /v1/publishers/{publisher}/books/{book}?dryRun=true HTTP/2
Host: library.googleapis.com
Accept: application/json
```

- 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.

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.

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. 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

{% tab proto %}

{% sample 'standard_operation.proto', 'message CreateBookRequest' %}

- 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.

{% 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 %}
7 changes: 7 additions & 0 deletions aip/general/0163/aip.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
id: 163
state: approved
created: 2019-12-16
placement:
category: design-patterns
order: 90
68 changes: 68 additions & 0 deletions aip/general/0163/standard_operation.oas.yaml
Original file line number Diff line number Diff line change
@@ -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.
74 changes: 74 additions & 0 deletions aip/general/0163/standard_operation.proto
Original file line number Diff line number Diff line change
@@ -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 dry_run = 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;
}