From c21c4971026eb27831a02b2a7023eb4a8082bb9b Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Thu, 23 May 2024 17:05:19 -0500 Subject: [PATCH 01/17] Doc update for the new Helidon 4 MP support in the Helidon OpenAPI code generators Signed-off-by: Tim Quinn --- docs/pom.xml | 58 ++ .../main/asciidoc/includes/attributes.adoc | 2 +- .../includes/openapi/openapi-generator.adoc | 7 +- .../mp/openapi/openapi-generator.adoc | 31 +- .../ExampleOpenApiGenClientResource.java | 47 ++ docs/src/main/resources/petstorex.yaml | 794 ++++++++++++++++++ parent/pom.xml | 6 + 7 files changed, 937 insertions(+), 8 deletions(-) create mode 100644 docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java create mode 100644 docs/src/main/resources/petstorex.yaml diff --git a/docs/pom.xml b/docs/pom.xml index e9f8aa3d2c5..0d104f44c93 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -34,6 +34,7 @@ false 2.1.0 2.1.0 + 7.6.0 @@ -101,8 +102,19 @@ + + ${project.artifactId} + + + + org.openapitools + openapi-generator-maven-plugin + ${version.plugin.openapi-generator} + + + org.apache.maven.plugins @@ -155,6 +167,52 @@ ${helidon.sitegen.skip} + + org.codehaus.mojo + build-helper-maven-plugin + + + compute-major-version + + parse-version + + validate + + parsedHelidonVersion + + + + + + org.openapitools + openapi-generator-maven-plugin + + + create-openapi-generator-project + + generate + + generate-sources + + java-helidon-client + mp + ${project.basedir}/src/main/resources/petstorex.yaml + + helidonVersion=${parsedHelidonVersion.majorVersion} + + ${project.build.directory}/generated-sources/mp/client + true + + io.helidon.docs.openapi.gen.mp + helidon-docs-openapi-gen-mp-client + io.helidon.docs.openapi.gen.mp.client.api + io.helidon.docs.openapi.gen.model + io.helidon.docs.openapi.gen.mp.client + + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/docs/src/main/asciidoc/includes/attributes.adoc b/docs/src/main/asciidoc/includes/attributes.adoc index 520f68b7226..e1c80e22389 100644 --- a/docs/src/main/asciidoc/includes/attributes.adoc +++ b/docs/src/main/asciidoc/includes/attributes.adoc @@ -281,7 +281,7 @@ endif::[] :micrometer-javadoc-registry-prometheus-base-url: {micrometer-javadoc-base-url}/micrometer-registry-prometheus/{version-lib-micrometer}/io/micrometer/prometheus // OpenAPI generator -:openapi-generator-version: 6.2.1 +:openapi-generator-version: 7.6.0 :openapi-generator-tool-base-url: https://github.com/OpenAPITools/openapi-generator :openapi-generator-site-version: v{openapi-generator-version} diff --git a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc index e75a4ee503d..f793fc31ba9 100644 --- a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc @@ -20,7 +20,6 @@ :keywords: helidon, {flavor-lc}, openapi, generator :feature-name: Helidon {flavor-uc} OpenAPI Generator // DO NOT CHANGE THE FOLLOWING - it's used as a minimum release that will not normally change with new releases of the OpenAPI generator -:first-version-with-strong-helidon-support: 6.2.1 // Update the following when it is convenient to keep pace with the latest releases of the OpenAPITools generator // end::preamble[] @@ -38,8 +37,8 @@ The link:{openapi-spec-url}[OpenAPI specification] provides a standard way to ex Separately, the link:{openapi-generator-tool-site-url}[OpenAPI generator] project has created a powerful code generator tool which accepts an OpenAPI document and generates client and server code for many languages and frameworks. The Helidon team contributes to this tool to ensure that it provides strong support for Helidon {flavor-uc} clients and servers. As a result, you can use the generator to create code that fits smoothly into your Helidon applications. -The OpenAPI generator release {first-version-with-strong-helidon-support} gained particularly strong support for Helidon. -This document applies to that release and later ones. + +Use the OpenAPI generator release {openapi-generator-version} or later which this document describes. In the vocabulary of the tool, there are two _generators_ for Helidon: @@ -81,7 +80,7 @@ The rest of this document walks you through <> each te == Maven Coordinates Your project does not need any dependencies on the OpenAPI generator. -To use the OpenAPI generator plug-in to generate or regenerate files during your project build, add the following to your project's `pom.xml` file to declare the plug-in. Choose whichever version of the generator plug-in meets your needs as long as it is at least {first-version-with-strong-helidon-support}. +To use the OpenAPI generator plug-in to generate or regenerate files during your project build, add the following to your project's `pom.xml` file to declare the plug-in. Choose whichever version of the generator plug-in meets your needs as long as it is at least {openapi-generator-version}. [source,xml,subs="+attributes,+macros"] .Declaring the OpenAPI Generator Plug-in diff --git a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc index 8b16730e1af..47302b4bd7b 100644 --- a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc @@ -24,8 +24,33 @@ include::{rootdir}/includes/mp.adoc[] -== Coming Soon +:incdir: {rootdir}/includes/openapi +:gen-inc: {incdir}/openapi-generator.adoc +:helidon-client-xref: {restclient-page} -The support for link:{openapi-generator-tool-site-url}[OpenAPI generator] is not available yet. +include::{gen-inc}[tag=preamble] -You can track the progress for this feature here: https://github.com/helidon-io/helidon/issues/7943. +include::{gen-inc}[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;using-generated-code-client-intro] + +The Helidon MP client generator creates a MicroProfile REST client interface for each API. +Each generated API interface is annotated so your code can `@Inject` the API into one of your own beans and then use the interface directly to invoke the remote service. Alternatively, you can also explicitly use the link:{microprofile-rest-client-javadoc-url}/org/eclipse/microprofile/rest/client/RestClientBuilder.html[`RestClientBuilder`] to create an instance programmatically and then invoke its methods to contact the remote service. +The xref:{restclient-page}[Helidon MP REST Client] documentation describes both approaches in more detail. + +In the following example, `ExampleResource` (itself running in a server) invokes a remote Pet service and shows one way to use the generated `PetApi` REST client interface. + + +[source,java] +.Using the generated `PetApi` returned from a separate service +---- +include::{sourcedir}/mp/openapi/ExampleOpenApiGenClientResource.java[tag=class-declaration, indent=0] + +---- +<1> Uses a bean-defining annotation so CDI can inject into this class. +<2> Requests that CDI inject the following field. +<3> Identifies to Helidon MP that the following field is a REST client. +<4> Declares the field using the generated `PetApi` type. +<5> Invokes the remote service using the injected field and the parameter from the incoming request. + + +include::{gen-inc}[tag=common-references] +* link:https://github.com/eclipse/microprofile-rest-client[MicroProfile REST Client specification] diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java b/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java new file mode 100644 index 00000000000..364c3327621 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.mp.openapi; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.helidon.docs.openapi.gen.model.Pet; +import io.helidon.docs.openapi.gen.mp.client.api.ApiException; +import io.helidon.docs.openapi.gen.mp.client.api.PetApi; + +import org.eclipse.microprofile.rest.client.inject.RestClient; + + +// tag::class-declaration[] +@Path("/exampleServiceCallingService") // <1> +public class ExampleOpenApiGenClientResource { + @Inject // <2> + @RestClient // <3> + private PetApi petApi; // <4> + + @GET + @Path("/getPet/{petId}") + @Produces(MediaType.APPLICATION_JSON) + public Pet getPetUsingId(@PathParam("petId") Long petId) throws ApiException { + Pet pet = petApi.getPetById(petId); // <5> + return pet; + } +} +// end::class-declaration[] diff --git a/docs/src/main/resources/petstorex.yaml b/docs/src/main/resources/petstorex.yaml new file mode 100644 index 00000000000..eb02dc6e1c3 --- /dev/null +++ b/docs/src/main/resources/petstorex.yaml @@ -0,0 +1,794 @@ +# +# Copyright (c) 2017, 2023 Oracle and/or its affiliates. +# +# 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 +# +# http://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. +# + +openapi: 3.0.0 +servers: + - url: 'https://petstore3.swagger.io/api/v3' +info: + description: >- + This is a sample server Petstore server. For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +tags: + - name: pet + description: Everything about your Pets + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user +paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + description: '' + operationId: addPet + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + $ref: '#/components/requestBodies/Pet' + put: + tags: + - pet + summary: Update an existing pet + description: '' + operationId: updatePet + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + $ref: '#/components/requestBodies/Pet' + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + style: form + explode: false + schema: + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid status value + security: + - petstore_auth: + - 'read:pets' + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: >- + Multiple tags can be provided with comma separated strings. Use tag1, + tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + style: form + explode: false + schema: + type: array + items: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid tag value + security: + - petstore_auth: + - 'read:pets' + deprecated: true + '/pet/{petId}': + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + name: + description: Updated name of the pet + type: string + status: + description: Updated status of the pet + type: string + delete: + tags: + - pet + summary: Deletes a pet + description: '' + operationId: deletePet + parameters: + - name: api_key + in: header + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + '/pet/{petId}/uploadImage': + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + file: + description: file to upload + type: string + format: binary + /pet/info/cookie/{label}: + get: + tags: + - pet + summary: passes params via cookie + description: '' + operationId: petInfoWithCookies + parameters: + - name: petId + in: cookie + description: ID of pet to get + required: true + schema: + type: integer + format: int64 + - name: transactionId + in: cookie + description: ID of transaction + required: true + schema: + type: integer + format: int64 + - name: label + in: path + description: general-purpose label + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: '' + operationId: placeOrder + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid Order + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + description: order placed for purchasing the pet + required: true + '/store/order/{orderId}': + get: + tags: + - store + summary: Find purchase order by ID + description: >- + For valid response try integer IDs with value <= 5 or > 10. Other values + will generated exceptions + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + type: integer + format: int64 + minimum: 1 + maximum: 5 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: >- + For valid response try integer IDs with value < 1000. Anything above + 1000 or nonintegers will generate API errors + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Created user object + required: true + /user/createWithArray: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithArrayInput + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + $ref: '#/components/requestBodies/UserArray' + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithListInput + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + $ref: '#/components/requestBodies/UserArray' + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: true + schema: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + - name: password + in: query + description: The password for login in clear text + required: true + schema: + type: string + responses: + '200': + description: successful operation + headers: + Set-Cookie: + description: >- + Cookie authentication key for use with the `api_key` + apiKey authentication. + schema: + type: string + example: AUTH_KEY=abcde12345; Path=/; HttpOnly + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when token expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + responses: + default: + description: successful operation + security: + - api_key: [] + '/user/{username}': + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + parameters: + - name: username + in: path + description: The name that needs to be fetched. Use user1 for testing. + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid user supplied + '404': + description: User not found + security: + - api_key: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Updated user object + required: true + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found + security: + - api_key: [] +externalDocs: + description: Find out more about Swagger + url: 'http://swagger.io' +components: + requestBodies: + UserArray: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + description: List of user object + required: true + Pet: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + api_key: + type: apiKey + name: api_key + in: header + schemas: + Order: + title: Pet Order + description: An order for a pets from the pet store + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + xml: + name: Order + Category: + title: Pet category + description: A category for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + xml: + name: Category + User: + title: a User + description: A User who is purchasing from the pet store + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + format: int32 + description: User Status + xml: + name: User + Tag: + title: Pet Tag + description: A tag for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + title: a Pet + description: A pet for sale in the pet store + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/components/schemas/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + $ref: '#/components/schemas/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + title: An uploaded response + description: Describes the result of uploading an image resource + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string diff --git a/parent/pom.xml b/parent/pom.xml index 92c4f131e8e..5bc8d6e1fb5 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -143,6 +143,7 @@ + 3.4.0 3.2.0 3.1.1 1.6 @@ -192,6 +193,11 @@ versions-maven-plugin ${version.plugin.versions} + + org.codehaus.mojo + build-helper-maven-plugin + ${version.plugin.build-helper} + From 32526dc1d1b4bed4a8fafc862144de22fed40a2d Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Thu, 23 May 2024 18:52:25 -0500 Subject: [PATCH 02/17] Copyright --- docs/src/main/resources/petstorex.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/resources/petstorex.yaml b/docs/src/main/resources/petstorex.yaml index eb02dc6e1c3..a44236ae73c 100644 --- a/docs/src/main/resources/petstorex.yaml +++ b/docs/src/main/resources/petstorex.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2023 Oracle and/or its affiliates. +# Copyright (c) 2024 Oracle and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 139b9f259e6bd986a7e2e5d7971a6774f8a736bd Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 29 Jul 2024 08:54:11 -0500 Subject: [PATCH 03/17] Refer to newer release of the OpenAPITools project --- docs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pom.xml b/docs/pom.xml index 0d104f44c93..8a253c62a6b 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -34,7 +34,7 @@ false 2.1.0 2.1.0 - 7.6.0 + 7.7.0 From 9cc40cbd6ce91cf7c7f5aee3de9db5678047671a Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 29 Jul 2024 17:42:44 -0500 Subject: [PATCH 04/17] Doc changes for OpenAPI Tools-based generation for Helidon 4 SE --- docs/pom.xml | 31 +- .../includes/openapi/openapi-generator.adoc | 21 +- .../se/openapi/openapi-generator.adoc | 271 +++++++++++++++++- .../ExampleOpenApiGenClientResource.java | 2 +- .../gen/server/api/AddPetOpCustom.java | 41 +++ .../gen/server/api/PetServiceImplCustom.java | 163 +++++++++++ 6 files changed, 518 insertions(+), 11 deletions(-) create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java diff --git a/docs/pom.xml b/docs/pom.xml index 8a253c62a6b..1829bc09e7c 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -176,7 +176,7 @@ parse-version - validate + initialize parsedHelidonVersion @@ -188,7 +188,7 @@ openapi-generator-maven-plugin - create-openapi-generator-project + create-openapi-generator-project-mp generate @@ -206,11 +206,36 @@ io.helidon.docs.openapi.gen.mp helidon-docs-openapi-gen-mp-client io.helidon.docs.openapi.gen.mp.client.api - io.helidon.docs.openapi.gen.model + io.helidon.docs.openapi.gen.mp.model io.helidon.docs.openapi.gen.mp.client + + create-openapi-generator-project-se-server-useabstractclass + + generate + + generate-sources + + java-helidon-server + se + ${project.basedir}/src/main/resources/petstorex.yaml + + helidonVersion=${parsedHelidonVersion.majorVersion} + useAbstractClass=true + + ${project.build.directory}/generated-sources/se/server + true + + io.helidon.docs.openapi.gen.se + helidon-docs-openapi-gen-se-server + io.helidon.docs.se.openapi.gen.server.api + io.helidon.docs.se.openapi.gen.model + io.helidon.docs.se.openapi.gen.server + + + diff --git a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc index f793fc31ba9..bc477bbef2a 100644 --- a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc @@ -307,7 +307,7 @@ Among the many configuration settings available to you, some you should particul |Version of Helidon for which to generate the files | {nbsp} -|`2.5.2` +|Latest published Helidon release * a|Affects: * Helidon version for the `` @@ -327,11 +327,13 @@ and the model classes. |`jackson` | |=== +* The generator attempts to retrieve the list of released Helidon versions from the Helidon website, falling back to locally-stored Java preferences loaded from the previous generator run, and as a last resort using hard-coded values for each major Helidon release. // end::config[] // tag::usage[] +// tag::usage-basics[] [[usage-section]] == Usage @@ -400,15 +402,24 @@ The generator replaces the generated classes or interfaces but does not touch ot The Helidon _client_ generator always creates concrete classes. Typically, you do not need to customize the behavior in the generated client API classes. If you choose to do so, write your own subclass of the generated client API class; _do not_ modify the generated files. -==== Grouping Operations into "APIs" +// end::usage-basics[] +// tag::usage-grouping-intro[] + +==== Grouping Operations into APIs Each operation in an OpenAPI document can have a `tags` attribute. -The generators group operations with the same `tags` value into the same API. +By default, the generators group operations with the same `tags` value into the same API or service. Alternatively, if you specify the option `x-helidon-groupBy` as `first-path-segment`, the generators use the first segment of the path to group operations together. +// end::usage-grouping-intro[] +// tag::usage-grouping-server[] When you generate a Helidon {flavor-uc} server, the generator creates a separate interface or class for each API your service _exposes_. You implement each interface or extend each class to add your business logic for that API. +// end::usage-grouping-server[] +// tag::usage-grouping-client[] When you generate a Helidon {flavor-uc} client, the generated code contains a separate API class for each distinct API your code might _invoke_. +// end::usage-grouping-client[] +// tag::usage-after-grouping[] [[usage-running]] === Running the OpenAPI Generators @@ -522,6 +533,7 @@ Before you use the online generator, consider whether any of the input you provi This document does not explore further the use of the online generator. +// end::usage-after-grouping[] // end::usage[] @@ -536,8 +548,9 @@ The Helidon generators go a long way in helping you write your client or server. // tag::using-generated-code-server-intro[] === Completing the Server -Recall from earlier how the OpenAPI generator gathers operations into one or more "APIs" and generates either a class or an interface--your choice--for each API. +Recall from earlier how the OpenAPI generator gathers operations into one or more APIs or services and generates either an abstract class or an interface--your choice--for each API. You need to extend each generated API class or implement each generated API interface by writing your own classes. + Any input parameters to the endpoints are expressed as POJO model objects or Java types, as declared in the OpenAPI document. Your server code uses each of the input parameters to accomplish whatever business purpose that endpoint is responsible for, possibly returning a result as a POJO or Java type as indicated for that operation in the OpenAPI document. // end::using-generated-code-server-intro[] diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index 3773c1dd516..0e70b883e08 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -23,9 +23,274 @@ :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] +:incdir: {rootdir}/includes/openapi +:gen-inc: {incdir}/openapi-generator.adoc +:petServiceImpl: {sourcedir}/se/openapi/gen/server/api/PetServiceImplCustom.java +:addPetOpCustom: {sourcedir}/se/openapi/gen/server/api/AddPetOpCustom.java -== Coming Soon +:helidon-client-xref: {webclient-page} -The support for link:{openapi-generator-tool-site-url}[OpenAPI generator] is not available yet. +include::{gen-inc}[tag=preamble] -You can track the progress for this feature here: https://github.com/helidon-io/helidon/issues/7943. +include::{gen-inc}[tags=intro;coords;config;usage-basics;usage-grouping-intro;usage-grouping-server] + +The generator creates Helidon routing logic based on the longest common path prefix shared among the operations that are grouped into each API. + +[NOTE] +==== +If the operations in an API have no common prefix then the generated routing will be inefficient at runtime. +The generator logs a warning and includes a `TODO` comment in the generated routing. + +Review the paths and the `tags` settings in your OpenAPI document and consider revising one or the other so all operations in each API share a common path prefix. If you do not have control over the OpenAPI document or do not want to change it, consider specifying the generator option `x-helidon-groupBy first-path-segment` which groups operations into APIs not by `tags` value but by the first segment of each operation's path. +==== + +include::{gen-inc}[tags=usage-grouping-client;usage-after-grouping;using-generated-code-intro;using-generated-code-server-intro] + +If you choose to generate interfaces for the APIs, the generator creates routing rules for the API services it generates but you write virtually all of the logic to process incoming requests by implementing the very short methods generated in the implementation class. + +The rest of this sections focuses on your next steps if, on the other hand, you decide to generate abstract classes. + +==== What you _must_ do: implement your business logic and send the response +The generator creates an implementation class as well as the abstract class for each API. +The implementation class contains a `handle` method for each API operation with a very simple method body that returns a not-yet-implemented HTTP status in the response. The following example shows the generated method for the `addPet` OpenAPI operation. +[source,java] +.The generated `handleAddPet` method in the `PetApiImpl` class +---- +include::{petServiceImpl}/[tags=class-header;handleAddPetDecl;handleAddPetGenerated;handleAddPetEnd, indent=0] +} +---- + +Customize the class to manage the pets and revise the method to save the new pet and send the correct response, as shown next. +[source,java] +.The customized `handleAddPet` method in the `PetApiImpl` class +---- +include::{petServiceImpl}/[tags=class-header;handleAddPetDecl;addedFields;handleAddPetCustom;handleAddPetEnd, indent=0] +} +---- +<1> Business logic: create a very simple data store - a real app would use a database. +<2> Business logic: make sure the pet being added does not already exist. Send the invalid request status code if it does. +<3> Business logic: add the pet to the data store. +<4> Prepare and send the `200` response. + +If a response has any _required_ response parameters you would pass them as parameters to the `builder` method. +Add _optional_ response parameters using other generated builder methods. +The following example illustrates this for the `findPetsByTags` operation and its `response` output parameter. +[source,java] +.The customized `findPetsByTags` method in the `PetApiImpl` class +---- +include::{petServiceImpl}/[tags=class-header;addedFields;handleFindPetsByTagsDecl;handleFindPetsByTagsCustom;handleFindPetsByTagsEnd, indent=0] +} +---- +<1> Uses the same data store as in the earlier example. +<2> The `tags` parameter conveys the tag values to be matched in selecting pets to report. Other generated code extracts the runtime argument's value from the request and then automatically passes it to the method. +<3> Collects all pets with any tag that matches any of the selection tags passed in. +<4> Uses the generated `Response200` to prepare the response. +<5> Assigns the optional `response` output parameter--the list of matching `Pet` objects. +<6> Send the response using the prepared response information. + +Write each of the `handleXxx` methods appropriately so they implement the business logic you need and send the response. + +The generator creates a `ResponseNNN` Java `record` for each operation response status code `NNN` that is declared in the OpenAPI document. You can return other status values with other output parameters even if they are not declared in the OpenAPI document, but your code must prepare the `ServerResponse` entirely by itself; the generator cannot generate helper records for responses that are absent from the document. + +==== What you _can_ do: override the generated behavior +Generated code takes care of the following work: + +* Route each request to the method which should respond. +* Extract each incoming parameter from the request and convert it to the correct type, applying any validation declared in the OpenAPI document. +* Pass the extracted parameters to the developer-written `handleXxx` method. +* Assemble required and optional response parameters and send the response. + +You can override any of the generated behavior by adding code to the generated API implementation class you are already editing to customize the `handleXxx` methods and by writing new classes which extend some of the generated classes. + +===== Override routing +To change the way routing occurs, simply override the `routing` method in your `PetServiceImpl` class. Make sure your custom routing handles all the paths for which the API is responsible. + +===== Override how to extract one or more parameters from a request +For each operation in an API the generator creates an inner class and, for each incoming parameter for that operation, a method which extracts and validates the parameter. +Override how a parameter is extracted by following these steps, using the `AddPetOp` as an example. + +. Write a class which extends the inner class for the operation. ++ +.Customized `AddPetOp` class +[source,java] +---- +include::{addPetOpCustom}[tag=classDecl] +---- +. In that subclass override the relevant method. ++ +.Customized `pet` method for extracting and validating the `pet` parameter +[source,java] +---- +include::{addPetOpCustom}[tag=petMethod] +---- +<1> Extracts the parameter from the request. This happens to use the same logic as in the generated method but you can customize that as well if you need to. +<2> Apply any relevant validations. This silly but illustrative example rejects any pet name that starts with a lower-case letter. +<3> Return the extracted value, properly typed. +. In the implementation class for the API (`PetServiceImpl`) override the `createAddPetOp` method so it returns an instance of your new subclass `AddPetOpCustom` of the operation inner class `AddPetOp`. ++ +[source,java] +.Providing your custom implementation of `AddPet` +---- +include::{petServiceImpl}[tag=createAddPetOp] +---- + +===== Override how an operation is prepared from a request +The generated abstract class contains a method named for each operation declared in the OpenAPI document (`addPet`) which accepts the Helidon request and response as parameters. +The generated code in these methods invokes the code to extract each incoming parameter from the request, perform any declared validation on them, and pass them to the developer-written method (`handleAddPet(request, response, pet)`). + +To completely change this behavior, override the `addPet` method in the `PetServiceImpl` class to do what you need. + +include::{gen-inc}[tag=using-generated-code-client-intro] + +The generated Helidon SE client includes the class `ApiClient`. This class corresponds to +the Helidon link:{webclient-javadoc-base-url}.api/io/helidon/webclient/api/WebClient.html[`WebClient`] and represents the +connection between your code and the remote server. The generator also creates one or more `Api` interfaces and +corresponding implementation classes. The examples below use the `PetApi` interface and the `PetApiImpl` class. + +To invoke the remote service your code must: + +. Create an instance of `ApiClient` using an `ApiClient.Builder`. +. Use that `ApiClient` instance to instantiate a `PetApi` object. +. Invoke the methods on the `PetApi` object to access the remote services and then retrieve the returned result value. + +The following sections explain these steps. + +==== Creating an `ApiClient` Instance +The Helidon SE client generator gives you as much flexibility as you need in connecting to the remote service. + +Internally, the `ApiClient` uses a Helidon `WebClient` object to contact the remote system. +The `ApiClient.Builder` automatically prepares a Helidon +link:{webclient-javadoc-base-url}.api/io/helidon/webclient/api/WebClientConfig.Builder.html[`WebClientConfig.Builder`] +object using information from the OpenAPI document. + +The next sections describe, from simplest to most complicated, the ways your code can create an `ApiClient` instance, each involving increased involvement with the `WebClient.Builder` object. + +===== Accepting the Automatic `WebClient.Builder` +In the simplest case, your code can get an `ApiClient` instance directly. + +[source,java] +.Creating an `ApiClient` instance - simple case +---- +ApiClient apiClient = ApiClient.builder().build(); +---- + +Your code relies fully on the automatic `WebClient.Builder`. +In many cases, this approach works very well, especially if the OpenAPI document correctly declares the servers and their URIs. + +===== Influencing the Automatic `WebClient.Builder` +Your code can use the `ApiClient.Builder` to fine-tune the settings for the internal `WebClient.Builder`. +For instance, your code can set an object mapper to be used for Jackson processing or the `JsonbConfig` object to be used for JSON-B processing, depending on which serialization library you chose when you ran the generator. + +Your code does not need to know how the object mapper setting is conveyed to the internal `WebClient.Builder`. The `ApiClient.Builder` knows how to do that. + +[source,java] +.Creating an `ApiClient` instance - influencing the `ApiClient.Builder` +---- +ApiClient apiClient = apiClient.builder() + .objectMapper(yourCustomMapper) + .build(); +---- + +===== Adjusting the Automatic `WebClient.Builder` +In more complicated situations, your code can adjust the settings of the `WebClient.Builder` the `ApiClient.Builder` creates. + +[source,java] +.Creating an `ApiClient` instance - adjusting the `WebClient.Builder` +---- +ApiClient.Builder apiClientBuilder = ApiClient.builder(); + +apiClientBuilder.webClientBuilder() // <1> + .connectTimeout(4, TimeUnit.SECONDS); // <2> + +ApiClient apiClient = apiClientBuilder.build(); // <3> +---- +<1> Access the `ApiClient.Builder`'s automatic `WebClient.Builder` instance. +<2> Adjust a setting of the `WebClient.Builder` directly. +<3> Build the `ApiClient` which implicitly builds the `WebClient` from the now-adjusted internal `WebClient.Builder`. + +The automatic `WebClient.Builder` retains information derived from the OpenAPI document unless your code overrides those specific settings. + +===== Providing a Custom `WebClient.Builder` +Lastly, you can construct the `WebClient.Builder` entirely yourself and have the `ApiClient.Builder` use it instead of its own internal builder. + +[source,java] +.Creating an `ApiClient` instance - using a custom `WebClient.Builder` +---- +WebClient.Builder customWebClientBuilder = WebClient.builder() + .connectTimeout(3, TimeUnit.SECONDS) + .baseUri("https://myservice.mycompany.com"); + +ApiClient apiClient = ApiClient.builder() + .webClientBuilder(customWebClientBuilder) + .build(); +---- +Note that this approach entirely replaces the internal, automatically-prepared `WebClient.Builder` with yours; it _does not_ merge the new builder with the internal one. In particular, any information from the OpenAPI document the generator used to prepare the internal `WebClient.Builder` is lost. + + +==== Creating a `PetApi` Instance +The `ApiClient` represents the connection to the remote server but not the individual RESTful operations. +Each generated `xxxApi` interface exposes a method for each operation declared in the OpenAPI document associated with that API via its `tags` value. +By example, the `PetApi` interface exposes a method for each operation in the OpenAPI document that pertains to pets. + +To invoke an operation defined on the `PetApi` interface, your code instantiates a `PetApi` using an `ApiClient` object: + +[source,java] +.Preparing the PetStore Client API +---- +ApiClient apiClient = ApiClient.builder().build(); +PetApi petApi = PetApiImpl.create(apiClient); +---- + +==== Invoking Remote Endpoints +With the `petApi` object, your code can invoke any of the methods on the `PetApi` interface to contact the remote service. + +The Helidon SE client generator creates an `ApiResponse` interface. +Each generated `PetApi` method returns an `ApiResponse` where the `returnType` is the return type declared in the OpenAPI document for the corresponding operation. + +The `ApiResponse` interface exposes two methods your code can use to work with the response from the remote service invocation: + +* `T result()` ++ +Provides access to the value returned by the remote service in the response. +This method lets your code fetch the return value directly. +* `HTTPClientResponse webClientResponse()` ++ +Provides access to the Helidon `HTTPClientResponse` object. +Your code can find out the HTTP return status, read headers in the response, and process the content (if any) in the response however it needs to. + +In the Helidon WebClient model, the first part of the response message can arrive (the status and headers are available) before the entity in the body of the response is readable. +So there are two events associated with an incoming HTTP response: + +. when the response _excluding_ the entity content has arrived, and +. when your code can begin consuming the entity content. + +You can adopt different styles of retrieving the results, depending on the specific needs of the code you are writing. + +===== Access with status checking +The Helidon WebClient programming model includes a `HTTPClientResponse` interface which exposes all aspects of the HTTP response returned from the remote service. + +The next example shows how your code can use the `HTTPClientResponse`. + +[source,java] +.Access with status checking +---- +ApiResponse> apiResponse = petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> + +HTTPClientResponse webClientResponse = apiResponse.webClientResponse(); // <2> + +if (webClientResponse.status().code() != 200) { // <3> + // Handle a non-successful status. +} + +List> availablePets = apiResponse.result(); // <4> +---- +<1> Start the remote service invocation. +<2> Wait for the HTTP response status and headers to arrive. +<3> Check the status in the HTTP response. +<4> Wait for the content to arrive subject to a four-second timeout. + +This code also blocks the current thread, first to wait for the initial response and then to wait for the result content. + +include::{gen-inc}[tag=common-references] + +* xref:{helidon-client-xref}[Helidon WebClient documentation] \ No newline at end of file diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java b/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java index 364c3327621..6b9df15a6b5 100644 --- a/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java +++ b/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java @@ -22,7 +22,7 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; -import io.helidon.docs.openapi.gen.model.Pet; +import io.helidon.docs.openapi.gen.mp.model.Pet; import io.helidon.docs.openapi.gen.mp.client.api.ApiException; import io.helidon.docs.openapi.gen.mp.client.api.PetApi; diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java new file mode 100644 index 00000000000..d3c490096f4 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.server.api; + +import io.helidon.docs.se.openapi.gen.model.Pet; +import io.helidon.webserver.http.ServerRequest; + +// tag::classDecl[] +public class AddPetOpCustom extends PetService.AddPetOp { +// end::classDecl[] + +// tag::petMethod[] + @Override + protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { + Pet result = request.content().hasEntity() // <1> + ? request.content().as(Pet.class) + : null; + + /* + Insist that pet names never start with a lower-case letter. + */ + if (result != null) { + validator.validatePattern("pet", result.getName(), "[^a-z].*"); // <2> + } + return result; // <3> + } +// end::petMethod[] +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java new file mode 100644 index 00000000000..b5fd91b9141 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.server.api; + +import java.util.ArrayList; +import java.util.stream.Collectors; +import java.io.File; +import java.util.HashMap; + +import io.helidon.http.HeaderNames; +import io.helidon.http.Headers; +import java.util.HexFormat; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import io.helidon.http.media.multipart.MultiPart; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Optional; +import io.helidon.common.parameters.Parameters; +import io.helidon.docs.se.openapi.gen.model.Pet; +import io.helidon.http.media.multipart.ReadablePart; +import java.util.Set; +import io.helidon.http.Status; +import java.io.UncheckedIOException; +import io.helidon.common.mapper.Value; +import io.helidon.webserver.http.ServerRequest; +import io.helidon.webserver.http.ServerResponse; + +// Use a wrapper class so we can use the actual name of the generated class on the +// inner class. If we name this outer class PetServiceImpl that conflicts with the +// file of the same name (and in the same package) that the generator created. +public class PetServiceImplCustom { +// tag::class-declaration[] +// tag::class-header[] +public class PetServiceImpl extends PetService { +// end::class-header[] +// tag::addedFields[] + + private final Map pets = new HashMap<>(); // <1> + +// end::addedFields[] +// tag::handleAddPetDecl[] + @Override + protected void handleAddPet(ServerRequest request, ServerResponse response, + Pet pet) { +// end::handleAddPetDecl[] +// tag::handleAddPetGenerated[] + response.status(Status.NOT_IMPLEMENTED_501).send(); +// end::handleAddPetGenerated[] +// tag::handleAddPetCustom[] + if (pets.containsKey(pet.getId())) { // <2> + AddPetOp.Response405.builder().send(response); + } + pets.put(pet.getId(), pet); // <3> + AddPetOp.Response200.builder().send(response); // <4> +// end::handleAddPetCustom[] +// tag::handleAddPetEnd[] + } +// end::handleAddPetEnd[] + + @Override + protected void handleDeletePet(ServerRequest request, ServerResponse response, + Long petId, + Optional apiKey) { + + response.status(Status.NOT_IMPLEMENTED_501).send(); + } + + @Override + protected void handleFindPetsByStatus(ServerRequest request, ServerResponse response, + List status) { + + response.status(Status.NOT_IMPLEMENTED_501).send(); + } + +// tag::handleFindPetsByTagsDecl[] + @Override + protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, + List tags) { // <2> +// end::handleFindPetsByTagsDecl[] +// tag::handleFindPetsByTagsCustom[] + + List result = pets.values().stream() + .filter(pet -> pet.getTags() + .stream() + .anyMatch(petTag -> tags.contains(petTag.getName()))) + .toList(); // <3> + + FindPetsByTagsOp.Response200.builder() // <4> + .response(result) // <5> + .send(response); // <6> + +// end::handleFindPetsByTagsCustom[] + response.status(Status.NOT_IMPLEMENTED_501).send(); +// tag::handleFindPetsByTagsEnd[] + } +// end::handleFindPetsByTagsEnd[] + +// tag::createAddPetOp[] + @Override + protected AddPetOp createAddPetOp() { + return new AddPetOpCustom(); + } +// end::createAddPetOp[] + @Override + protected void handleGetPetById(ServerRequest request, ServerResponse response, + Long petId) { + + response.status(Status.NOT_IMPLEMENTED_501).send(); + } + + @Override + protected void handlePetInfoWithCookies(ServerRequest request, ServerResponse response, + Long petId, + Long transactionId, + String label) { + + response.status(Status.NOT_IMPLEMENTED_501).send(); + } + + @Override + protected void handleUpdatePet(ServerRequest request, ServerResponse response, + Pet pet) { + + response.status(Status.NOT_IMPLEMENTED_501).send(); + } + + @Override + protected void handleUpdatePetWithForm(ServerRequest request, ServerResponse response, + Long petId, + Optional name, + Optional status) { + + response.status(Status.NOT_IMPLEMENTED_501).send(); + } + + @Override + protected void handleUploadFile(ServerRequest request, ServerResponse response, + Long petId, + Optional additionalMetadata, + Optional _file) { + + response.status(Status.NOT_IMPLEMENTED_501).send(); + } + +}} + + +// end::class-declaration[] From d816e0149f5e75ea6d253f909cb7a5094615ac21 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Tue, 30 Jul 2024 09:06:41 -0500 Subject: [PATCH 05/17] Slight update to the generator doc --- .../asciidoc/includes/openapi/openapi-generator.adoc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc index bc477bbef2a..15b19dcd891 100644 --- a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc @@ -234,7 +234,7 @@ You must specify the following options: |=== === Recommended Settings for the OpenAPI Generator -Your project might have different needs, but in general we advise developers to use the following settings when using the OpenAPI generator. +Your project might have different needs, but in general we advise developers to use the following settings when using the OpenAPI generator, both from the command line and using the Maven plug-in. .Recommended OpenAPI Generator Additional Properties @@ -269,6 +269,10 @@ Your project might have different needs, but in general we advise developers to | `artifactVersion` | Artifact version in the generated `pom.xml` | `1.0.0` + +| `useAbstractClass` +| Generate server abstract classes instead of interfaces. Setting to `true` generates significantly more helpful code. +| `false` |=== [NOTE] @@ -313,6 +317,7 @@ a|Affects: * Helidon version for the `` * Dependencies (`javax` vs. `jakarta`) * `java import` statements in generated code (`javax` vs. `jakarta`) +* Which Helidon APIs are used (3.x vs. 4.x, for example) |`fullProject` |Whether to generate all the normal files or only API files @@ -551,7 +556,7 @@ The Helidon generators go a long way in helping you write your client or server. Recall from earlier how the OpenAPI generator gathers operations into one or more APIs or services and generates either an abstract class or an interface--your choice--for each API. You need to extend each generated API class or implement each generated API interface by writing your own classes. -Any input parameters to the endpoints are expressed as POJO model objects or Java types, as declared in the OpenAPI document. Your server code uses each of the input parameters to accomplish whatever business purpose that endpoint is responsible for, possibly returning a result as a POJO or Java type as indicated for that operation in the OpenAPI document. +Any input parameters to the operations are expressed as POJO model objects or Java types as declared in the OpenAPI document. You write server code to use each of the input parameters to accomplish whatever business purpose that operation is responsible for, possibly returning a result as a POJO or Java type as indicated for that operation in the OpenAPI document. // end::using-generated-code-server-intro[] In some cases, you might need more control over the response sent to the client. In that case, specify the additional property `returnResponse=true` when you run the Helidon server generator. The return type for the generated methods is From 851b4525cea5f8394d586255bcdb6c2e98d9f8c4 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Tue, 30 Jul 2024 12:23:23 -0500 Subject: [PATCH 06/17] Convert 'using client' examples to use snippets --- docs/pom.xml | 31 ++++- .../se/openapi/openapi-generator.adoc | 102 +++++++++-------- .../se/openapi/gen/client/ExampleClient.java | 108 ++++++++++++++++++ .../gen/server/api/AddPetOpCustom.java | 2 +- .../gen/server/api/PetServiceImplCustom.java | 2 +- 5 files changed, 195 insertions(+), 50 deletions(-) create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java diff --git a/docs/pom.xml b/docs/pom.xml index 1829bc09e7c..83485426f37 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -231,11 +231,40 @@ io.helidon.docs.openapi.gen.se helidon-docs-openapi-gen-se-server io.helidon.docs.se.openapi.gen.server.api - io.helidon.docs.se.openapi.gen.model + io.helidon.docs.se.openapi.gen.server.model io.helidon.docs.se.openapi.gen.server + + create-openapi-generator-project-se-client + + generate + + generate-sources + + java-helidon-client + se + ${project.basedir}/src/main/resources/petstorex.yaml + + helidonVersion=${parsedHelidonVersion.majorVersion} + + openApiNullable=false + + ${project.build.directory}/generated-sources/se/client + true + + io.helidon.docs.openapi.gen.se + helidon-docs-openapi-gen-se-client + io.helidon.docs.se.openapi.gen.client.api + io.helidon.docs.se.openapi.gen.client.model + io.helidon.docs.se.openapi.gen.client + + + diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index 0e70b883e08..7ee8135371a 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -27,6 +27,7 @@ include::{rootdir}/includes/se.adoc[] :gen-inc: {incdir}/openapi-generator.adoc :petServiceImpl: {sourcedir}/se/openapi/gen/server/api/PetServiceImplCustom.java :addPetOpCustom: {sourcedir}/se/openapi/gen/server/api/AddPetOpCustom.java +:exampleClient: {sourcedir}/se/openapi/gen/client/ExampleClient.java :helidon-client-xref: {webclient-page} @@ -163,68 +164,71 @@ The `ApiClient.Builder` automatically prepares a Helidon link:{webclient-javadoc-base-url}.api/io/helidon/webclient/api/WebClientConfig.Builder.html[`WebClientConfig.Builder`] object using information from the OpenAPI document. -The next sections describe, from simplest to most complicated, the ways your code can create an `ApiClient` instance, each involving increased involvement with the `WebClient.Builder` object. +The next sections describe, from simplest to most complicated, the ways your code can create an `ApiClient` instance, each involving increased involvement with the `WebClientConfig.Builder` object. -===== Accepting the Automatic `WebClient.Builder` +===== Accepting the Automatic `WebClientConfig.Builder` In the simplest case, your code can get an `ApiClient` instance directly. [source,java] .Creating an `ApiClient` instance - simple case ---- -ApiClient apiClient = ApiClient.builder().build(); +include::{exampleClient}[tags=classDecl;initDecl;defaultApiClient;initEnd;classEnd] ---- +<1> The same `ApiClient` instance can be reused to invoke multiple APIs handled by the same server. +<2> Creates an `ApiClient` instance using default settings from the OpenAPI document. -Your code relies fully on the automatic `WebClient.Builder`. +Your code relies fully on the automatic `WebClient`. In many cases, this approach works very well, especially if the OpenAPI document correctly declares the servers and their URIs. -===== Influencing the Automatic `WebClient.Builder` -Your code can use the `ApiClient.Builder` to fine-tune the settings for the internal `WebClient.Builder`. +===== Influencing the Automatic `WebClientConfig.Builder` +Your code can use the `ApiClient.Builder` to fine-tune the settings for the internal `WebClientConfig.Builder`. For instance, your code can set an object mapper to be used for Jackson processing or the `JsonbConfig` object to be used for JSON-B processing, depending on which serialization library you chose when you ran the generator. -Your code does not need to know how the object mapper setting is conveyed to the internal `WebClient.Builder`. The `ApiClient.Builder` knows how to do that. +Your code does not need to know how the object mapper setting is conveyed to the internal `WebClientConfig.Builder`. The `ApiClient.Builder` knows how to do that. [source,java] .Creating an `ApiClient` instance - influencing the `ApiClient.Builder` ---- -ApiClient apiClient = apiClient.builder() - .objectMapper(yourCustomMapper) - .build(); +include::{exampleClient}[tags=classDecl;initDecl;apiClientWithObjMapper;initEnd;classEnd] ---- +<1> Stores a reusable `ApiClient`. +<2> A real app would fully set up the `ObjectMapper`. +<3> Sets the object mapper for use in the `ApiClient.Builder` 's internal `WebClientConfig.Builder`. -===== Adjusting the Automatic `WebClient.Builder` -In more complicated situations, your code can adjust the settings of the `WebClient.Builder` the `ApiClient.Builder` creates. +===== Adjusting the Automatic `WebClientConfig.Builder` +In more complicated situations, your code can adjust the settings of the `WebClientConfig.Builder` which the `ApiClient.Builder` creates. [source,java] -.Creating an `ApiClient` instance - adjusting the `WebClient.Builder` +.Creating an `ApiClient` instance - adjusting the `WebClientConfig.Builder` ---- -ApiClient.Builder apiClientBuilder = ApiClient.builder(); - -apiClientBuilder.webClientBuilder() // <1> - .connectTimeout(4, TimeUnit.SECONDS); // <2> - -ApiClient apiClient = apiClientBuilder.build(); // <3> +include::{exampleClient}[tags=classDecl;initDecl;apiClientWithAdjustedBuilder;initEnd;classEnd] ---- -<1> Access the `ApiClient.Builder`'s automatic `WebClient.Builder` instance. -<2> Adjust a setting of the `WebClient.Builder` directly. -<3> Build the `ApiClient` which implicitly builds the `WebClient` from the now-adjusted internal `WebClient.Builder`. +<1> Stores a reusable `AppClient`. +<2> Creates a new `AppClient` builder. +<3> Access the `ApiClient.Builder`'s automatic `WebClientConfig.Builder` instance. +<4> Adjusts a setting of the `WebClientConfig.Builder` directly. +<5> Builds the `ApiClient` which implicitly builds the `WebClient` from the now-adjusted internal `WebClientConfig.Builder`. -The automatic `WebClient.Builder` retains information derived from the OpenAPI document unless your code overrides those specific settings. +The automatic `WebClientConfig.Builder` retains information derived from the OpenAPI document unless your code overrides those specific settings. -===== Providing a Custom `WebClient.Builder` -Lastly, you can construct the `WebClient.Builder` entirely yourself and have the `ApiClient.Builder` use it instead of its own internal builder. +===== Providing a Custom `WebClientConfig.Builder` +Lastly, you can construct the `WebClientConfig.Builder` entirely yourself and have the `ApiClient.Builder` use it instead of its own internal builder. [source,java] -.Creating an `ApiClient` instance - using a custom `WebClient.Builder` +.Creating an `ApiClient` instance - using a custom `WebClientConfig.Builder` ---- -WebClient.Builder customWebClientBuilder = WebClient.builder() - .connectTimeout(3, TimeUnit.SECONDS) - .baseUri("https://myservice.mycompany.com"); - -ApiClient apiClient = ApiClient.builder() - .webClientBuilder(customWebClientBuilder) - .build(); +include::{exampleClient}[tags=classDecl;initDecl;apiClientWithCustomWebClientBuilder;initEnd;classEnd] ---- -Note that this approach entirely replaces the internal, automatically-prepared `WebClient.Builder` with yours; it _does not_ merge the new builder with the internal one. In particular, any information from the OpenAPI document the generator used to prepare the internal `WebClient.Builder` is lost. +<1> Stores a reusable `AppClient`. +<2> Creates a new `WebClientConfig.Builder`. +<3> Sets the connection timeout directly on the `WebClientConfig.Builder`. +<4> Sets the base URI on the `WebClienConfig.Builder`. +<5> Creates a new `ApiClient.Builder'. +<6> Sets the `WebClientConfig.Builder` which the `ApiClient.Builder` should use (instead of the one it prepares internally). +<7> Builds the `ApiClient` which uses the newly-assigned `WebClientConfig.Builder` in the process. + + +Note that this approach entirely replaces the internal, automatically-prepared `WebClientConfig.Builder` with yours; it _does not_ merge the new builder with the internal one. In particular, any information from the OpenAPI document the generator used to prepare the internal `WebClientConfig.Builder` is lost. ==== Creating a `PetApi` Instance @@ -237,15 +241,18 @@ To invoke an operation defined on the `PetApi` interface, your code instantiates [source,java] .Preparing the PetStore Client API ---- -ApiClient apiClient = ApiClient.builder().build(); -PetApi petApi = PetApiImpl.create(apiClient); +include::{exampleClient}[tags=classDeclFull;prepPetApi;classEnd] ---- +<1> Stores a reusable `AppClient`. +<2> Stores a reusable `PetApi` for invoking pet-related operations. +<3> Initializes and saves the `PetApi` instance using the previously-prepared `apiClient`. + ==== Invoking Remote Endpoints With the `petApi` object, your code can invoke any of the methods on the `PetApi` interface to contact the remote service. The Helidon SE client generator creates an `ApiResponse` interface. -Each generated `PetApi` method returns an `ApiResponse` where the `returnType` is the return type declared in the OpenAPI document for the corresponding operation. +Each generated `PetApi` method returns an `ApiResponse` where the `returnType` is the return type (if any) declared in the OpenAPI document for the corresponding operation. The `ApiResponse` interface exposes two methods your code can use to work with the response from the remote service invocation: @@ -266,6 +273,15 @@ So there are two events associated with an incoming HTTP response: You can adopt different styles of retrieving the results, depending on the specific needs of the code you are writing. +===== Access only the result +[source,java] +.Access with only result access +---- +include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableUseResult;findAvailableEnd] +---- +<1> Use the previously-prepared `petApi` to find pets that have the `available` status. +<2> Retrieve the typed result from the `ApiResponse`. + ===== Access with status checking The Helidon WebClient programming model includes a `HTTPClientResponse` interface which exposes all aspects of the HTTP response returned from the remote service. @@ -274,20 +290,12 @@ The next example shows how your code can use the `HTTPClientResponse`. [source,java] .Access with status checking ---- -ApiResponse> apiResponse = petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> - -HTTPClientResponse webClientResponse = apiResponse.webClientResponse(); // <2> - -if (webClientResponse.status().code() != 200) { // <3> - // Handle a non-successful status. -} - -List> availablePets = apiResponse.result(); // <4> +include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableResponseCheck;findAvailableEnd] ---- <1> Start the remote service invocation. <2> Wait for the HTTP response status and headers to arrive. <3> Check the status in the HTTP response. -<4> Wait for the content to arrive subject to a four-second timeout. +<4> Wait for the content to arrive, extracting the result and converting it to the proper type. This code also blocks the current thread, first to wait for the initial response and then to wait for the result content. diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java new file mode 100644 index 00000000000..ebbe6cf9ff5 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.client; + +import java.time.Duration; +import java.util.List; + +import io.helidon.docs.se.openapi.gen.client.api.PetApi; +import io.helidon.docs.se.openapi.gen.client.api.PetApiImpl; +import io.helidon.docs.se.openapi.gen.client.model.Pet; +import io.helidon.webclient.api.HttpClientResponse; +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.api.WebClientConfig; + +import com.fasterxml.jackson.databind.ObjectMapper; + +// tag::classDeclFull[] +// tag::classDecl[] +public class ExampleClient { + + private ApiClient apiClient; // <1> + +// end::classDecl[] + private PetApi petApi; // <2> + +// end::classDeclFull[] +// tag::initDecl[] + void init() { +// end::initDecl[] +// tag::defaultApiClient[] + ApiClient apiClient = ApiClient.builder().build(); // <2> +// end::defaultApiClient[] +// tag::apiClientWithObjMapper[] + ObjectMapper myObjectMapper = null; // <2> + + apiClient = ApiClient.builder() + .objectMapper(myObjectMapper) // <3> + .build(); +// end::apiClientWithObjMapper[] + +// tag::apiClientWithAdjustedBuilder[] + ApiClient.Builder apiClientAdjustedBuilder = ApiClient.builder(); // <2> + + apiClientAdjustedBuilder + .webClientBuilder() // <3> + .connectTimeout(Duration.ofSeconds(4)); // <4> + + apiClient = apiClientAdjustedBuilder.build(); // <5> +// end::apiClientWithAdjustedBuilder[] +// tag::apiClientWithCustomWebClientBuilder[] + WebClientConfig.Builder customWebClientBuilder = WebClient.builder()// <2> + .connectTimeout(Duration.ofSeconds(3)) // <3> + .baseUri("https://myservice.mycompany.com"); // <4> + + apiClient = ApiClient.builder() // <5> + .webClientBuilder(customWebClientBuilder) // <6> + .build(); // <7> +// end::apiClientWithCustomWebClientBuilder[] +// tag::initEnd[] + } +// end::initEnd[] + +// tag::prepPetApi[] + void preparePetApi() { + petApi = PetApiImpl.create(apiClient); // <3> + } +// end::prepPetApi[] +// tag::findAvailableDecl[] + void findAvailablePets() { +// end::findAvailableDecl[] +// tag::findAvailableSimple[] + ApiResponse> apiResponse = + petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> +// end::findAvailableSimple[] +// tag::findAvailableResponseCheck[] + + try (HttpClientResponse webClientResponse = apiResponse.webClientResponse()) { // <2> + if (webClientResponse.status().code() != 200) { // <3> + // Handle a non-successful status. + } + } + + List avlPets = apiResponse.result(); // <4> +// end::findAvailableResponseCheck[] +// tag::findAvailableUseResult[] + + List availablePets = apiResponse.result(); // <2> +// end::findAvailableUseResult[] +// tag::findAvailableEnd[] + } +// end::findAvailableEnd[] + +// tag::classEnd[] +} +// end::classEnd[] diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java index d3c490096f4..6db3c95273f 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java @@ -15,7 +15,7 @@ */ package io.helidon.docs.se.openapi.gen.server.api; -import io.helidon.docs.se.openapi.gen.model.Pet; +import io.helidon.docs.se.openapi.gen.server.model.Pet; import io.helidon.webserver.http.ServerRequest; // tag::classDecl[] diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java index b5fd91b9141..5368699265f 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java @@ -31,7 +31,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Optional; import io.helidon.common.parameters.Parameters; -import io.helidon.docs.se.openapi.gen.model.Pet; +import io.helidon.docs.se.openapi.gen.server.model.Pet; import io.helidon.http.media.multipart.ReadablePart; import java.util.Set; import io.helidon.http.Status; From 7990c15c002c824c372636659df98985eba3ebf1 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 2 Aug 2024 15:02:24 -0500 Subject: [PATCH 07/17] Convert from generating temporary projects to mocking --- docs/pom.xml | 93 ------------- .../ExampleOpenApiGenClientResource.java | 11 +- .../java/io/helidon/docs/mp/openapi/README.md | 17 +++ .../io/helidon/docs/se/openapi/gen/README.md | 28 ++++ .../docs/se/openapi/gen/client/ApiClient.java | 44 ++++++ .../se/openapi/gen/client/ApiResponse.java | 29 ++++ .../se/openapi/gen/client/ExampleClient.java | 2 +- .../se/openapi/gen/client/api/PetApi.java | 26 ++++ .../se/openapi/gen/client/api/PetApiImpl.java | 34 +++++ .../docs/se/openapi/gen/model/Pet.java | 41 ++++++ .../docs/se/openapi/gen/model/Tag.java | 23 ++++ .../gen/server/api/AddPetOpCustom.java | 2 +- .../se/openapi/gen/server/api/PetService.java | 125 ++++++++++++++++++ .../gen/server/api/PetServiceImplCustom.java | 2 +- .../gen/server/api/ValidatorUtils.java | 24 ++++ 15 files changed, 401 insertions(+), 100 deletions(-) create mode 100644 docs/src/main/java/io/helidon/docs/mp/openapi/README.md create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java diff --git a/docs/pom.xml b/docs/pom.xml index 83485426f37..4d582167e3f 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -106,15 +106,6 @@ ${project.artifactId} - - - - org.openapitools - openapi-generator-maven-plugin - ${version.plugin.openapi-generator} - - - org.apache.maven.plugins @@ -183,90 +174,6 @@ - - org.openapitools - openapi-generator-maven-plugin - - - create-openapi-generator-project-mp - - generate - - generate-sources - - java-helidon-client - mp - ${project.basedir}/src/main/resources/petstorex.yaml - - helidonVersion=${parsedHelidonVersion.majorVersion} - - ${project.build.directory}/generated-sources/mp/client - true - - io.helidon.docs.openapi.gen.mp - helidon-docs-openapi-gen-mp-client - io.helidon.docs.openapi.gen.mp.client.api - io.helidon.docs.openapi.gen.mp.model - io.helidon.docs.openapi.gen.mp.client - - - - - create-openapi-generator-project-se-server-useabstractclass - - generate - - generate-sources - - java-helidon-server - se - ${project.basedir}/src/main/resources/petstorex.yaml - - helidonVersion=${parsedHelidonVersion.majorVersion} - useAbstractClass=true - - ${project.build.directory}/generated-sources/se/server - true - - io.helidon.docs.openapi.gen.se - helidon-docs-openapi-gen-se-server - io.helidon.docs.se.openapi.gen.server.api - io.helidon.docs.se.openapi.gen.server.model - io.helidon.docs.se.openapi.gen.server - - - - - create-openapi-generator-project-se-client - - generate - - generate-sources - - java-helidon-client - se - ${project.basedir}/src/main/resources/petstorex.yaml - - helidonVersion=${parsedHelidonVersion.majorVersion} - - openApiNullable=false - - ${project.build.directory}/generated-sources/se/client - true - - io.helidon.docs.openapi.gen.se - helidon-docs-openapi-gen-se-client - io.helidon.docs.se.openapi.gen.client.api - io.helidon.docs.se.openapi.gen.client.model - io.helidon.docs.se.openapi.gen.client - - - - - org.apache.maven.plugins maven-compiler-plugin diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java b/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java index 6b9df15a6b5..ed84bf7e160 100644 --- a/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java +++ b/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java @@ -22,12 +22,15 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; -import io.helidon.docs.openapi.gen.mp.model.Pet; -import io.helidon.docs.openapi.gen.mp.client.api.ApiException; -import io.helidon.docs.openapi.gen.mp.client.api.PetApi; - import org.eclipse.microprofile.rest.client.inject.RestClient; +interface Pet { +} +interface PetApi { + Pet getPetById(long petId); +} +class ApiException extends Exception { +} // tag::class-declaration[] @Path("/exampleServiceCallingService") // <1> diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/README.md b/docs/src/main/java/io/helidon/docs/mp/openapi/README.md new file mode 100644 index 00000000000..03f6dcd0067 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/openapi/README.md @@ -0,0 +1,17 @@ +To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: + +1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). +2. `mkdir mpClient` +3. `cd mpClient` +4. ```java + java -jar {downloadLocation}/openapi-generator-cli.jar generate \ + -g java-helidon-client \ + --library mp \ + -i {helidon-root}/docs/src/main/resources/petstorex.yaml \ + --helidonVersion 4 + ``` + or specify the appropriate Helidon major or exact version. + + + + diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md b/docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md new file mode 100644 index 00000000000..e201a13066a --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md @@ -0,0 +1,28 @@ +To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: + +1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). +2. `mkdir seServer` +3. `cd seServer` +4. ```java + java -jar {downloadLocation}/openapi-generator-cli.jar generate \ + -g java-helidon-server \ + --library se \ + -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ + -p useAbstractClass=true \ + --helidonVersion x.y.z + ``` + where `x.y.z` is the version of Helidon you are working with. +5. `mkdir ../seClient` +6. `cd ../seClient` +7. ```java + java -jar {downloadLocation}/openapi-generator-cli.jar generate \ + -g java-helidon-client \ + --library se \ + -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ + --helidonVersion x.y.z + ``` + where `x.y.z` is the version of Helidon you are working with. + + + + diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java new file mode 100644 index 00000000000..cd42a1e4794 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.client; + +import io.helidon.webclient.api.WebClientConfig; + +public class ApiClient { + + static Builder builder() { + return null; + } + + public static class Builder { + + public ApiClient build() { + return null; + } + + Builder objectMapper(Object om) { + return this; + } + + Builder webClientBuilder(WebClientConfig.Builder webClientConfigBuilder){ + return this; + } + + WebClientConfig.Builder webClientBuilder() { + return WebClientConfig.builder(); + } + } +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java new file mode 100644 index 00000000000..00d44a37c0a --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.client; + +import io.helidon.webclient.api.HttpClientResponse; + +public class ApiResponse { + + public HttpClientResponse webClientResponse() { + return null; + } + + public T result() { + return null; + } +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java index ebbe6cf9ff5..3737623024f 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java @@ -20,7 +20,7 @@ import io.helidon.docs.se.openapi.gen.client.api.PetApi; import io.helidon.docs.se.openapi.gen.client.api.PetApiImpl; -import io.helidon.docs.se.openapi.gen.client.model.Pet; +import io.helidon.docs.se.openapi.gen.model.Pet; import io.helidon.webclient.api.HttpClientResponse; import io.helidon.webclient.api.WebClient; import io.helidon.webclient.api.WebClientConfig; diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java new file mode 100644 index 00000000000..5afde0effb4 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.client.api; + +import java.util.List; + +import io.helidon.docs.se.openapi.gen.client.ApiResponse; +import io.helidon.docs.se.openapi.gen.model.Pet; + +public interface PetApi { + + ApiResponse> findPetsByStatus(List statusValues); +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java new file mode 100644 index 00000000000..ecb01a8e67f --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.client.api; + +import java.util.List; + +import io.helidon.docs.se.openapi.gen.client.ApiClient; +import io.helidon.docs.se.openapi.gen.client.ApiResponse; +import io.helidon.docs.se.openapi.gen.model.Pet; + +public class PetApiImpl implements PetApi { + + public static PetApi create(ApiClient apiClient) { + return null; + } + + @Override + public ApiResponse> findPetsByStatus(List statusValues) { + return null; + } +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java new file mode 100644 index 00000000000..50d3feef31c --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.model; + +import java.util.List; + +public class Pet { + + public String getName() { + return null; + } + + public long getId() { + return 0L; + } + + public enum StatusEnum { + AVAILABLE; + + public String value() { + return ""; + } + } + + public List getTags() { + return List.of(); + } +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java new file mode 100644 index 00000000000..0cf6589f9af --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.model; + +public class Tag { + + public String getName() { + return null; + } +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java index 6db3c95273f..d3c490096f4 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java @@ -15,7 +15,7 @@ */ package io.helidon.docs.se.openapi.gen.server.api; -import io.helidon.docs.se.openapi.gen.server.model.Pet; +import io.helidon.docs.se.openapi.gen.model.Pet; import io.helidon.webserver.http.ServerRequest; // tag::classDecl[] diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java new file mode 100644 index 00000000000..ab7c2f6140f --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.server.api; + +import java.util.List; +import java.util.Optional; + +import io.helidon.docs.se.openapi.gen.model.Pet; +import io.helidon.http.media.multipart.ReadablePart; +import io.helidon.webserver.http.ServerRequest; +import io.helidon.webserver.http.ServerResponse; + +public class PetService { + + protected void handleAddPet(ServerRequest request, ServerResponse response, Pet pet) { + } + + protected void handleDeletePet(ServerRequest request, ServerResponse response, + Long petId, + Optional apiKey) { + } + + protected void handleFindPetsByStatus(ServerRequest request, ServerResponse response, + List status) { + } + + protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, + List tags) { + } + + protected AddPetOp createAddPetOp() { + return null; + } + + protected void handleGetPetById(ServerRequest request, ServerResponse response, + Long petId) { + } + + protected void handlePetInfoWithCookies(ServerRequest request, ServerResponse response, + Long petId, + Long transactionId, + String label) { + } + + protected void handleUpdatePet(ServerRequest request, ServerResponse response, + Pet pet) { + } + + protected void handleUpdatePetWithForm(ServerRequest request, ServerResponse response, + Long petId, + Optional name, + Optional status) { + } + + protected void handleUploadFile(ServerRequest request, ServerResponse response, + Long petId, + Optional additionalMetadata, + Optional _file) { + } + + + public static class AddPetOp { + + protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { + return null; + } + + public record Response405() { + + public static Response405.Builder builder() { + return new Builder(); + } + + public static class Builder { + void send(ServerResponse response) { + } + } + } + + public record Response200(Pet response) { + + public static Response200.Builder builder() { + return new Builder(); + } + + public static class Builder { + void send(ServerResponse response) { + + } + } + } + } + + public static class FindPetsByTagsOp { + + public record Response200() { + + public static Response200.Builder builder() { + return new Response200.Builder(); + } + + public static class Builder { + public void send(ServerResponse response) { + } + + public Builder response(List result) { + return this; + } + } + } + } +} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java index 5368699265f..b5fd91b9141 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java @@ -31,7 +31,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Optional; import io.helidon.common.parameters.Parameters; -import io.helidon.docs.se.openapi.gen.server.model.Pet; +import io.helidon.docs.se.openapi.gen.model.Pet; import io.helidon.http.media.multipart.ReadablePart; import java.util.Set; import io.helidon.http.Status; diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java new file mode 100644 index 00000000000..bb62a0ac600 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi.gen.server.api; + +public class ValidatorUtils { + + public static class Validator { + void validatePattern(String itemName, String value, String pattern) { + } + } +} From 8a481d99c0415c5d72cb14850ca8649a8469f604 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 2 Aug 2024 15:58:11 -0500 Subject: [PATCH 08/17] Remove some unneeded text --- docs/pom.xml | 17 ----- .../se/openapi/gen/server/api/PetService.java | 35 --------- .../gen/server/api/PetServiceImplCustom.java | 71 ------------------- 3 files changed, 123 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 4d582167e3f..ff8c54ce9ef 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -34,7 +34,6 @@ false 2.1.0 2.1.0 - 7.7.0 @@ -158,22 +157,6 @@ ${helidon.sitegen.skip} - - org.codehaus.mojo - build-helper-maven-plugin - - - compute-major-version - - parse-version - - initialize - - parsedHelidonVersion - - - - org.apache.maven.plugins maven-compiler-plugin diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java index ab7c2f6140f..623eee5c8aa 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java @@ -28,15 +28,6 @@ public class PetService { protected void handleAddPet(ServerRequest request, ServerResponse response, Pet pet) { } - protected void handleDeletePet(ServerRequest request, ServerResponse response, - Long petId, - Optional apiKey) { - } - - protected void handleFindPetsByStatus(ServerRequest request, ServerResponse response, - List status) { - } - protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, List tags) { } @@ -45,32 +36,6 @@ protected AddPetOp createAddPetOp() { return null; } - protected void handleGetPetById(ServerRequest request, ServerResponse response, - Long petId) { - } - - protected void handlePetInfoWithCookies(ServerRequest request, ServerResponse response, - Long petId, - Long transactionId, - String label) { - } - - protected void handleUpdatePet(ServerRequest request, ServerResponse response, - Pet pet) { - } - - protected void handleUpdatePetWithForm(ServerRequest request, ServerResponse response, - Long petId, - Optional name, - Optional status) { - } - - protected void handleUploadFile(ServerRequest request, ServerResponse response, - Long petId, - Optional additionalMetadata, - Optional _file) { - } - public static class AddPetOp { diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java index b5fd91b9141..c1dba93858a 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java @@ -15,28 +15,12 @@ */ package io.helidon.docs.se.openapi.gen.server.api; -import java.util.ArrayList; -import java.util.stream.Collectors; -import java.io.File; import java.util.HashMap; -import io.helidon.http.HeaderNames; -import io.helidon.http.Headers; -import java.util.HexFormat; -import java.io.IOException; -import java.io.InputStream; import java.util.List; import java.util.Map; -import io.helidon.http.media.multipart.MultiPart; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Optional; -import io.helidon.common.parameters.Parameters; import io.helidon.docs.se.openapi.gen.model.Pet; -import io.helidon.http.media.multipart.ReadablePart; -import java.util.Set; import io.helidon.http.Status; -import java.io.UncheckedIOException; -import io.helidon.common.mapper.Value; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; @@ -72,21 +56,6 @@ protected void handleAddPet(ServerRequest request, ServerResponse response, } // end::handleAddPetEnd[] - @Override - protected void handleDeletePet(ServerRequest request, ServerResponse response, - Long petId, - Optional apiKey) { - - response.status(Status.NOT_IMPLEMENTED_501).send(); - } - - @Override - protected void handleFindPetsByStatus(ServerRequest request, ServerResponse response, - List status) { - - response.status(Status.NOT_IMPLEMENTED_501).send(); - } - // tag::handleFindPetsByTagsDecl[] @Override protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, @@ -116,46 +85,6 @@ protected AddPetOp createAddPetOp() { return new AddPetOpCustom(); } // end::createAddPetOp[] - @Override - protected void handleGetPetById(ServerRequest request, ServerResponse response, - Long petId) { - - response.status(Status.NOT_IMPLEMENTED_501).send(); - } - - @Override - protected void handlePetInfoWithCookies(ServerRequest request, ServerResponse response, - Long petId, - Long transactionId, - String label) { - - response.status(Status.NOT_IMPLEMENTED_501).send(); - } - - @Override - protected void handleUpdatePet(ServerRequest request, ServerResponse response, - Pet pet) { - - response.status(Status.NOT_IMPLEMENTED_501).send(); - } - - @Override - protected void handleUpdatePetWithForm(ServerRequest request, ServerResponse response, - Long petId, - Optional name, - Optional status) { - - response.status(Status.NOT_IMPLEMENTED_501).send(); - } - - @Override - protected void handleUploadFile(ServerRequest request, ServerResponse response, - Long petId, - Optional additionalMetadata, - Optional _file) { - - response.status(Status.NOT_IMPLEMENTED_501).send(); - } }} From 04fe60ce15350d36da51bbad1bb24a78ef1f1609 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 2 Aug 2024 16:35:31 -0500 Subject: [PATCH 09/17] Move the standalone Tag type as an inner type of Pet --- .../docs/se/openapi/gen/model/Pet.java | 7 ++++++ .../docs/se/openapi/gen/model/Tag.java | 23 ------------------- 2 files changed, 7 insertions(+), 23 deletions(-) delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java index 50d3feef31c..ff7839ef3ae 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java @@ -38,4 +38,11 @@ public String value() { public List getTags() { return List.of(); } + + public static class Tag { + + public String getName() { + return null; + } + } } diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java deleted file mode 100644 index 0cf6589f9af..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Tag.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.model; - -public class Tag { - - public String getName() { - return null; - } -} From eeade6834517da20e19bd05cfa87bf46e1b3fd7a Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Fri, 2 Aug 2024 17:46:24 -0500 Subject: [PATCH 10/17] Fix some missing indents on code includes --- .../se/openapi/openapi-generator.adoc | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index 7ee8135371a..cf2d1d240c4 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -115,14 +115,14 @@ Override how a parameter is extracted by following these steps, using the `AddPe .Customized `AddPetOp` class [source,java] ---- -include::{addPetOpCustom}[tag=classDecl] +include::{addPetOpCustom}[tag=classDecl, indent=0] ---- . In that subclass override the relevant method. + .Customized `pet` method for extracting and validating the `pet` parameter [source,java] ---- -include::{addPetOpCustom}[tag=petMethod] +include::{addPetOpCustom}[tag=petMethod, indent=0] ---- <1> Extracts the parameter from the request. This happens to use the same logic as in the generated method but you can customize that as well if you need to. <2> Apply any relevant validations. This silly but illustrative example rejects any pet name that starts with a lower-case letter. @@ -132,7 +132,7 @@ include::{addPetOpCustom}[tag=petMethod] [source,java] .Providing your custom implementation of `AddPet` ---- -include::{petServiceImpl}[tag=createAddPetOp] +include::{petServiceImpl}[tag=createAddPetOp, indent=0] ---- ===== Override how an operation is prepared from a request @@ -141,7 +141,7 @@ The generated code in these methods invokes the code to extract each incoming pa To completely change this behavior, override the `addPet` method in the `PetServiceImpl` class to do what you need. -include::{gen-inc}[tag=using-generated-code-client-intro] +include::{gen-inc}[tag=using-generated-code-client-intro, indent=0] The generated Helidon SE client includes the class `ApiClient`. This class corresponds to the Helidon link:{webclient-javadoc-base-url}.api/io/helidon/webclient/api/WebClient.html[`WebClient`] and represents the @@ -172,7 +172,7 @@ In the simplest case, your code can get an `ApiClient` instance directly. [source,java] .Creating an `ApiClient` instance - simple case ---- -include::{exampleClient}[tags=classDecl;initDecl;defaultApiClient;initEnd;classEnd] +include::{exampleClient}[tags=classDecl;initDecl;defaultApiClient;initEnd;classEnd, indent=0] ---- <1> The same `ApiClient` instance can be reused to invoke multiple APIs handled by the same server. <2> Creates an `ApiClient` instance using default settings from the OpenAPI document. @@ -189,7 +189,7 @@ Your code does not need to know how the object mapper setting is conveyed to the [source,java] .Creating an `ApiClient` instance - influencing the `ApiClient.Builder` ---- -include::{exampleClient}[tags=classDecl;initDecl;apiClientWithObjMapper;initEnd;classEnd] +include::{exampleClient}[tags=classDecl;initDecl;apiClientWithObjMapper;initEnd;classEnd, indent=0] ---- <1> Stores a reusable `ApiClient`. <2> A real app would fully set up the `ObjectMapper`. @@ -201,7 +201,7 @@ In more complicated situations, your code can adjust the settings of the `WebCli [source,java] .Creating an `ApiClient` instance - adjusting the `WebClientConfig.Builder` ---- -include::{exampleClient}[tags=classDecl;initDecl;apiClientWithAdjustedBuilder;initEnd;classEnd] +include::{exampleClient}[tags=classDecl;initDecl;apiClientWithAdjustedBuilder;initEnd;classEnd, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Creates a new `AppClient` builder. @@ -217,7 +217,7 @@ Lastly, you can construct the `WebClientConfig.Builder` entirely yourself and ha [source,java] .Creating an `ApiClient` instance - using a custom `WebClientConfig.Builder` ---- -include::{exampleClient}[tags=classDecl;initDecl;apiClientWithCustomWebClientBuilder;initEnd;classEnd] +include::{exampleClient}[tags=classDecl;initDecl;apiClientWithCustomWebClientBuilder;initEnd;classEnd, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Creates a new `WebClientConfig.Builder`. @@ -241,7 +241,7 @@ To invoke an operation defined on the `PetApi` interface, your code instantiates [source,java] .Preparing the PetStore Client API ---- -include::{exampleClient}[tags=classDeclFull;prepPetApi;classEnd] +include::{exampleClient}[tags=classDeclFull;prepPetApi;classEnd, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Stores a reusable `PetApi` for invoking pet-related operations. @@ -277,7 +277,7 @@ You can adopt different styles of retrieving the results, depending on the speci [source,java] .Access with only result access ---- -include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableUseResult;findAvailableEnd] +include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableUseResult;findAvailableEnd, indent=0] ---- <1> Use the previously-prepared `petApi` to find pets that have the `available` status. <2> Retrieve the typed result from the `ApiResponse`. @@ -290,7 +290,7 @@ The next example shows how your code can use the `HTTPClientResponse`. [source,java] .Access with status checking ---- -include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableResponseCheck;findAvailableEnd] +include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableResponseCheck;findAvailableEnd, indent=0] ---- <1> Start the remote service invocation. <2> Wait for the HTTP response status and headers to arrive. From b21ea53050106e00feed7b50e2ec85a8e278fec3 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Sat, 3 Aug 2024 13:25:28 -0500 Subject: [PATCH 11/17] Combine all snippets and required stubs into a single snippets file for each of SE and MP --- .../mp/openapi/openapi-generator.adoc | 2 +- .../se/openapi/openapi-generator.adoc | 30 +- .../ExampleOpenApiGenClientResource.java | 50 --- .../mp/openapi/OpenApiGeneratorSnippets.java | 72 ++++ .../java/io/helidon/docs/mp/openapi/README.md | 17 - .../se/openapi/OpenApiGeneratorSnippets.java | 387 ++++++++++++++++++ .../io/helidon/docs/se/openapi/gen/README.md | 28 -- .../docs/se/openapi/gen/client/ApiClient.java | 44 -- .../se/openapi/gen/client/ApiResponse.java | 29 -- .../se/openapi/gen/client/ExampleClient.java | 108 ----- .../se/openapi/gen/client/api/PetApi.java | 26 -- .../se/openapi/gen/client/api/PetApiImpl.java | 34 -- .../docs/se/openapi/gen/model/Pet.java | 48 --- .../gen/server/api/AddPetOpCustom.java | 41 -- .../se/openapi/gen/server/api/PetService.java | 90 ---- .../gen/server/api/PetServiceImplCustom.java | 92 ----- .../gen/server/api/ValidatorUtils.java | 24 -- 17 files changed, 474 insertions(+), 648 deletions(-) delete mode 100644 docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java create mode 100644 docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java delete mode 100644 docs/src/main/java/io/helidon/docs/mp/openapi/README.md create mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java delete mode 100644 docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java diff --git a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc index 47302b4bd7b..7f3cbf0fe1a 100644 --- a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc @@ -42,7 +42,7 @@ In the following example, `ExampleResource` (itself running in a server) invokes [source,java] .Using the generated `PetApi` returned from a separate service ---- -include::{sourcedir}/mp/openapi/ExampleOpenApiGenClientResource.java[tag=class-declaration, indent=0] +include::{sourcedir}/mp/openapi/OpenApiGeneratorSnippets.java[tag=class-declaration, indent=0] ---- <1> Uses a bean-defining annotation so CDI can inject into this class. diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index cf2d1d240c4..77590fadc66 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -25,9 +25,7 @@ include::{rootdir}/includes/se.adoc[] :incdir: {rootdir}/includes/openapi :gen-inc: {incdir}/openapi-generator.adoc -:petServiceImpl: {sourcedir}/se/openapi/gen/server/api/PetServiceImplCustom.java -:addPetOpCustom: {sourcedir}/se/openapi/gen/server/api/AddPetOpCustom.java -:exampleClient: {sourcedir}/se/openapi/gen/client/ExampleClient.java +:snippets: {sourcedir}/se/openapi/OpenApiGeneratorSnippets.java :helidon-client-xref: {webclient-page} @@ -57,7 +55,7 @@ The implementation class contains a `handle` method for each API operation with [source,java] .The generated `handleAddPet` method in the `PetApiImpl` class ---- -include::{petServiceImpl}/[tags=class-header;handleAddPetDecl;handleAddPetGenerated;handleAddPetEnd, indent=0] +include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomhandleAddPetGenerated;PetServiceImplCustomhandleAddPetEnd, indent=0] } ---- @@ -65,7 +63,7 @@ Customize the class to manage the pets and revise the method to save the new pet [source,java] .The customized `handleAddPet` method in the `PetApiImpl` class ---- -include::{petServiceImpl}/[tags=class-header;handleAddPetDecl;addedFields;handleAddPetCustom;handleAddPetEnd, indent=0] +include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomaddedFields;PetServiceImplCustomhandleAddPetCustom;PetServiceImplCustomhandleAddPetEnd, indent=0] } ---- <1> Business logic: create a very simple data store - a real app would use a database. @@ -79,7 +77,7 @@ The following example illustrates this for the `findPetsByTags` operation and it [source,java] .The customized `findPetsByTags` method in the `PetApiImpl` class ---- -include::{petServiceImpl}/[tags=class-header;addedFields;handleFindPetsByTagsDecl;handleFindPetsByTagsCustom;handleFindPetsByTagsEnd, indent=0] +include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomaddedFields;PetServiceImplCustomhandleFindPetsByTagsDecl;PetServiceImplCustomhandleFindPetsByTagsCustom;PetServiceImplCustomhandleFindPetsByTagsEnd, indent=0] } ---- <1> Uses the same data store as in the earlier example. @@ -115,14 +113,14 @@ Override how a parameter is extracted by following these steps, using the `AddPe .Customized `AddPetOp` class [source,java] ---- -include::{addPetOpCustom}[tag=classDecl, indent=0] +include::{snippets}[tag=AddPetOpCustomClassDecl, indent=0] ---- . In that subclass override the relevant method. + .Customized `pet` method for extracting and validating the `pet` parameter [source,java] ---- -include::{addPetOpCustom}[tag=petMethod, indent=0] +include::{snippets}[tag=AddPetOpCustompetMethod, indent=0] ---- <1> Extracts the parameter from the request. This happens to use the same logic as in the generated method but you can customize that as well if you need to. <2> Apply any relevant validations. This silly but illustrative example rejects any pet name that starts with a lower-case letter. @@ -132,7 +130,7 @@ include::{addPetOpCustom}[tag=petMethod, indent=0] [source,java] .Providing your custom implementation of `AddPet` ---- -include::{petServiceImpl}[tag=createAddPetOp, indent=0] +include::{snippets}[tag=PetServiceImplCustomcreateAddPetOp, indent=0] ---- ===== Override how an operation is prepared from a request @@ -172,7 +170,7 @@ In the simplest case, your code can get an `ApiClient` instance directly. [source,java] .Creating an `ApiClient` instance - simple case ---- -include::{exampleClient}[tags=classDecl;initDecl;defaultApiClient;initEnd;classEnd, indent=0] +include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientDefaultApiClient;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] ---- <1> The same `ApiClient` instance can be reused to invoke multiple APIs handled by the same server. <2> Creates an `ApiClient` instance using default settings from the OpenAPI document. @@ -189,7 +187,7 @@ Your code does not need to know how the object mapper setting is conveyed to the [source,java] .Creating an `ApiClient` instance - influencing the `ApiClient.Builder` ---- -include::{exampleClient}[tags=classDecl;initDecl;apiClientWithObjMapper;initEnd;classEnd, indent=0] +include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientApiClientWithObjMapper;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] ---- <1> Stores a reusable `ApiClient`. <2> A real app would fully set up the `ObjectMapper`. @@ -201,7 +199,7 @@ In more complicated situations, your code can adjust the settings of the `WebCli [source,java] .Creating an `ApiClient` instance - adjusting the `WebClientConfig.Builder` ---- -include::{exampleClient}[tags=classDecl;initDecl;apiClientWithAdjustedBuilder;initEnd;classEnd, indent=0] +include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientApiClientWithAdjustedBuilder;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Creates a new `AppClient` builder. @@ -217,7 +215,7 @@ Lastly, you can construct the `WebClientConfig.Builder` entirely yourself and ha [source,java] .Creating an `ApiClient` instance - using a custom `WebClientConfig.Builder` ---- -include::{exampleClient}[tags=classDecl;initDecl;apiClientWithCustomWebClientBuilder;initEnd;classEnd, indent=0] +include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientApiClientWithCustomWebClientBuilder;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Creates a new `WebClientConfig.Builder`. @@ -241,7 +239,7 @@ To invoke an operation defined on the `PetApi` interface, your code instantiates [source,java] .Preparing the PetStore Client API ---- -include::{exampleClient}[tags=classDeclFull;prepPetApi;classEnd, indent=0] +include::{snippets}[tags=ExampleClientclassDeclFull;ExampleClientPrepPetApi;ExampleClientclassEnd, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Stores a reusable `PetApi` for invoking pet-related operations. @@ -277,7 +275,7 @@ You can adopt different styles of retrieving the results, depending on the speci [source,java] .Access with only result access ---- -include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableUseResult;findAvailableEnd, indent=0] +include::{snippets}[tags=ExampleClientFindAvailableDecl;ExampleClientFindAvailableSimple;ExampleClientFindAvailableUseResult;ExampleClientFindAvailableEnd, indent=0] ---- <1> Use the previously-prepared `petApi` to find pets that have the `available` status. <2> Retrieve the typed result from the `ApiResponse`. @@ -290,7 +288,7 @@ The next example shows how your code can use the `HTTPClientResponse`. [source,java] .Access with status checking ---- -include::{exampleClient}[tags=findAvailableDecl;findAvailableSimple;findAvailableResponseCheck;findAvailableEnd, indent=0] +include::{snippets}[tags=ExampleClientFindAvailableDecl;ExampleClientFindAvailableSimple;ExampleClientFindAvailableResponseCheck;ExampleClientFindAvailableEnd, indent=0] ---- <1> Start the remote service invocation. <2> Wait for the HTTP response status and headers to arrive. diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java b/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java deleted file mode 100644 index ed84bf7e160..00000000000 --- a/docs/src/main/java/io/helidon/docs/mp/openapi/ExampleOpenApiGenClientResource.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.mp.openapi; - -import jakarta.inject.Inject; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; - -import org.eclipse.microprofile.rest.client.inject.RestClient; - -interface Pet { -} -interface PetApi { - Pet getPetById(long petId); -} -class ApiException extends Exception { -} - -// tag::class-declaration[] -@Path("/exampleServiceCallingService") // <1> -public class ExampleOpenApiGenClientResource { - @Inject // <2> - @RestClient // <3> - private PetApi petApi; // <4> - - @GET - @Path("/getPet/{petId}") - @Produces(MediaType.APPLICATION_JSON) - public Pet getPetUsingId(@PathParam("petId") Long petId) throws ApiException { - Pet pet = petApi.getPetById(petId); // <5> - return pet; - } -} -// end::class-declaration[] diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java new file mode 100644 index 00000000000..f5b9249dade --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.mp.openapi; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.inject.RestClient; + +public class OpenApiGeneratorSnippets { + + /* + To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: + + 1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). + 2. `mkdir mpClient` + 3. `cd mpClient` + 4. ```java + java -jar {downloadLocation}/openapi-generator-cli.jar generate \ + -g java-helidon-client \ + --library mp \ + -i {helidon-root}/docs/src/main/resources/petstorex.yaml \ + --helidonVersion 4 + ``` + or specify the appropriate Helidon major or exact version. + */ + + interface Pet { + } + + interface PetApi { + Pet getPetById(long petId); + } + + class ApiException extends Exception { + } + +// tag::class-declaration[] + @Path("/exampleServiceCallingService") // <1> + public class ExampleOpenApiGenClientResource { + @Inject // <2> + @RestClient // <3> + private PetApi petApi; // <4> + + @GET + @Path("/getPet/{petId}") + @Produces(MediaType.APPLICATION_JSON) + public Pet getPetUsingId(@PathParam("petId") Long petId) throws ApiException { + Pet pet = petApi.getPetById(petId); // <5> + return pet; + } + } +// end::class-declaration[] + +} \ No newline at end of file diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/README.md b/docs/src/main/java/io/helidon/docs/mp/openapi/README.md deleted file mode 100644 index 03f6dcd0067..00000000000 --- a/docs/src/main/java/io/helidon/docs/mp/openapi/README.md +++ /dev/null @@ -1,17 +0,0 @@ -To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: - -1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). -2. `mkdir mpClient` -3. `cd mpClient` -4. ```java - java -jar {downloadLocation}/openapi-generator-cli.jar generate \ - -g java-helidon-client \ - --library mp \ - -i {helidon-root}/docs/src/main/resources/petstorex.yaml \ - --helidonVersion 4 - ``` - or specify the appropriate Helidon major or exact version. - - - - diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java new file mode 100644 index 00000000000..8ab7b22cd65 --- /dev/null +++ b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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. + */ +package io.helidon.docs.se.openapi; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.helidon.http.Status; +import io.helidon.webclient.api.HttpClientResponse; +import io.helidon.webclient.api.WebClient; +import io.helidon.webclient.api.WebClientConfig; +import io.helidon.webserver.http.ServerRequest; +import io.helidon.webserver.http.ServerResponse; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class OpenApiGeneratorSnippets { + + /* + To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: + + 1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). + 2. `mkdir seServer` + 3. `cd seServer` + 4. ```java + java -jar {downloadLocation}/openapi-generator-cli.jar generate \ + -g java-helidon-server \ + --library se \ + -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ + -p useAbstractClass=true \ + --helidonVersion x.y.z + ``` + where `x.y.z` is the version of Helidon you are working with. + 5. `mkdir ../seClient` + 6. `cd ../seClient` + 7. ```java + java -jar {downloadLocation}/openapi-generator-cli.jar generate \ + -g java-helidon-client \ + --library se \ + -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ + --helidonVersion x.y.z + ``` + where `x.y.z` is the version of Helidon you are working with. + + */ + public static class Pet { + + public String getName() { + return null; + } + + public long getId() { + return 0L; + } + + public enum StatusEnum { + AVAILABLE; + + public String value() { + return ""; + } + } + + public List getTags() { + return List.of(); + } + + + } + + public static class Tag { + + public String getName() { + return null; + } + } + + public interface PetApi { + + ApiResponse> findPetsByStatus(List statusValues); + } + + public static class PetApiImpl implements PetApi { + + public static PetApi create(ApiClient apiClient) { + return null; + } + + @Override + public ApiResponse> findPetsByStatus(List statusValues) { + return null; + } + } + + public static class ApiClient { + + static Builder builder() { + return null; + } + + public static class Builder { + + public ApiClient build() { + return null; + } + + Builder objectMapper(Object om) { + return this; + } + + Builder webClientBuilder(WebClientConfig.Builder webClientConfigBuilder){ + return this; + } + + WebClientConfig.Builder webClientBuilder() { + return WebClientConfig.builder(); + } + } + } + + public static class ApiResponse { + + public HttpClientResponse webClientResponse() { + return null; + } + + public T result() { + return null; + } + } + + // tag::ExampleClientclassDeclFull[] + // tag::ExampleClientclassDecl[] + public class ExampleClient { + + private ApiClient apiClient; // <1> + + // end::ExampleClientclassDecl[] + private PetApi petApi; // <2> + + // end::ExampleClientclassDeclFull[] + // tag::ExampleClientinitDecl[] + void init() { + // end::ExampleClientinitDecl[] + // tag::ExampleClientDefaultApiClient[] + ApiClient apiClient = ApiClient.builder().build(); // <2> + // end::ExampleClientDefaultApiClient[] + // tag::ExampleClientApiClientWithObjMapper[] + ObjectMapper myObjectMapper = null; // <2> + + apiClient = ApiClient.builder() + .objectMapper(myObjectMapper) // <3> + .build(); + // end::ExampleClientApiClientWithObjMapper[] + + // tag::ExampleClientApiClientWithAdjustedBuilder[] + ApiClient.Builder apiClientAdjustedBuilder = ApiClient.builder(); // <2> + + apiClientAdjustedBuilder + .webClientBuilder() // <3> + .connectTimeout(Duration.ofSeconds(4)); // <4> + + apiClient = apiClientAdjustedBuilder.build(); // <5> + // end::ExampleClientApiClientWithAdjustedBuilder[] + // tag::ExampleClientApiClientWithCustomWebClientBuilder[] + WebClientConfig.Builder customWebClientBuilder = WebClient.builder()// <2> + .connectTimeout(Duration.ofSeconds(3)) // <3> + .baseUri("https://myservice.mycompany.com"); // <4> + + apiClient = ApiClient.builder() // <5> + .webClientBuilder(customWebClientBuilder) // <6> + .build(); // <7> + // end::ExampleClientApiClientWithCustomWebClientBuilder[] + // tag::ExampleClientinitEnd[] + } + // end::ExampleClientinitEnd[] + + // tag::ExampleClientPrepPetApi[] + void preparePetApi() { + petApi = PetApiImpl.create(apiClient); // <3> + } + // end::ExampleClientPrepPetApi[] + // tag::ExampleClientFindAvailableDecl[] + void findAvailablePets() { + // end::ExampleClientFindAvailableDecl[] + // tag::ExampleClientFindAvailableSimple[] + ApiResponse> apiResponse = + petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> + // end::ExampleClientFindAvailableSimple[] + // tag::ExampleClientFindAvailableResponseCheck[] + + try (HttpClientResponse webClientResponse = apiResponse.webClientResponse()) { // <2> + if (webClientResponse.status().code() != 200) { // <3> + // Handle a non-successful status. + } + } + + List avlPets = apiResponse.result(); // <4> + // end::ExampleClientFindAvailableResponseCheck[] + // tag::ExampleClientFindAvailableUseResult[] + + List availablePets = apiResponse.result(); // <2> + // end::ExampleClientFindAvailableUseResult[] + // tag::ExampleClientFindAvailableEnd[] + } + // end::ExampleClientFindAvailableEnd[] + + // tag::ExampleClientclassEnd[] + } + // end::ExampleClientclassEnd[] + + // tag::AddPetOpCustomClassDecl[] + public class AddPetOpCustom extends PetService.AddPetOp { + // end::AddPetOpCustomClassDecl[] + + // tag::AddPetOpCustompetMethod[] + @Override + protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { + Pet result = request.content().hasEntity() // <1> + ? request.content().as(Pet.class) + : null; + + /* + Insist that pet names never start with a lower-case letter. + */ + if (result != null) { + validator.validatePattern("pet", result.getName(), "[^a-z].*"); // <2> + } + return result; // <3> + } + // end::AddPetOpCustompetMethod[] + } + + public static class PetService { + + protected void handleAddPet(ServerRequest request, ServerResponse response, Pet pet) { + } + + protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, + List tags) { + } + + protected AddPetOp createAddPetOp() { + return null; + } + + + public static class AddPetOp { + + protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { + return null; + } + + public record Response405() { + + public static Response405.Builder builder() { + return new Builder(); + } + + public static class Builder { + void send(ServerResponse response) { + } + } + } + + public record Response200(Pet response) { + + public static Response200.Builder builder() { + return new Builder(); + } + + public static class Builder { + void send(ServerResponse response) { + + } + } + } + } + + public static class FindPetsByTagsOp { + + public record Response200() { + + public static Response200.Builder builder() { + return new Response200.Builder(); + } + + public static class Builder { + public void send(ServerResponse response) { + } + + public Builder response(List result) { + return this; + } + } + } + } + } + + public static class ValidatorUtils { + + public static class Validator { + void validatePattern(String itemName, String value, String pattern) { + } + } + } + +// tag::PetServiceImplCustomclass-declaration[] +// tag::PetServiceImplCustomclass-header[] + public class PetServiceImpl extends PetService { +// end::PetServiceImplCustomclass-header[] +// tag::PetServiceImplCustomaddedFields[] + + private final Map pets = new HashMap<>(); // <1> + +// end::PetServiceImplCustomaddedFields[] +// tag::PetServiceImplCustomhandleAddPetDecl[] + @Override + protected void handleAddPet(ServerRequest request, ServerResponse response, + Pet pet) { +// end::PetServiceImplCustomhandleAddPetDecl[] +// tag::PetServiceImplCustomhandleAddPetGenerated[] + response.status(Status.NOT_IMPLEMENTED_501).send(); +// end::PetServiceImplCustomhandleAddPetGenerated[] +// tag::PetServiceImplCustomhandleAddPetCustom[] + if (pets.containsKey(pet.getId())) { // <2> + AddPetOp.Response405.builder().send(response); + } + pets.put(pet.getId(), pet); // <3> + AddPetOp.Response200.builder().send(response); // <4> +// end::PetServiceImplCustomhandleAddPetCustom[] +// tag::PetServiceImplCustomhandleAddPetEnd[] + } +// end::PetServiceImplCustomhandleAddPetEnd[] + +// tag::PetServiceImplCustomhandleFindPetsByTagsDecl[] + @Override + protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, + List tags) { // <2> +// end::PetServiceImplCustomhandleFindPetsByTagsDecl[] +// tag::PetServiceImplCustomhandleFindPetsByTagsCustom[] + + List result = pets.values().stream() + .filter(pet -> pet.getTags() + .stream() + .anyMatch(petTag -> tags.contains(petTag.getName()))) + .toList(); // <3> + + FindPetsByTagsOp.Response200.builder() // <4> + .response(result) // <5> + .send(response); // <6> + +// end::PetServiceImplCustomhandleFindPetsByTagsCustom[] + response.status(Status.NOT_IMPLEMENTED_501).send(); +// tag::PetServiceImplCustomhandleFindPetsByTagsEnd[] + } +// end::PetServiceImplCustomhandleFindPetsByTagsEnd[] + +// tag::PetServiceImplCustomcreateAddPetOp[] + @Override + protected AddPetOp createAddPetOp() { + return new AddPetOpCustom(); + } +// end::PetServiceImplCustomcreateAddPetOp[] + } +// end::PetServiceImplCustomclass-declaration[] + + + +} + + diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md b/docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md deleted file mode 100644 index e201a13066a..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/README.md +++ /dev/null @@ -1,28 +0,0 @@ -To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: - -1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). -2. `mkdir seServer` -3. `cd seServer` -4. ```java - java -jar {downloadLocation}/openapi-generator-cli.jar generate \ - -g java-helidon-server \ - --library se \ - -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ - -p useAbstractClass=true \ - --helidonVersion x.y.z - ``` - where `x.y.z` is the version of Helidon you are working with. -5. `mkdir ../seClient` -6. `cd ../seClient` -7. ```java - java -jar {downloadLocation}/openapi-generator-cli.jar generate \ - -g java-helidon-client \ - --library se \ - -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ - --helidonVersion x.y.z - ``` - where `x.y.z` is the version of Helidon you are working with. - - - - diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java deleted file mode 100644 index cd42a1e4794..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiClient.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.client; - -import io.helidon.webclient.api.WebClientConfig; - -public class ApiClient { - - static Builder builder() { - return null; - } - - public static class Builder { - - public ApiClient build() { - return null; - } - - Builder objectMapper(Object om) { - return this; - } - - Builder webClientBuilder(WebClientConfig.Builder webClientConfigBuilder){ - return this; - } - - WebClientConfig.Builder webClientBuilder() { - return WebClientConfig.builder(); - } - } -} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java deleted file mode 100644 index 00d44a37c0a..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ApiResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.client; - -import io.helidon.webclient.api.HttpClientResponse; - -public class ApiResponse { - - public HttpClientResponse webClientResponse() { - return null; - } - - public T result() { - return null; - } -} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java deleted file mode 100644 index 3737623024f..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/ExampleClient.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.client; - -import java.time.Duration; -import java.util.List; - -import io.helidon.docs.se.openapi.gen.client.api.PetApi; -import io.helidon.docs.se.openapi.gen.client.api.PetApiImpl; -import io.helidon.docs.se.openapi.gen.model.Pet; -import io.helidon.webclient.api.HttpClientResponse; -import io.helidon.webclient.api.WebClient; -import io.helidon.webclient.api.WebClientConfig; - -import com.fasterxml.jackson.databind.ObjectMapper; - -// tag::classDeclFull[] -// tag::classDecl[] -public class ExampleClient { - - private ApiClient apiClient; // <1> - -// end::classDecl[] - private PetApi petApi; // <2> - -// end::classDeclFull[] -// tag::initDecl[] - void init() { -// end::initDecl[] -// tag::defaultApiClient[] - ApiClient apiClient = ApiClient.builder().build(); // <2> -// end::defaultApiClient[] -// tag::apiClientWithObjMapper[] - ObjectMapper myObjectMapper = null; // <2> - - apiClient = ApiClient.builder() - .objectMapper(myObjectMapper) // <3> - .build(); -// end::apiClientWithObjMapper[] - -// tag::apiClientWithAdjustedBuilder[] - ApiClient.Builder apiClientAdjustedBuilder = ApiClient.builder(); // <2> - - apiClientAdjustedBuilder - .webClientBuilder() // <3> - .connectTimeout(Duration.ofSeconds(4)); // <4> - - apiClient = apiClientAdjustedBuilder.build(); // <5> -// end::apiClientWithAdjustedBuilder[] -// tag::apiClientWithCustomWebClientBuilder[] - WebClientConfig.Builder customWebClientBuilder = WebClient.builder()// <2> - .connectTimeout(Duration.ofSeconds(3)) // <3> - .baseUri("https://myservice.mycompany.com"); // <4> - - apiClient = ApiClient.builder() // <5> - .webClientBuilder(customWebClientBuilder) // <6> - .build(); // <7> -// end::apiClientWithCustomWebClientBuilder[] -// tag::initEnd[] - } -// end::initEnd[] - -// tag::prepPetApi[] - void preparePetApi() { - petApi = PetApiImpl.create(apiClient); // <3> - } -// end::prepPetApi[] -// tag::findAvailableDecl[] - void findAvailablePets() { -// end::findAvailableDecl[] -// tag::findAvailableSimple[] - ApiResponse> apiResponse = - petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> -// end::findAvailableSimple[] -// tag::findAvailableResponseCheck[] - - try (HttpClientResponse webClientResponse = apiResponse.webClientResponse()) { // <2> - if (webClientResponse.status().code() != 200) { // <3> - // Handle a non-successful status. - } - } - - List avlPets = apiResponse.result(); // <4> -// end::findAvailableResponseCheck[] -// tag::findAvailableUseResult[] - - List availablePets = apiResponse.result(); // <2> -// end::findAvailableUseResult[] -// tag::findAvailableEnd[] - } -// end::findAvailableEnd[] - -// tag::classEnd[] -} -// end::classEnd[] diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java deleted file mode 100644 index 5afde0effb4..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApi.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.client.api; - -import java.util.List; - -import io.helidon.docs.se.openapi.gen.client.ApiResponse; -import io.helidon.docs.se.openapi.gen.model.Pet; - -public interface PetApi { - - ApiResponse> findPetsByStatus(List statusValues); -} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java deleted file mode 100644 index ecb01a8e67f..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/client/api/PetApiImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.client.api; - -import java.util.List; - -import io.helidon.docs.se.openapi.gen.client.ApiClient; -import io.helidon.docs.se.openapi.gen.client.ApiResponse; -import io.helidon.docs.se.openapi.gen.model.Pet; - -public class PetApiImpl implements PetApi { - - public static PetApi create(ApiClient apiClient) { - return null; - } - - @Override - public ApiResponse> findPetsByStatus(List statusValues) { - return null; - } -} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java deleted file mode 100644 index ff7839ef3ae..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/model/Pet.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.model; - -import java.util.List; - -public class Pet { - - public String getName() { - return null; - } - - public long getId() { - return 0L; - } - - public enum StatusEnum { - AVAILABLE; - - public String value() { - return ""; - } - } - - public List getTags() { - return List.of(); - } - - public static class Tag { - - public String getName() { - return null; - } - } -} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java deleted file mode 100644 index d3c490096f4..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/AddPetOpCustom.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.server.api; - -import io.helidon.docs.se.openapi.gen.model.Pet; -import io.helidon.webserver.http.ServerRequest; - -// tag::classDecl[] -public class AddPetOpCustom extends PetService.AddPetOp { -// end::classDecl[] - -// tag::petMethod[] - @Override - protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { - Pet result = request.content().hasEntity() // <1> - ? request.content().as(Pet.class) - : null; - - /* - Insist that pet names never start with a lower-case letter. - */ - if (result != null) { - validator.validatePattern("pet", result.getName(), "[^a-z].*"); // <2> - } - return result; // <3> - } -// end::petMethod[] -} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java deleted file mode 100644 index 623eee5c8aa..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetService.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.server.api; - -import java.util.List; -import java.util.Optional; - -import io.helidon.docs.se.openapi.gen.model.Pet; -import io.helidon.http.media.multipart.ReadablePart; -import io.helidon.webserver.http.ServerRequest; -import io.helidon.webserver.http.ServerResponse; - -public class PetService { - - protected void handleAddPet(ServerRequest request, ServerResponse response, Pet pet) { - } - - protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, - List tags) { - } - - protected AddPetOp createAddPetOp() { - return null; - } - - - public static class AddPetOp { - - protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { - return null; - } - - public record Response405() { - - public static Response405.Builder builder() { - return new Builder(); - } - - public static class Builder { - void send(ServerResponse response) { - } - } - } - - public record Response200(Pet response) { - - public static Response200.Builder builder() { - return new Builder(); - } - - public static class Builder { - void send(ServerResponse response) { - - } - } - } - } - - public static class FindPetsByTagsOp { - - public record Response200() { - - public static Response200.Builder builder() { - return new Response200.Builder(); - } - - public static class Builder { - public void send(ServerResponse response) { - } - - public Builder response(List result) { - return this; - } - } - } - } -} diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java deleted file mode 100644 index c1dba93858a..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/PetServiceImplCustom.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.server.api; - -import java.util.HashMap; - -import java.util.List; -import java.util.Map; -import io.helidon.docs.se.openapi.gen.model.Pet; -import io.helidon.http.Status; -import io.helidon.webserver.http.ServerRequest; -import io.helidon.webserver.http.ServerResponse; - -// Use a wrapper class so we can use the actual name of the generated class on the -// inner class. If we name this outer class PetServiceImpl that conflicts with the -// file of the same name (and in the same package) that the generator created. -public class PetServiceImplCustom { -// tag::class-declaration[] -// tag::class-header[] -public class PetServiceImpl extends PetService { -// end::class-header[] -// tag::addedFields[] - - private final Map pets = new HashMap<>(); // <1> - -// end::addedFields[] -// tag::handleAddPetDecl[] - @Override - protected void handleAddPet(ServerRequest request, ServerResponse response, - Pet pet) { -// end::handleAddPetDecl[] -// tag::handleAddPetGenerated[] - response.status(Status.NOT_IMPLEMENTED_501).send(); -// end::handleAddPetGenerated[] -// tag::handleAddPetCustom[] - if (pets.containsKey(pet.getId())) { // <2> - AddPetOp.Response405.builder().send(response); - } - pets.put(pet.getId(), pet); // <3> - AddPetOp.Response200.builder().send(response); // <4> -// end::handleAddPetCustom[] -// tag::handleAddPetEnd[] - } -// end::handleAddPetEnd[] - -// tag::handleFindPetsByTagsDecl[] - @Override - protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, - List tags) { // <2> -// end::handleFindPetsByTagsDecl[] -// tag::handleFindPetsByTagsCustom[] - - List result = pets.values().stream() - .filter(pet -> pet.getTags() - .stream() - .anyMatch(petTag -> tags.contains(petTag.getName()))) - .toList(); // <3> - - FindPetsByTagsOp.Response200.builder() // <4> - .response(result) // <5> - .send(response); // <6> - -// end::handleFindPetsByTagsCustom[] - response.status(Status.NOT_IMPLEMENTED_501).send(); -// tag::handleFindPetsByTagsEnd[] - } -// end::handleFindPetsByTagsEnd[] - -// tag::createAddPetOp[] - @Override - protected AddPetOp createAddPetOp() { - return new AddPetOpCustom(); - } -// end::createAddPetOp[] - -}} - - -// end::class-declaration[] diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java b/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java deleted file mode 100644 index bb62a0ac600..00000000000 --- a/docs/src/main/java/io/helidon/docs/se/openapi/gen/server/api/ValidatorUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2024 Oracle and/or its affiliates. - * - * 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 - * - * http://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. - */ -package io.helidon.docs.se.openapi.gen.server.api; - -public class ValidatorUtils { - - public static class Validator { - void validatePattern(String itemName, String value, String pattern) { - } - } -} From 0564b7c8670774cfffa6dc61c6afba2da2b00374 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 5 Aug 2024 15:46:57 -0500 Subject: [PATCH 12/17] Review comments --- docs/pom.xml | 2 -- .../se/openapi/openapi-generator.adoc | 19 ++++++++----------- .../se/openapi/OpenApiGeneratorSnippets.java | 5 ++--- parent/pom.xml | 5 ----- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index ff8c54ce9ef..e9f8aa3d2c5 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -101,8 +101,6 @@ - - ${project.artifactId} diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index 77590fadc66..0f5da25fed5 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -23,15 +23,13 @@ :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] -:incdir: {rootdir}/includes/openapi -:gen-inc: {incdir}/openapi-generator.adoc :snippets: {sourcedir}/se/openapi/OpenApiGeneratorSnippets.java :helidon-client-xref: {webclient-page} -include::{gen-inc}[tag=preamble] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=preamble] -include::{gen-inc}[tags=intro;coords;config;usage-basics;usage-grouping-intro;usage-grouping-server] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tags=intro;coords;config;usage-basics;usage-grouping-intro;usage-grouping-server] The generator creates Helidon routing logic based on the longest common path prefix shared among the operations that are grouped into each API. @@ -43,7 +41,7 @@ The generator logs a warning and includes a `TODO` comment in the generated rout Review the paths and the `tags` settings in your OpenAPI document and consider revising one or the other so all operations in each API share a common path prefix. If you do not have control over the OpenAPI document or do not want to change it, consider specifying the generator option `x-helidon-groupBy first-path-segment` which groups operations into APIs not by `tags` value but by the first segment of each operation's path. ==== -include::{gen-inc}[tags=usage-grouping-client;usage-after-grouping;using-generated-code-intro;using-generated-code-server-intro] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tags=usage-grouping-client;usage-after-grouping;using-generated-code-intro;using-generated-code-server-intro] If you choose to generate interfaces for the APIs, the generator creates routing rules for the API services it generates but you write virtually all of the logic to process incoming requests by implementing the very short methods generated in the implementation class. @@ -55,16 +53,15 @@ The implementation class contains a `handle` method for each API operation with [source,java] .The generated `handleAddPet` method in the `PetApiImpl` class ---- -include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomhandleAddPetGenerated;PetServiceImplCustomhandleAddPetEnd, indent=0] -} +include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomhandleAddPetGenerated;PetServiceImplCustomhandleAddPetEnd;PetServiceImplCustomclass-end, indent=0] + ---- Customize the class to manage the pets and revise the method to save the new pet and send the correct response, as shown next. [source,java] .The customized `handleAddPet` method in the `PetApiImpl` class ---- -include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomaddedFields;PetServiceImplCustomhandleAddPetCustom;PetServiceImplCustomhandleAddPetEnd, indent=0] -} +include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomaddedFields;PetServiceImplCustomhandleAddPetCustom;PetServiceImplCustomhandleAddPetEnd;PetServiceImplCustomclass-end, indent=0] ---- <1> Business logic: create a very simple data store - a real app would use a database. <2> Business logic: make sure the pet being added does not already exist. Send the invalid request status code if it does. @@ -139,7 +136,7 @@ The generated code in these methods invokes the code to extract each incoming pa To completely change this behavior, override the `addPet` method in the `PetServiceImpl` class to do what you need. -include::{gen-inc}[tag=using-generated-code-client-intro, indent=0] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=using-generated-code-client-intro, indent=0] The generated Helidon SE client includes the class `ApiClient`. This class corresponds to the Helidon link:{webclient-javadoc-base-url}.api/io/helidon/webclient/api/WebClient.html[`WebClient`] and represents the @@ -297,6 +294,6 @@ include::{snippets}[tags=ExampleClientFindAvailableDecl;ExampleClientFindAvailab This code also blocks the current thread, first to wait for the initial response and then to wait for the result content. -include::{gen-inc}[tag=common-references] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=common-references] * xref:{helidon-client-xref}[Helidon WebClient documentation] \ No newline at end of file diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java index 8ab7b22cd65..1b3b00dd710 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java @@ -377,11 +377,10 @@ protected AddPetOp createAddPetOp() { return new AddPetOpCustom(); } // end::PetServiceImplCustomcreateAddPetOp[] +// tag::PetServiceImplCustomclass-end[] } + // end::PetServiceImplCustomclass-end[] // end::PetServiceImplCustomclass-declaration[] - - - } diff --git a/parent/pom.xml b/parent/pom.xml index 5bc8d6e1fb5..487d2e20c90 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -193,11 +193,6 @@ versions-maven-plugin ${version.plugin.versions} - - org.codehaus.mojo - build-helper-maven-plugin - ${version.plugin.build-helper} - From 1c800f5decc364a665140c51a4ef7a5e0f63bcdd Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Mon, 5 Aug 2024 16:38:00 -0500 Subject: [PATCH 13/17] Further review comments --- docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc | 6 +++--- .../helidon/docs/se/openapi/OpenApiGeneratorSnippets.java | 8 +++++--- parent/pom.xml | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc index 7f3cbf0fe1a..b73750d8058 100644 --- a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc @@ -28,9 +28,9 @@ include::{rootdir}/includes/mp.adoc[] :gen-inc: {incdir}/openapi-generator.adoc :helidon-client-xref: {restclient-page} -include::{gen-inc}[tag=preamble] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=preamble] -include::{gen-inc}[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;using-generated-code-client-intro] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;using-generated-code-client-intro] The Helidon MP client generator creates a MicroProfile REST client interface for each API. Each generated API interface is annotated so your code can `@Inject` the API into one of your own beans and then use the interface directly to invoke the remote service. Alternatively, you can also explicitly use the link:{microprofile-rest-client-javadoc-url}/org/eclipse/microprofile/rest/client/RestClientBuilder.html[`RestClientBuilder`] to create an instance programmatically and then invoke its methods to contact the remote service. @@ -52,5 +52,5 @@ include::{sourcedir}/mp/openapi/OpenApiGeneratorSnippets.java[tag=class-declarat <5> Invokes the remote service using the injected field and the parameter from the incoming request. -include::{gen-inc}[tag=common-references] +include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=common-references] * link:https://github.com/eclipse/microprofile-rest-client[MicroProfile REST Client specification] diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java index 1b3b00dd710..fa52b18c300 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java @@ -32,7 +32,9 @@ public class OpenApiGeneratorSnippets { /* - To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: + To generate a project containing the types which need to be mocked--in case of changes in the upstream generator. + Here {downloadLocation} is where you downloaded the generator JAR to, and {helidonRoot} is your root directory + for the Helidon source. 1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). 2. `mkdir seServer` @@ -41,7 +43,7 @@ public class OpenApiGeneratorSnippets { java -jar {downloadLocation}/openapi-generator-cli.jar generate \ -g java-helidon-server \ --library se \ - -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ + -i {helidonRoot}/docs/src/main/resources/petstorex.yaml \ -p useAbstractClass=true \ --helidonVersion x.y.z ``` @@ -52,7 +54,7 @@ public class OpenApiGeneratorSnippets { java -jar {downloadLocation}/openapi-generator-cli.jar generate \ -g java-helidon-client \ --library se \ - -i ~/mic/j4c/helidon/docs/src/main/resources/petstorex.yaml \ + -i {helidonRoot}/docs/src/main/resources/petstorex.yaml \ --helidonVersion x.y.z ``` where `x.y.z` is the version of Helidon you are working with. diff --git a/parent/pom.xml b/parent/pom.xml index 487d2e20c90..92c4f131e8e 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -143,7 +143,6 @@ - 3.4.0 3.2.0 3.1.1 1.6 From 21c3eb51e69dd9e61ae0fd1ed3e383574c15b6ec Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Tue, 6 Aug 2024 10:59:19 -0500 Subject: [PATCH 14/17] Review comments --- docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc | 2 -- docs/src/main/asciidoc/se/openapi/openapi-generator.adoc | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc index b73750d8058..901a57bb620 100644 --- a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc @@ -24,8 +24,6 @@ include::{rootdir}/includes/mp.adoc[] -:incdir: {rootdir}/includes/openapi -:gen-inc: {incdir}/openapi-generator.adoc :helidon-client-xref: {restclient-page} include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=preamble] diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index 0f5da25fed5..09ce1b61bde 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -75,7 +75,6 @@ The following example illustrates this for the `findPetsByTags` operation and it .The customized `findPetsByTags` method in the `PetApiImpl` class ---- include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomaddedFields;PetServiceImplCustomhandleFindPetsByTagsDecl;PetServiceImplCustomhandleFindPetsByTagsCustom;PetServiceImplCustomhandleFindPetsByTagsEnd, indent=0] -} ---- <1> Uses the same data store as in the earlier example. <2> The `tags` parameter conveys the tag values to be matched in selecting pets to report. Other generated code extracts the runtime argument's value from the request and then automatically passes it to the method. @@ -106,6 +105,7 @@ For each operation in an API the generator creates an inner class and, for each Override how a parameter is extracted by following these steps, using the `AddPetOp` as an example. . Write a class which extends the inner class for the operation. +. In that subclass override the relevant method. + .Customized `AddPetOp` class [source,java] From 02f7340b69f4e927338164fec47a213f892e0173 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Wed, 7 Aug 2024 10:48:54 -0500 Subject: [PATCH 15/17] More review comments --- .../main/resources => etc}/petstorex.yaml | 0 .../includes/openapi/openapi-generator.adoc | 6 +- .../mp/openapi/OpenApiGeneratorSnippets.java | 41 ++++++++---- .../se/openapi/OpenApiGeneratorSnippets.java | 64 +++++++++++-------- 4 files changed, 68 insertions(+), 43 deletions(-) rename docs/{src/main/resources => etc}/petstorex.yaml (100%) diff --git a/docs/src/main/resources/petstorex.yaml b/docs/etc/petstorex.yaml similarity index 100% rename from docs/src/main/resources/petstorex.yaml rename to docs/etc/petstorex.yaml diff --git a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc index 15b19dcd891..6203315c789 100644 --- a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc @@ -19,8 +19,6 @@ :description: Helidon {flavor-uc} OpenAPI Generator :keywords: helidon, {flavor-lc}, openapi, generator :feature-name: Helidon {flavor-uc} OpenAPI Generator -// DO NOT CHANGE THE FOLLOWING - it's used as a minimum release that will not normally change with new releases of the OpenAPI generator -// Update the following when it is convenient to keep pace with the latest releases of the OpenAPITools generator // end::preamble[] // tag::intro[] @@ -225,9 +223,9 @@ You must specify the following options: | `java-helidon-server` + `java-helidon-client` -|`--library` + -`` +|`--library` | {nbsp} +| `` |Library you want to use |`mp` + `se` diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java index f5b9249dade..0b75bbd915e 100644 --- a/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java @@ -26,20 +26,35 @@ public class OpenApiGeneratorSnippets { - /* - To generate a project containing the types which need to be mocked--in case of changes in the upstream generator: + /* + + # The following commands download the generator and run it to generate projects containing the types which need to be + # mocked in the snippets in case of changes in the upstream generators. + # + # See these instructions https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar for full + # instructions to download the generator and + # https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#11---compatibility to see the latest stable version + # of the generator. + # + # As of this writing use at least 7.8.0. + # + # Revise the generatorVersion and helidonVersion settings below accordingly. + + generatorVersion=7.8.0 + helidonVersion=4 # Uses the latest publicly released version of 4. Specify x.y.z if you want to specify an exact release. + + curl -O \ + --output-dir /tmp \ + https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/$generatorVersion/openapi-generator-cli-${generatorVersion}.jar + + rm -rf /tmp/petapi-client + java -jar /tmp/openapi-generator-cli-$generatorVersion.jar generate \ + -o /tmp/petapi-client \ + -g java-helidon-client \ + --library mp \ + -i etc/petstorex.yaml \ + -p helidonVersion=${helidonVersion} - 1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). - 2. `mkdir mpClient` - 3. `cd mpClient` - 4. ```java - java -jar {downloadLocation}/openapi-generator-cli.jar generate \ - -g java-helidon-client \ - --library mp \ - -i {helidon-root}/docs/src/main/resources/petstorex.yaml \ - --helidonVersion 4 - ``` - or specify the appropriate Helidon major or exact version. */ interface Pet { diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java index fa52b18c300..0277d838007 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java @@ -32,32 +32,44 @@ public class OpenApiGeneratorSnippets { /* - To generate a project containing the types which need to be mocked--in case of changes in the upstream generator. - Here {downloadLocation} is where you downloaded the generator JAR to, and {helidonRoot} is your root directory - for the Helidon source. - - 1. Download the generator: see [these instructions](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar). - 2. `mkdir seServer` - 3. `cd seServer` - 4. ```java - java -jar {downloadLocation}/openapi-generator-cli.jar generate \ - -g java-helidon-server \ - --library se \ - -i {helidonRoot}/docs/src/main/resources/petstorex.yaml \ - -p useAbstractClass=true \ - --helidonVersion x.y.z - ``` - where `x.y.z` is the version of Helidon you are working with. - 5. `mkdir ../seClient` - 6. `cd ../seClient` - 7. ```java - java -jar {downloadLocation}/openapi-generator-cli.jar generate \ - -g java-helidon-client \ - --library se \ - -i {helidonRoot}/docs/src/main/resources/petstorex.yaml \ - --helidonVersion x.y.z - ``` - where `x.y.z` is the version of Helidon you are working with. + + # The following commands download the generator and run it to generate projects containing the types which need to be + # mocked in the snippets in case of changes in the upstream generators. + # + # See these instructions https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#13---download-jar for full + # instructions to download the generator and + # https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#11---compatibility to see the latest stable version + # of the generator. + # + # As of this writing use at least 7.8.0. + # + # Revise the generatorVersion and helidonVersion settings below accordingly. + + generatorVersion=7.8.0 + helidonVersion=4 # Uses the latest publicly released version of 4. Specify x.y.z if you want to specify an exact release. + + curl -O \ + --output-dir /tmp \ + https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/$generatorVersion/openapi-generator-cli-${generatorVersion}.jar + + # Generate the server project. + rm -rf /tmp/petapi-server + java -jar /tmp/openapi-generator-cli-$generatorVersion.jar generate \ + -o /tmp/petapi-server \ + -g java-helidon-server \ + --library se \ + -i etc/petstorex.yaml \ + -p useAbstractClass=true \ + -p helidonVersion=${helidonVersion} + + # Generate the client project. + rm -rf /tmp/petapi-client + java -jar /tmp/openapi-generator-cli-$generatorVersion.jar generate \ + -o /tmp/petapi-client \ + -g java-helidon-client \ + --library se \ + -i etc/petstorex.yaml \ + -p helidonVersion=${helidonVersion} */ public static class Pet { From c3816fc643c4232802cd7a6eb839bc9e293bc4c8 Mon Sep 17 00:00:00 2001 From: Tim Quinn Date: Wed, 7 Aug 2024 11:43:10 -0500 Subject: [PATCH 16/17] Review comments --- .../se/openapi/openapi-generator.adoc | 36 +- .../se/openapi/OpenApiGeneratorSnippets.java | 455 ++++++++++-------- 2 files changed, 260 insertions(+), 231 deletions(-) diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index 09ce1b61bde..60048d6e898 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -23,12 +23,10 @@ :rootdir: {docdir}/../.. include::{rootdir}/includes/se.adoc[] -:snippets: {sourcedir}/se/openapi/OpenApiGeneratorSnippets.java +:snippets: {sourcedir}/se/openapi/OpenApiGeneratorSnippets.java :helidon-client-xref: {webclient-page} -include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=preamble] - include::{rootdir}/includes/openapi/openapi-generator.adoc[tags=intro;coords;config;usage-basics;usage-grouping-intro;usage-grouping-server] The generator creates Helidon routing logic based on the longest common path prefix shared among the operations that are grouped into each API. @@ -53,15 +51,14 @@ The implementation class contains a `handle` method for each API operation with [source,java] .The generated `handleAddPet` method in the `PetApiImpl` class ---- -include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomhandleAddPetGenerated;PetServiceImplCustomhandleAddPetEnd;PetServiceImplCustomclass-end, indent=0] - +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_1, indent=0] ---- Customize the class to manage the pets and revise the method to save the new pet and send the correct response, as shown next. [source,java] .The customized `handleAddPet` method in the `PetApiImpl` class ---- -include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomhandleAddPetDecl;PetServiceImplCustomaddedFields;PetServiceImplCustomhandleAddPetCustom;PetServiceImplCustomhandleAddPetEnd;PetServiceImplCustomclass-end, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_2, indent=0] ---- <1> Business logic: create a very simple data store - a real app would use a database. <2> Business logic: make sure the pet being added does not already exist. Send the invalid request status code if it does. @@ -74,7 +71,7 @@ The following example illustrates this for the `findPetsByTags` operation and it [source,java] .The customized `findPetsByTags` method in the `PetApiImpl` class ---- -include::{snippets}/[tags=PetServiceImplCustomclass-header;PetServiceImplCustomaddedFields;PetServiceImplCustomhandleFindPetsByTagsDecl;PetServiceImplCustomhandleFindPetsByTagsCustom;PetServiceImplCustomhandleFindPetsByTagsEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_3, indent=0] ---- <1> Uses the same data store as in the earlier example. <2> The `tags` parameter conveys the tag values to be matched in selecting pets to report. Other generated code extracts the runtime argument's value from the request and then automatically passes it to the method. @@ -110,14 +107,7 @@ Override how a parameter is extracted by following these steps, using the `AddPe .Customized `AddPetOp` class [source,java] ---- -include::{snippets}[tag=AddPetOpCustomClassDecl, indent=0] ----- -. In that subclass override the relevant method. -+ -.Customized `pet` method for extracting and validating the `pet` parameter -[source,java] ----- -include::{snippets}[tag=AddPetOpCustompetMethod, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_4, indent=0] ---- <1> Extracts the parameter from the request. This happens to use the same logic as in the generated method but you can customize that as well if you need to. <2> Apply any relevant validations. This silly but illustrative example rejects any pet name that starts with a lower-case letter. @@ -127,7 +117,7 @@ include::{snippets}[tag=AddPetOpCustompetMethod, indent=0] [source,java] .Providing your custom implementation of `AddPet` ---- -include::{snippets}[tag=PetServiceImplCustomcreateAddPetOp, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_5, indent=0] ---- ===== Override how an operation is prepared from a request @@ -167,7 +157,7 @@ In the simplest case, your code can get an `ApiClient` instance directly. [source,java] .Creating an `ApiClient` instance - simple case ---- -include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientDefaultApiClient;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_6, indent=0] ---- <1> The same `ApiClient` instance can be reused to invoke multiple APIs handled by the same server. <2> Creates an `ApiClient` instance using default settings from the OpenAPI document. @@ -184,7 +174,7 @@ Your code does not need to know how the object mapper setting is conveyed to the [source,java] .Creating an `ApiClient` instance - influencing the `ApiClient.Builder` ---- -include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientApiClientWithObjMapper;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_7, indent=0] ---- <1> Stores a reusable `ApiClient`. <2> A real app would fully set up the `ObjectMapper`. @@ -196,7 +186,7 @@ In more complicated situations, your code can adjust the settings of the `WebCli [source,java] .Creating an `ApiClient` instance - adjusting the `WebClientConfig.Builder` ---- -include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientApiClientWithAdjustedBuilder;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_8, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Creates a new `AppClient` builder. @@ -212,7 +202,7 @@ Lastly, you can construct the `WebClientConfig.Builder` entirely yourself and ha [source,java] .Creating an `ApiClient` instance - using a custom `WebClientConfig.Builder` ---- -include::{snippets}[tags=ExampleClientclassDecl;ExampleClientinitDecl;ExampleClientApiClientWithCustomWebClientBuilder;ExampleClientinitEnd;ExampleClientclassEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_9, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Creates a new `WebClientConfig.Builder`. @@ -236,7 +226,7 @@ To invoke an operation defined on the `PetApi` interface, your code instantiates [source,java] .Preparing the PetStore Client API ---- -include::{snippets}[tags=ExampleClientclassDeclFull;ExampleClientPrepPetApi;ExampleClientclassEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_10, indent=0] ---- <1> Stores a reusable `AppClient`. <2> Stores a reusable `PetApi` for invoking pet-related operations. @@ -272,7 +262,7 @@ You can adopt different styles of retrieving the results, depending on the speci [source,java] .Access with only result access ---- -include::{snippets}[tags=ExampleClientFindAvailableDecl;ExampleClientFindAvailableSimple;ExampleClientFindAvailableUseResult;ExampleClientFindAvailableEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_11, indent=0] ---- <1> Use the previously-prepared `petApi` to find pets that have the `available` status. <2> Retrieve the typed result from the `ApiResponse`. @@ -285,7 +275,7 @@ The next example shows how your code can use the `HTTPClientResponse`. [source,java] .Access with status checking ---- -include::{snippets}[tags=ExampleClientFindAvailableDecl;ExampleClientFindAvailableSimple;ExampleClientFindAvailableResponseCheck;ExampleClientFindAvailableEnd, indent=0] +include::{sourcedir}/se/openapi/OpenApiGeneratorSnippets.java/[tags=snippet_12, indent=0] ---- <1> Start the remote service invocation. <2> Wait for the HTTP response status and headers to arrive. diff --git a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java index 0277d838007..d389f53039b 100644 --- a/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java +++ b/docs/src/main/java/io/helidon/docs/se/openapi/OpenApiGeneratorSnippets.java @@ -29,7 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; -public class OpenApiGeneratorSnippets { +@SuppressWarnings("ALL") class OpenApiGeneratorSnippets { /* @@ -72,46 +72,122 @@ public class OpenApiGeneratorSnippets { -p helidonVersion=${helidonVersion} */ - public static class Pet { + // stub + public class AddPetOpCustom extends PetService.AddPetOp { + } + + // stub + static class PetService { + + void handleAddPet(ServerRequest request, ServerResponse response, Pet pet) { + } + + void handleFindPetsByTags(ServerRequest request, ServerResponse response, + List tags) { + } + + AddPetOp createAddPetOp() { + return null; + } + + static class AddPetOp { + + Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { + return null; + } + + record Response405() { + + static Response405.Builder builder() { + return new Builder(); + } + + static class Builder { + void send(ServerResponse response) { + } + } + } + + record Response200(Pet response) { + + static Response200.Builder builder() { + return new Builder(); + } + + static class Builder { + void send(ServerResponse response) { + } + } + } + } - public String getName() { + static class FindPetsByTagsOp { + + record Response200() { + + static Response200.Builder builder() { + return new Response200.Builder(); + } + + static class Builder { + public void send(ServerResponse response) { + } + + public Builder response(List result) { + return this; + } + } + } + } + } + + // stub + class ValidatorUtils { + class Validator { + void validatePattern(String itemName, String value, String pattern) { + } + } + } + + // stub + class Pet { + + String getName() { return null; } - public long getId() { + long getId() { return 0L; } - public enum StatusEnum { + enum StatusEnum { AVAILABLE; - public String value() { - return ""; + String value() { + return null; } } - public List getTags() { + List getTags() { return List.of(); } - - } - public static class Tag { + // stub + class Tag { - public String getName() { + String getName() { return null; } } - public interface PetApi { - + interface PetApi { ApiResponse> findPetsByStatus(List statusValues); } - public static class PetApiImpl implements PetApi { + class PetApiImpl implements PetApi { - public static PetApi create(ApiClient apiClient) { + static PetApi create(ApiClient apiClient) { return null; } @@ -121,15 +197,15 @@ public ApiResponse> findPetsByStatus(List statusValues) { } } - public static class ApiClient { + class ApiClient { static Builder builder() { return null; } - public static class Builder { + class Builder { - public ApiClient build() { + ApiClient build() { return null; } @@ -137,7 +213,7 @@ Builder objectMapper(Object om) { return this; } - Builder webClientBuilder(WebClientConfig.Builder webClientConfigBuilder){ + Builder webClientBuilder(WebClientConfig.Builder webClientConfigBuilder) { return this; } @@ -147,254 +223,217 @@ WebClientConfig.Builder webClientBuilder() { } } - public static class ApiResponse { + class ApiResponse { - public HttpClientResponse webClientResponse() { + HttpClientResponse webClientResponse() { return null; } - public T result() { + T result() { return null; } } - // tag::ExampleClientclassDeclFull[] - // tag::ExampleClientclassDecl[] - public class ExampleClient { - - private ApiClient apiClient; // <1> - - // end::ExampleClientclassDecl[] - private PetApi petApi; // <2> - - // end::ExampleClientclassDeclFull[] - // tag::ExampleClientinitDecl[] - void init() { - // end::ExampleClientinitDecl[] - // tag::ExampleClientDefaultApiClient[] - ApiClient apiClient = ApiClient.builder().build(); // <2> - // end::ExampleClientDefaultApiClient[] - // tag::ExampleClientApiClientWithObjMapper[] - ObjectMapper myObjectMapper = null; // <2> - - apiClient = ApiClient.builder() - .objectMapper(myObjectMapper) // <3> - .build(); - // end::ExampleClientApiClientWithObjMapper[] - - // tag::ExampleClientApiClientWithAdjustedBuilder[] - ApiClient.Builder apiClientAdjustedBuilder = ApiClient.builder(); // <2> - - apiClientAdjustedBuilder - .webClientBuilder() // <3> - .connectTimeout(Duration.ofSeconds(4)); // <4> - - apiClient = apiClientAdjustedBuilder.build(); // <5> - // end::ExampleClientApiClientWithAdjustedBuilder[] - // tag::ExampleClientApiClientWithCustomWebClientBuilder[] - WebClientConfig.Builder customWebClientBuilder = WebClient.builder()// <2> - .connectTimeout(Duration.ofSeconds(3)) // <3> - .baseUri("https://myservice.mycompany.com"); // <4> - - apiClient = ApiClient.builder() // <5> - .webClientBuilder(customWebClientBuilder) // <6> - .build(); // <7> - // end::ExampleClientApiClientWithCustomWebClientBuilder[] - // tag::ExampleClientinitEnd[] + class Snippet1 { + // tag::snippet_1[] + public class PetServiceImpl extends PetService { + @Override + protected void handleAddPet(ServerRequest request, ServerResponse response, + Pet pet) { + response.status(Status.NOT_IMPLEMENTED_501).send(); + } } - // end::ExampleClientinitEnd[] + // end::snippet_1[] + } - // tag::ExampleClientPrepPetApi[] - void preparePetApi() { - petApi = PetApiImpl.create(apiClient); // <3> - } - // end::ExampleClientPrepPetApi[] - // tag::ExampleClientFindAvailableDecl[] - void findAvailablePets() { - // end::ExampleClientFindAvailableDecl[] - // tag::ExampleClientFindAvailableSimple[] - ApiResponse> apiResponse = - petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> - // end::ExampleClientFindAvailableSimple[] - // tag::ExampleClientFindAvailableResponseCheck[] + class Snippet2 { + // tag::snippet_2[] + public class PetServiceImpl extends PetService { - try (HttpClientResponse webClientResponse = apiResponse.webClientResponse()) { // <2> - if (webClientResponse.status().code() != 200) { // <3> - // Handle a non-successful status. + private final Map pets = new HashMap<>(); // <1> + + @Override + protected void handleAddPet(ServerRequest request, ServerResponse response, + Pet pet) { + if (pets.containsKey(pet.getId())) { // <2> + AddPetOp.Response405.builder().send(response); } + pets.put(pet.getId(), pet); // <3> + AddPetOp.Response200.builder().send(response); // <4> } + } + // end::snippet_2[] + } - List avlPets = apiResponse.result(); // <4> - // end::ExampleClientFindAvailableResponseCheck[] - // tag::ExampleClientFindAvailableUseResult[] + class Snippet3 { + // tag::snippet_3[] + public class PetServiceImpl extends PetService { - List availablePets = apiResponse.result(); // <2> - // end::ExampleClientFindAvailableUseResult[] - // tag::ExampleClientFindAvailableEnd[] - } - // end::ExampleClientFindAvailableEnd[] + private final Map pets = new HashMap<>(); // <1> - // tag::ExampleClientclassEnd[] - } - // end::ExampleClientclassEnd[] + @Override + protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, + List tags) { // <2> - // tag::AddPetOpCustomClassDecl[] - public class AddPetOpCustom extends PetService.AddPetOp { - // end::AddPetOpCustomClassDecl[] + List result = pets.values().stream() + .filter(pet -> pet.getTags() + .stream() + .anyMatch(petTag -> tags.contains(petTag.getName()))) + .toList(); // <3> + + FindPetsByTagsOp.Response200.builder() // <4> + .response(result) // <5> + .send(response); // <6> - // tag::AddPetOpCustompetMethod[] - @Override - protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { - Pet result = request.content().hasEntity() // <1> - ? request.content().as(Pet.class) - : null; - - /* - Insist that pet names never start with a lower-case letter. - */ - if (result != null) { - validator.validatePattern("pet", result.getName(), "[^a-z].*"); // <2> } - return result; // <3> } - // end::AddPetOpCustompetMethod[] + // end::snippet_3[] } - public static class PetService { - - protected void handleAddPet(ServerRequest request, ServerResponse response, Pet pet) { - } + class Snippet4 { + // tag::snippet_4[] + public class AddPetOpCustom extends PetService.AddPetOp { + @Override + protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { + Pet result = request.content().hasEntity() // <1> + ? request.content().as(Pet.class) + : null; - protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, - List tags) { + // Insist that pet names never start with a lower-case letter. + if (result != null) { + validator.validatePattern("pet", result.getName(), "[^a-z].*"); // <2> + } + return result; // <3> + } } + // end::snippet_4[] + } - protected AddPetOp createAddPetOp() { - return null; + class Snippet5 { + // tag::snippet_5[] + public class PetServiceImpl extends PetService { + @Override + protected AddPetOp createAddPetOp() { + return new AddPetOpCustom(); + } } + // end::snippet_5[] + } + class Snippet6 { + // tag::snippet_6[] + public class ExampleClient { - public static class AddPetOp { + private ApiClient apiClient; // <1> - protected Pet pet(ServerRequest request, ValidatorUtils.Validator validator) { - return null; + void init() { + ApiClient apiClient = ApiClient.builder().build(); // <2> } + } + // end::snippet_6[] + } - public record Response405() { + class Snippet7 { + // tag::snippet_7[] + public class ExampleClient { - public static Response405.Builder builder() { - return new Builder(); - } + private ApiClient apiClient; // <1> - public static class Builder { - void send(ServerResponse response) { - } - } + void init() { + ObjectMapper myObjectMapper = new ObjectMapper(); // <2> + apiClient = ApiClient.builder() + .objectMapper(myObjectMapper) // <3> + .build(); } + } + // end::snippet_7[] + } - public record Response200(Pet response) { + class Snippet8 { + // tag::snippet_8[] + public class ExampleClient { - public static Response200.Builder builder() { - return new Builder(); - } + private ApiClient apiClient; // <1> - public static class Builder { - void send(ServerResponse response) { + void init() { + ApiClient.Builder apiClientAdjustedBuilder = ApiClient.builder(); // <2> - } - } + apiClientAdjustedBuilder + .webClientBuilder() // <3> + .connectTimeout(Duration.ofSeconds(4)); // <4> + + apiClient = apiClientAdjustedBuilder.build(); // <5> } } + // end::snippet_8[] + } - public static class FindPetsByTagsOp { - - public record Response200() { + class Snippet9 { + // tag::snippet_9[] + public class ExampleClient { - public static Response200.Builder builder() { - return new Response200.Builder(); - } + private ApiClient apiClient; // <1> - public static class Builder { - public void send(ServerResponse response) { - } + void init() { + WebClientConfig.Builder customWebClientBuilder = WebClient.builder() // <2> + .connectTimeout(Duration.ofSeconds(3)) // <3> + .baseUri("https://myservice.mycompany.com"); // <4> - public Builder response(List result) { - return this; - } - } + apiClient = ApiClient.builder() // <5> + .webClientBuilder(customWebClientBuilder) // <6> + .build(); // <7> } } + // end::snippet_9[] } - public static class ValidatorUtils { + class Snippet10 { + // tag::snippet_10[] + public class ExampleClient { - public static class Validator { - void validatePattern(String itemName, String value, String pattern) { + private ApiClient apiClient; // <1> + + private PetApi petApi; // <2> + + void preparePetApi() { + petApi = PetApiImpl.create(apiClient); // <3> } } + // end::snippet_10[] } -// tag::PetServiceImplCustomclass-declaration[] -// tag::PetServiceImplCustomclass-header[] - public class PetServiceImpl extends PetService { -// end::PetServiceImplCustomclass-header[] -// tag::PetServiceImplCustomaddedFields[] + class Snippet11 { + PetApi petApi; - private final Map pets = new HashMap<>(); // <1> + // tag::snippet_11[] + void findAvailablePets() { + ApiResponse> apiResponse = + petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> -// end::PetServiceImplCustomaddedFields[] -// tag::PetServiceImplCustomhandleAddPetDecl[] - @Override - protected void handleAddPet(ServerRequest request, ServerResponse response, - Pet pet) { -// end::PetServiceImplCustomhandleAddPetDecl[] -// tag::PetServiceImplCustomhandleAddPetGenerated[] - response.status(Status.NOT_IMPLEMENTED_501).send(); -// end::PetServiceImplCustomhandleAddPetGenerated[] -// tag::PetServiceImplCustomhandleAddPetCustom[] - if (pets.containsKey(pet.getId())) { // <2> - AddPetOp.Response405.builder().send(response); - } - pets.put(pet.getId(), pet); // <3> - AddPetOp.Response200.builder().send(response); // <4> -// end::PetServiceImplCustomhandleAddPetCustom[] -// tag::PetServiceImplCustomhandleAddPetEnd[] + List availablePets = apiResponse.result(); // <2> } -// end::PetServiceImplCustomhandleAddPetEnd[] + // end::snippet_11[] + } -// tag::PetServiceImplCustomhandleFindPetsByTagsDecl[] - @Override - protected void handleFindPetsByTags(ServerRequest request, ServerResponse response, - List tags) { // <2> -// end::PetServiceImplCustomhandleFindPetsByTagsDecl[] -// tag::PetServiceImplCustomhandleFindPetsByTagsCustom[] - - List result = pets.values().stream() - .filter(pet -> pet.getTags() - .stream() - .anyMatch(petTag -> tags.contains(petTag.getName()))) - .toList(); // <3> - - FindPetsByTagsOp.Response200.builder() // <4> - .response(result) // <5> - .send(response); // <6> - -// end::PetServiceImplCustomhandleFindPetsByTagsCustom[] - response.status(Status.NOT_IMPLEMENTED_501).send(); -// tag::PetServiceImplCustomhandleFindPetsByTagsEnd[] - } -// end::PetServiceImplCustomhandleFindPetsByTagsEnd[] + class Snippet12 { + PetApi petApi; -// tag::PetServiceImplCustomcreateAddPetOp[] - @Override - protected AddPetOp createAddPetOp() { - return new AddPetOpCustom(); + // tag::snippet_12[] + void findAvailablePets() { + ApiResponse> apiResponse = + petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> + + try (HttpClientResponse webClientResponse = apiResponse.webClientResponse()) { // <2> + if (webClientResponse.status().code() != 200) { // <3> + // Handle a non-successful status. + } + } + + List avlPets = apiResponse.result(); // <4> } -// end::PetServiceImplCustomcreateAddPetOp[] -// tag::PetServiceImplCustomclass-end[] + // end::snippet_12[] } - // end::PetServiceImplCustomclass-end[] -// end::PetServiceImplCustomclass-declaration[] } + From 058d9c79f25f6b186d9e9f8851c7aeb8cf4ba7da Mon Sep 17 00:00:00 2001 From: Romain Grecourt Date: Wed, 7 Aug 2024 11:16:58 -0700 Subject: [PATCH 17/17] - Use snippet_1 as the tag name in mp/openapi/OpenApiGeneratorSnippets.java - Inline table of content from includes/openapi/openapi-generator.adoc to remove warning - Remove unused attribute {snippets} - Add new line at EOF in se/openapi/openapi-generator.adoc - Remove empty line in mp/openapi/openapi-generator.adoc --- .../includes/openapi/openapi-generator.adoc | 8 ----- .../mp/openapi/openapi-generator.adoc | 11 ++++-- .../se/openapi/openapi-generator.adoc | 11 ++++-- .../mp/openapi/OpenApiGeneratorSnippets.java | 35 ++++++++++--------- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc index 6203315c789..1680dc60707 100644 --- a/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/includes/openapi/openapi-generator.adoc @@ -22,14 +22,6 @@ // end::preamble[] // tag::intro[] -== Contents - -- <> -- <> -- <> -- <> -- <> - == Overview The link:{openapi-spec-url}[OpenAPI specification] provides a standard way to express RESTful APIs. diff --git a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc index 901a57bb620..4c506b6a736 100644 --- a/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/mp/openapi/openapi-generator.adoc @@ -26,7 +26,13 @@ include::{rootdir}/includes/mp.adoc[] :helidon-client-xref: {restclient-page} -include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=preamble] +== Contents + +- <> +- <> +- <> +- <> +- <> include::{rootdir}/includes/openapi/openapi-generator.adoc[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;using-generated-code-client-intro] @@ -40,8 +46,7 @@ In the following example, `ExampleResource` (itself running in a server) invokes [source,java] .Using the generated `PetApi` returned from a separate service ---- -include::{sourcedir}/mp/openapi/OpenApiGeneratorSnippets.java[tag=class-declaration, indent=0] - +include::{sourcedir}/mp/openapi/OpenApiGeneratorSnippets.java[tag=snippet_1, indent=0] ---- <1> Uses a bean-defining annotation so CDI can inject into this class. <2> Requests that CDI inject the following field. diff --git a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc index 60048d6e898..9b4332b2854 100644 --- a/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc +++ b/docs/src/main/asciidoc/se/openapi/openapi-generator.adoc @@ -24,9 +24,16 @@ include::{rootdir}/includes/se.adoc[] -:snippets: {sourcedir}/se/openapi/OpenApiGeneratorSnippets.java :helidon-client-xref: {webclient-page} +== Contents + +- <> +- <> +- <> +- <> +- <> + include::{rootdir}/includes/openapi/openapi-generator.adoc[tags=intro;coords;config;usage-basics;usage-grouping-intro;usage-grouping-server] The generator creates Helidon routing logic based on the longest common path prefix shared among the operations that are grouped into each API. @@ -286,4 +293,4 @@ This code also blocks the current thread, first to wait for the initial response include::{rootdir}/includes/openapi/openapi-generator.adoc[tag=common-references] -* xref:{helidon-client-xref}[Helidon WebClient documentation] \ No newline at end of file +* xref:{helidon-client-xref}[Helidon WebClient documentation] diff --git a/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java b/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java index 0b75bbd915e..b538784d3cb 100644 --- a/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java +++ b/docs/src/main/java/io/helidon/docs/mp/openapi/OpenApiGeneratorSnippets.java @@ -24,7 +24,8 @@ import org.eclipse.microprofile.rest.client.inject.RestClient; -public class OpenApiGeneratorSnippets { +@SuppressWarnings("ALL") +class OpenApiGeneratorSnippets { /* @@ -55,7 +56,7 @@ public class OpenApiGeneratorSnippets { -i etc/petstorex.yaml \ -p helidonVersion=${helidonVersion} - */ + */ interface Pet { } @@ -67,21 +68,23 @@ interface PetApi { class ApiException extends Exception { } -// tag::class-declaration[] - @Path("/exampleServiceCallingService") // <1> - public class ExampleOpenApiGenClientResource { - @Inject // <2> - @RestClient // <3> - private PetApi petApi; // <4> + class Snippet1 { - @GET - @Path("/getPet/{petId}") - @Produces(MediaType.APPLICATION_JSON) - public Pet getPetUsingId(@PathParam("petId") Long petId) throws ApiException { - Pet pet = petApi.getPetById(petId); // <5> - return pet; + // tag::snippet_1[] + @Path("/exampleServiceCallingService") // <1> + public class ExampleOpenApiGenClientResource { + @Inject // <2> + @RestClient // <3> + private PetApi petApi; // <4> + + @GET + @Path("/getPet/{petId}") + @Produces(MediaType.APPLICATION_JSON) + public Pet getPetUsingId(@PathParam("petId") Long petId) throws ApiException { + Pet pet = petApi.getPetById(petId); // <5> + return pet; + } } + // end::snippet_1[] } -// end::class-declaration[] - } \ No newline at end of file