diff --git a/aep/general/0133/aep.md.j2 b/aep/general/0133/aep.md.j2 index 726b1172..da377ef3 100644 --- a/aep/general/0133/aep.md.j2 +++ b/aep/general/0133/aep.md.j2 @@ -31,6 +31,9 @@ Create methods are specified using the following pattern: **should** be the only variable in the URI path. - The collection identifier (`books` in the above example) **must** be a literal string. +- Some resources take longer to be created than is reasonable for a regular API + request. In this situation, the API should use a [long-running + operation](/long-running-operations). {% tab proto %} @@ -61,7 +64,7 @@ rpc CreateBook(CreateBookRequest) returns (Book) { {% tab oas %} -**Note:** OAS guidance not yet written +{% sample '../oas.yaml', '/publishers/{publisher_id}/books:' %} {% endtabs %} @@ -107,38 +110,9 @@ message CreateBookRequest { {% tab oas %} -**Note:** OAS guidance not yet written +- The request body **must** be the resource being created. -{% endtabs %} - -### Long-running create - -Some resources take longer to create a resource than is reasonable for a -regular API request. In this situation, the API **should** use a long-running -operation (AEP-151) instead: - -- The response type **must** be set to the resource (what the return type would - be if the RPC was not long-running). - -{% tab proto %} - -```proto -rpc CreateBook(CreateBookRequest) returns (aep.api.Operation) { - option (google.api.http) = { - post: "/v1/{parent=publishers/*}/books" - }; - option (aep.api.operation_info) = { - response_type: "Book" - metadata_type: "OperationMetadata" - }; -} -``` - -- Both the `response_type` and `metadata_type` fields **must** be specified. - -{% tab oas %} - -**Note:** OAS guidance not yet written +{% sample '../oas.yaml', 'book:' %} {% endtabs %} @@ -167,10 +141,6 @@ publishers/lacroix/books/les-miserables publishers/012345678-abcd-cdef/books/12341234-5678-abcd ``` -- The `id` field **must** exist on the request message, not the resource - itself. - - The field **may** be required or optional. If it is required, it **should** - include the corresponding annotation. - The `path` field on the resource **must** be ignored. - The documentation **should** explain what the acceptable format is, and the format **should** follow the guidance for resource path formatting in @@ -187,16 +157,17 @@ publishers/012345678-abcd-cdef/books/12341234-5678-abcd the RPC, with a value of `"parent,{resource},id"` if the resource being created is not a top-level resource, or with a value of `"{resource},id"` if the resource being created is a top-level resource. +- The `id` field **must** exist on the request message, not the resource + itself. + - The field **may** be required or optional. If it is required, it **should** + include the corresponding annotation. {% tab oas %} -**Note:** OAS guidance not yet written +- The `id` is a query parameter on the request URI. {% endtabs %} -**Note:** For REST APIs, the user-specified ID field, `id`, is provided as a -query parameters on the request URI. - ### Errors See [errors][], in particular [when to use PERMISSION_DENIED and NOT_FOUND diff --git a/aep/general/0134/aep.md.j2 b/aep/general/0134/aep.md.j2 index 438171f4..7118f325 100644 --- a/aep/general/0134/aep.md.j2 +++ b/aep/general/0134/aep.md.j2 @@ -24,12 +24,12 @@ Update methods are specified using the following pattern: - The response **should** include the fully-populated resource, and **must** include any fields that were sent and included in the update mask unless they are input only (see AEP-203). - - If the update RPC is [long-running](#long-running-update), the response - **must** be an `Operation` for which the return type is the resource - itself. - The method **should** support partial resource update, and the HTTP verb **should** be `PATCH`. - The operation **must** have [strong consistency][]. +- Some resources take longer to be created than is reasonable for a regular API + request. In this situation, the API should use a [long-running + operation](/long-running-operations). {% tab proto %} @@ -54,7 +54,7 @@ rpc UpdateBook(UpdateBookRequest) returns (Book) { {% tab oas %} -**Note:** OAS example not yet written. +{% sample '../oas.yaml', '$.paths./publishers.post' %} {% endtabs %} @@ -112,7 +112,7 @@ message UpdateBookRequest { {% tab oas %} -**Note:** OAS example not yet written. +{% sample '../oas.yaml', '$.paths./publishers.post.parameters' %} {% endtabs %} @@ -160,79 +160,12 @@ If a rating were set on a book and the existing `PUT` request were executed, it would wipe out the book's rating. In essence, a `PUT` request unintentionally would wipe out data because the previous version did not know about it. -### Long-running update - -Some resources take longer to update a resource than is reasonable for a -regular API request. In this situation, the API **should** use a [long-running -operation][AEP-151] instead: - -- The response type **must** be set to the resource (what the return type would - be if the method were not long-running). - -{% tab proto %} - -```proto -rpc UpdateBook(UpdateBookRequest) returns (aep.api.Operation) { - option (google.api.http) = { - patch: "/v1/{book.name=publishers/*/books/*}" - }; - option (aep.api.operation_info) = { - response_type: "Book" - metadata_type: "OperationMetadata" - }; -} -``` - -- Both the `response_type` and `metadata_type` fields **must** be specified. - -{% tab oas %} - -**Note:** OAS example not yet written. - -{% endtabs %} - +### Create or Update If the service uses client-assigned resource paths, `Update` methods **may** expose a `bool allow_missing` field, which will cause the method to succeed in the event that the user attempts to update a resource that is not present (and -will create the resource in the process): - -{% tab proto %} - -```proto -message UpdateBookRequest { - ... - - // If set to true, and the book is not found, a new book will be created. - // In this situation, `update_mask` is ignored. - bool allow_missing = 3; -} -``` - -{% tab oas %} - -**Note:** OAS example not yet written. - -{% endtabs %} - -More specifically, the `allow_missing` flag triggers the following behavior: - -- If the method call is on a resource that does not exist, the resource is - created. All fields are applied regardless of any provided field mask. - - However, if any required fields are missing or fields have invalid values, - an `INVALID_ARGUMENT` error is returned. -- If the method call is on a resource that already exists, and all fields - match, the existing resource is returned unchanged. -- If the method call is on a resource that already exists, only fields declared - in the field mask are updated. - -The user **must** have the update permissions to call `Update` even with -`allow_missing` set to `true`. - -If the service uses client-assigned resource paths, `Update` methods **may** -expose a `bool allow_missing` field, which will cause the method to succeed in -the event that the user attempts to update a resource that is not present (and -will create the resource in the process): +will create the resource in the process). {% tab proto %} @@ -315,7 +248,7 @@ message Book { {% tab oas %} -**Note:** OAS example not yet written. +{% sample '../oas.yaml', '$.components.schemas.publisher' %} {% endtabs %} diff --git a/aep/general/0158/aep.md.j2 b/aep/general/0158/aep.md.j2 index be3d908f..d3d24322 100644 --- a/aep/general/0158/aep.md.j2 +++ b/aep/general/0158/aep.md.j2 @@ -55,7 +55,7 @@ message ListBooksResponse { {% tab oas %} -**Note:** OAS example not yet written. +{% sample '../oas.yaml', '$.paths./publishers.get' %} {% endtabs %} diff --git a/aep/general/0217/aep.md.j2 b/aep/general/0217/aep.md.j2 index e64ef429..62e469a1 100644 --- a/aep/general/0217/aep.md.j2 +++ b/aep/general/0217/aep.md.j2 @@ -31,7 +31,7 @@ message ListBooksResponse { {% tab oas %} -**Note:** OAS example note yet written. +{% sample '../oas.yaml', '$.paths./publishers/{publisher}/books.get.responses.200.content.application/json' %} {% endtabs %} diff --git a/aep/general/oas.yaml b/aep/general/oas.yaml new file mode 100644 index 00000000..39d5db83 --- /dev/null +++ b/aep/general/oas.yaml @@ -0,0 +1,501 @@ +components: + schemas: + book: + properties: + author: + items: + type: object + type: array + edition: + format: int32 + type: integer + etag: + type: string + id: + readOnly: true + type: string + x-terraform-id: true + path: + readOnly: true + type: string + price: + format: float + type: number + published: + type: boolean + required: + - price + - published + type: object + x-aep-resource: + parents: + - publisher + patterns: + - /publishers/{publisher}/books/{book} + plural: books + singular: book + book-edition: + properties: + displayname: + type: string + id: + readOnly: true + type: string + x-terraform-id: true + path: + readOnly: true + type: string + required: + - displayname + type: object + x-aep-resource: + parents: + - book + patterns: + - /publishers/{publisher}/books/{book}/editions/{book-edition} + plural: book-editions + singular: book-edition + isbn: + properties: + id: + readOnly: true + type: string + x-terraform-id: true + path: + readOnly: true + type: string + type: object + x-aep-resource: + patterns: + - /isbns/{isbn} + plural: isbns + singular: isbn + publisher: + properties: + description: + type: string + etag: + type: string + id: + readOnly: true + type: string + x-terraform-id: true + path: + readOnly: true + type: string + type: object + x-aep-resource: + patterns: + - /publishers/{publisher} + plural: publishers + singular: publisher +info: + title: bookstore.example.com + version: version not set +openapi: 3.1.0 +paths: + /isbns: + post: + parameters: + - in: query + name: id + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/isbn' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/isbn' + description: Successful response + /isbns/{isbn}: + get: + parameters: + - in: path + name: isbn + required: true + schema: {} + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/isbn' + description: Successful response + /publishers: + get: + parameters: + - in: query + name: max_page_size + required: true + schema: {} + type: integer + - in: query + name: page_token + required: true + schema: {} + type: string + responses: + '200': + content: + application/json: + schema: + properties: + results: + items: + $ref: '#/components/schemas/publisher' + type: array + type: object + description: Successful response + post: + parameters: + - in: query + name: id + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/publisher' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/publisher' + description: Successful response + /publishers/{publisher}: + delete: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + responses: + '200': + content: null + description: '' + get: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/publisher' + description: Successful response + patch: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/publisher' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/publisher' + description: Successful response + put: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/publisher' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/publisher' + description: Successful response + /publishers/{publisher}/books: + get: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: query + name: max_page_size + required: true + schema: {} + type: integer + - in: query + name: page_token + required: true + schema: {} + type: string + responses: + '200': + content: + application/json: + schema: + properties: + results: + items: + $ref: '#/components/schemas/book' + type: array + unreachable: + items: + type: string + type: array + type: object + description: Successful response + post: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: query + name: id + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/book' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/book' + description: Successful response + /publishers/{publisher}/books/{book}: + delete: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + responses: + '200': + content: null + description: '' + get: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/book' + description: Successful response + patch: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/book' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/book' + description: Successful response + put: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/book' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/book' + description: Successful response + /publishers/{publisher}/books/{book}/editions: + get: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + - in: query + name: max_page_size + required: true + schema: {} + type: integer + - in: query + name: page_token + required: true + schema: {} + type: string + responses: + '200': + content: + application/json: + schema: + properties: + results: + items: + $ref: '#/components/schemas/book-edition' + type: array + type: object + description: Successful response + post: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + - in: query + name: id + required: true + schema: {} + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/book-edition' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/book-edition' + description: Successful response + /publishers/{publisher}/books/{book}/editions/{book-edition}: + delete: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + - in: path + name: book-edition + required: true + schema: {} + type: string + responses: + '200': + content: null + description: '' + get: + parameters: + - in: path + name: publisher + required: true + schema: {} + type: string + - in: path + name: book + required: true + schema: {} + type: string + - in: path + name: book-edition + required: true + schema: {} + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/book-edition' + description: Successful response +servers: + - url: http://localhost:8081