From a9b12a9919838f7745ff2b2ac3108eeca654f7cb Mon Sep 17 00:00:00 2001 From: "tim.quinn@oracle.com" Date: Wed, 26 Oct 2022 10:01:40 -0500 Subject: [PATCH 1/5] Andrii's doc plus additions and refactoring --- docs/includes/attributes.adoc | 25 +- docs/includes/openapi/openapi-generator.adoc | 511 +++++++++++++++++++ docs/includes/openapi/openapi-overview.adoc | 25 + docs/includes/openapi/openapi.adoc | 210 ++++++++ docs/includes/pages.adoc | 8 +- docs/mp/introduction.adoc | 12 +- docs/mp/openapi/openapi-generator.adoc | 71 +++ docs/mp/openapi/openapi-overview.adoc | 34 ++ docs/mp/openapi/openapi.adoc | 273 ++++++++++ docs/se/health.adoc | 14 +- docs/se/introduction.adoc | 15 +- docs/se/openapi/openapi-generator.adoc | 163 ++++++ docs/se/openapi/openapi-overview.adoc | 30 ++ docs/se/openapi/openapi.adoc | 124 +++++ docs/se/tracing.adoc | 18 +- docs/sitegen.yaml | 24 +- 16 files changed, 1506 insertions(+), 51 deletions(-) create mode 100644 docs/includes/openapi/openapi-generator.adoc create mode 100644 docs/includes/openapi/openapi-overview.adoc create mode 100644 docs/includes/openapi/openapi.adoc create mode 100644 docs/mp/openapi/openapi-generator.adoc create mode 100644 docs/mp/openapi/openapi-overview.adoc create mode 100644 docs/mp/openapi/openapi.adoc create mode 100644 docs/se/openapi/openapi-generator.adoc create mode 100644 docs/se/openapi/openapi-overview.adoc create mode 100644 docs/se/openapi/openapi.adoc diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index a42b2e9f826..0bbcf8fadf3 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -23,7 +23,7 @@ ifndef::attributes-included[] // functional attributes :imagesdir: {rootdir}/images -:helidon-version: 3.0.3-SNAPSHOT +:helidon-version: 3.0.0-SNAPSHOT :helidon-version-is-release: false ifeval::["{helidon-version-is-release}" != "true"] @@ -187,14 +187,14 @@ endif::[] // Helidon component javadoc base URLs :config-javadoc-base-url: {javadoc-base-url}/io.helidon.config -:configurable-javadoc-base-url: {javadoc-base-url}/io.helidon.common.configurable -:faulttolerance-javadoc-base-url: {javadoc-base-url}/io.helidon.faulttolerance +:configurable-javadoc-base-url: {javadoc-base-url}/apidocs/io.helidon.common.configurable +:faulttolerance-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.faulttolerance :grpc-server-javadoc-base-url: {javadoc-base-url}/io.helidon.grpc.server :health-javadoc-base-url: {javadoc-base-url}/io.helidon.health.checks :integration-oci-sdk-cdi-javadoc-base-url: {javadoc-base-url}/io.helidon.integrations.oci.sdk.cdi -:media-jsonp-javadoc-base-url: {javadoc-base-url}/io.helidon.media.jsonp -:media-jsonb-javadoc-base-url: {javadoc-base-url}/io.helidon.media.jsonb -:media-jackson-javadoc-base-url: {javadoc-base-url}/io.helidon.media.jackson +:media-jsonp-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.media.jsonp +:media-jsonb-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.media.jsonb +:media-jackson-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.media.jackson :metrics-javadoc-base-url: {javadoc-base-url}/io.helidon.metrics.api :metrics-mp-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.metrics :metrics-serviceapi-javadoc-base-url: {javadoc-base-url}/io.helidon.metrics.serviceapi @@ -208,11 +208,11 @@ endif::[] :scheduling-javadoc-base-url: {javadoc-base-url}/io.helidon.microprofile.scheduling :security-integration-jersey-base-url: {javadoc-base-url}/io.helidon.security.integration.jersey :security-integration-webserver-base-url: {javadoc-base-url}/io.helidon.security.integration.webserver -:webclient-javadoc-base-url: {javadoc-base-url}/io.helidon.webclient -:webserver-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver -:webserver-jersey-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver.jersey +:webclient-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.webclient +:webserver-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.webserver +:webserver-jersey-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.webserver.jersey :webserver-staticcontent-javadoc-base-url: {webserver-javadoc-base-url}.staticcontent -:webserver-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.webserver.cors +:webserver-cors-javadoc-base-url: {javadoc-base-url}/io.helidon.reactive.webserver.cors :graphql-javadoc-base-url: {javadoc-base-url}/io.helidon.graphql.server :security-provider-oidc-base-url: {javadoc-base-url}/io.helidon.security.providers.oidc :security-provider-httpauth-base-url: {javadoc-base-url}/io.helidon.security.providers.httpauth @@ -262,4 +262,9 @@ endif::[] :micrometer-javadoc-base-url: https://javadoc.io/doc/io.micrometer :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-tool-base-url: https://github.com/OpenAPITools/openapi-generator +:openapi-generator-tool-site-url: https://openapi-generator.tech + endif::attributes-included[] diff --git a/docs/includes/openapi/openapi-generator.adoc b/docs/includes/openapi/openapi-generator.adoc new file mode 100644 index 00000000000..556528ae33d --- /dev/null +++ b/docs/includes/openapi/openapi-generator.adoc @@ -0,0 +1,511 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2022 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. + +/////////////////////////////////////////////////////////////////////////////// +// tag::preamble[] +:toc: +:toc-placement: preamble +: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 +: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 +:generator-version: 6.2.1 +:generator-site-version: v{openapi-generator-version} +// end::preamble[] + +// tag::intro[] +== Contents + +- <> +- <> +- <> +- <> +- <> + +== Overview +The link:{openapi-spec-url}[OpenAPI specification] provides a standard way to express RESTful APIs. + +Separately, the link:{openapi-generator-tool-site-url}[OpenAPI generator] project has created a powerful code generator tool that 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 gained particularly strong support for Helidon in release +{first-version-with-strong-helidon-support}. +This document applies to that release and later ones. + +In the vocabulary of the tool, there are two _generators_ for Helidon: + +* `java-helidon-client` (which this document refers to as the Helidon client generator) +* `java-helidon-server` (Helidon server generator). + +Each of these generators supports two _libraries_: + +* `mp` - for Helidon MP code generation +* `se` - for Helidon SE code generation + +Use the Helidon _client_ generator and its `{flavor-lc}` library to create a +ifdef::mp-flavor[] +xref:{helidon-client-xref}[Helidon MicroProfile REST client]. +endif::mp-flavor[] +ifdef::se-flavor[] +Helidon SE client based on xref:{helidon-client-xref}[Helidon WebClients]. +endif::se-flavor[] +The resulting client library works with any server that implements the API declared in the OpenAPI document. +The library provides an abstraction similar to remote procedure calls (RPC). To access a remote service that implements the endpoints declared in the OpenAPI document, your code uses the generated library to establish a connection to the remote service and then calls remote service endpoints by invoking a local API using POJO business objects. + +Use the tool's Helidon _server_ generator and its `{flavor-lc}` library to create server endpoint stubs for a Helidon {flavor-uc} service. You build on these stubs by extending a generated abstract class or implementing a generated interface, adding your specific business logic to finish the implementation of the endpoints in your API. The combination of the generated server code with Helidon {flavor-uc}} underneath it allows you to focus on the business details instead of the networking. + +You can run the OpenAPI generators in three ways: + +// tag::three-ways-to-run[] +* using the OpenAPI generator CLI +* using the OpenAPI generator Maven or Gradle plug-in +* using the online OpenAPI generator website +// end::three-ways-to-run[] + +The rest of this document walks you through <> each technique and how to <> the generators to produce code as you want. + +// end::intro[] + +// tag::coords[] + +== Maven Coordinates +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. You can choose whichever version of the generator plug-in meets your needs as long as it is at least {first-version-with-strong-helidon-support}. + +[source,xml,subs="+attributes"] +.Declare the OpenAPI generator Plug-in +---- + + {generator-version} + +... + + ... + + ... + + org.openapitools + openapi-generator-maven-plugin + $\{openapi-generator-version} + + ... + + ... + +---- + +Your project does not need dependencies on the OpenAPI generator itself. + +// end::coords[] + +// tag::config[] + +== Configuration + +The OpenAPI generators support a substantial, powerful, and sometimes bewildering group of configuration settings. +[[links-to-settings]]For complete lists see the +link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-client.md[Helidon client generator options] and +link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-server.md[Helidon server generator options] pages. + +The OpenAPI generator divides its settings into two types: + +* _options_ ++ +These high-level settings generally govern the overall behavior of the tool. ++ +For the CLI, use the common option style: ++ +`-o target/generated-sources` ++ +`--output target/generated-sources` ++ +For the Maven plug-in, use elements within the `` section of the plug-in: ++ +[source,xml] +---- + + ${project.build.directory}/generated-sources + +---- +* _additional properties_ ++ +These settings typically affect how a specific generator or library behaves. ++ +For the CLI: ++ +`--additional-properties "useAbstractClasses=false,returnResponse=true"` ++ +or ++ +[source,bash] +---- +-p useAbstractClasses=false +-p returnResponse=true +---- ++ +For the Maven plug-in, use elements within the `` section: ++ +[source,xml] +---- + + .... + + false + true + + +---- + +Keep this distinction between options and additional properties in mind so you know how to express the configuration you want. +The <> to the lists of configuration options for the Helidon generators groups options and additional properties in separate tables. + +=== Required Options + +You must specify the following options: + +.Required OpenAPI Generator Options +[cols="4,1,6,5"] +|=== +|Option / Plug-in Setting | Short Option | Description | Values + +|`--inputSpec` + +`` +|`-i` +|Path to the OpenAPI document defining the REST API +| + +|`--generatorName` + +`` +|`-g` +| Generator you want to use (`java-helidon-server` or `java-helidon-client`) +| `java-helidon-server` + +`java-helidon-client` + +|`--library` + +`` +| +|Library you want to use +|`mp` + +`se` +|=== + +=== Recommended Settings +Your project might have different needs, but in general we advise developers to use the following settings. + +.Recommended OpenAPI Generator Options +[cols="4,1,6,5"] +|=== +|Option |Short Option | Description |Default + +|`--output` + +`` +|`-o` +| Directory where the generator should place files. + +We strongly recommend `-o target/generated-sources` or a subdirectory below there. +| `.` + +(current directory) + +| `` +(plug-in only) +| +| Whether Maven should include the output directory as a source root (include it automatically in the build). + +We advise `true`. +| `false` +|=== + +.Recommended OpenAPI Generator Additional Properties +[cols="3,6,5"] +|=== +| Property | Description | Default + +| `apiPackage` +| Name of the package for generated API interfaces/classes +| `org.openapitools.server.api` or + +`org.openapitools.client.api` + +| `modelPackage` +| Name of the package for generated model classes +| `org.openapitools.server.model` or + +`org.openapitools.client.model` + +| `invokerPackage` +| Name of the package for generated driver classes +| `org.openapitools.server` or + +`org.openapitools.client` + +| `groupId` +| Group ID in the generated `pom.xml` +| `org.openapitools` + +| `artifactId` +| Artifact ID in the generated `pom.xml` +| `openapi-java-server` or + +`openapi-java-client` + +| `artifactVersion` +| Artifact version in the generated `pom.xml` +| `1.0.0` +|=== + +=== Common Settings +Among the many configuration settings available to you, the table below includes several you should particularly consider. Refer to the <> for complete lists. + +.Common OpenAPI Generator Additional Properties +[cols="4,6,3,3,6"] +|=== +|Property |Description |Values |Default |Notes + +| `helidonVersion` + +|Version of Helidon for which to generate the files +| +|`2.5.2` +a|Affects: + +* Helidon version for the `` +* Dependencies (`javax` vs. `jakarta`) +* `java import` statements in generated code (`javax` vs. `jakarta`) + +|`fullProject` +|Whether to generate all the normal files or only API files +|`true`/`false` +|`false` +| The "API files" include files developers do not normally modify after they are generated: the interfaces or abstract classes for the declared API +and the model classes. + +|`serializationLibrary` +|which Java library to use for serializing JSON +|`jsonb`, `jackson` +|`jackson` +| +|=== + + +// end::config[] + +// tag::usage[] + +[[usage-section]] +== Usage + +This section covers two major topics: + +* <> +* <> + +[[usage-planning]] +=== Planning Your Use of the OpenAPI Generators + +Beyond the conventions listed above, there are several important choices you need to make when planning your project and running the OpenAPI generators which this section describes. + +==== Generating a New Project and Generating _Into_ an Existing Project + +You can use the OpenAPI generator to create a new project or to generate files into an existing project. +Some developers do both, using the generator to create the project at first and then to update the project as they evolve the OpenAPI document or change the generation options they select. The OpenAPI generator CLI and plug-in both support both types of generation. + +Once you have an existing project (either from using the OpenAPI generator or from creating your project in another way), you can run the generator any number of times to update generated files inside your project to account for changes in your OpenAPI document changes or in the generation options you choose. + +Unlike with the generated API or model files, if the generator detects an existing `pom.xml` it does not overwrite it. +Certain generation options can influence the generated dependencies in the `pom.xml` file--for example, the `serializationLibrary` setting creates dependencies on either JSON-B or Jackson artifacts. +As a result, changing the generation options can change the dependencies your project should have. +To see the changes in the dependencies which the generator infers: + +* Be sure to follow the recommendation mentioned above to generate output into `generated-sources`. +* Include `clean` in your `mvn` command. This, combined with the previous step, removes previously-generated files including the `pom.xml` from `generated-sources`. +* Run the generator again. +The newly-created `generated-sources/pom.xml` file shows the dependencies the generator inferred from the most recent options. You can manually update your project's `pom.xml` accordingly. + + + +==== Generating Interfaces or Abstract Classes +As you generate a Helidon {flavor-uc} server, you can choose whether to create Java interfaces or abstract classes to represent the RESTful API endpoints. + +By default, the Helidon OpenAPI server generator creates abstract classes. +You write your own concrete subclasses which extend those generated abstract classes, supplying the business logic for each REST endpoint. +_Do not_ modify the generated abstract classes. + +If you set `useAbstractClass=false` then the generator creates Java interfaces instead of abstract classes. +You write classes which implement the generated interfaces. + +Either way, you can safely regenerate the code later--for example if your OpenAPI document has changed--so long as you have not edited the generated code (which you should not do). +The generator replaces the abstract classes or interfaces but does not touch other classes you wrote. + +The Helidon client generator always creates concrete classes. Typically, you do not need to customize the behavior in the generated client API classes, but if you choose to do so, write your own subclass of the generated client API class; _do not_ modify the generated file. + +==== Grouping Operations into "APIs" +Each operation in an OpenAPI document can have a `tags` attribute. +The generator groups operations with the same `tags` value into the same API. + +When you generate a Helidon {flavor-uc} server, the generator creates a separate interface or abstract class for each API. +You implement each interface or extend each abstract class. + +A generated client contains a separate API class for each distinct API. + +[[usage-running]] +=== Running the OpenAPI Generators + +This sections explains the various ways you can run the OpenAPI generators + +You already know there are many settings--as command-line options or configuration of the generator's Maven plug-in--with which you control how the generators work. The examples in this document adopt a few conventions we encourage you to follow. + +Earlier we listed the ways you can run the OpenAPI generator: + +include::openapi-generator.adoc[tag=three-ways-to-run] + +The next sections describe each in detail. + +==== Using the OpenAPI Generator CLI + +[NOTE] +.Downloading the OpenAPI Generator CLI +You need to download the CLI `.jar` file before you can run the CLI. +Follow these link:https://github.com/OpenAPITools/openapi-generator#13---download-jar[instructions] and remember where you save the `.jar` file. +This document uses the placeholder `path-to-generator` to represent the directory where you store that downloaded file. + +The following example uses the Helidon server generator to create a project or regenerate files into an existing project. + +:example-project-type: server +.Creating or updating a server project using the OpenAPI generator CLI +// tag::example-cli-usage[] +[source,bash,subs="attributes+"] +---- +java -jar path-to-generator/openapi-generator-cli.jar \ + generate \ + -i petstore.yaml \ + -g java-helidon-{example-project-type} \ + --library {flavor-lc} \ + -o target/generated-sources/{example-project-type} \ + -p groupId=io.helidon.examples \ + -p artifactId=helidon-openapigen-{flavor-lc}-{example-project-type} \ + -p artifactVersion=1.0.0-SNAPSHOT \ + -p apiPackage=io.helidon.examples.openapigen.{flavor-lc}.{example-project-type}.api \ + -p modelPackage=io.helidon.examples.openapigen.{flavor-lc}.{example-project-type}.model \ + -p invokerPackage=io.helidon.examples.openapigen.{flavor-lc}.{example-project-type} +---- +// end::example-cli-usage[] + +The next example runs the Helidon client generator using the same input file. + +:example-project-type: client +.Creating or updating a client project using the OpenAPI generator CLI +include::openapi-generator.adoc[tag=example-cli-usage] + +The key differences between the commands are: + +* the generator selected by the `-g` option, +* the output directory, and +* the artifact ID and package names (`client` vs. `server`). + +You could use these two commands to generate a server submodule and a client submodule in a Maven multi-module project. + +You can use the resulting client project to interact with any server which implements the API described in the `petstore.yaml` OpenAPI document, whether it was generated using the OpenAPI generator tool or not. + +In both examples, the generator creates the entire project if it does not exist and recreates the generated files if the project already exists. +The generator does not overwrite an existing `pom.xml` file, previously-generated test files, or files you created yourself. + + +==== Invoking the OpenAPI Generator Maven Plug-in + +You can run the OpenAPI generator plug-in as part of your project build. + +First, declare the plug-in as explained in the earlier <>. + +Then, in the `` section of your `pom.xml` file add an execution of the plug-in with the configuration you want. By default, the plug-in runs during the `generate-sources` phase of the Maven build. + + +For example, this Maven plugin can be used to generate/regenerate files (API interfaces or abstract classes that represent REST endpoints, models and other files that the end user does not modify) : + +[source,xml] +---- + + org.openapitools + openapi-generator-maven-plugin + ${openapi-generator-version} + + + + generate + + + ${project.basedir}/src/main/resources/petstore.yaml + java-helidon-server + se + + false + + + + + +---- + +==== Using the Online Generator + +The OpenAPI tools project hosts and maintains the online OpenAPI generator at http://api.openapi-generator.tech. You can use the site's API browser to explore the available generators and the settings each supports, expressed as JSON. + +To generate your project, you supply the options and additional properties as JSON. The online generator provides you with a file ID which you then use to retrieve it as a single file. + +This document does not explore further the use of the online generator. + +// end::usage[] + + +// tag::using-generated-code-intro[] +[[using-generated-code]] +== Using the Generated Code + +The Helidon generators go a long way in helping you write your client or server. Even so, there are important parts of your project only you can provide. This section describes your next steps _after_ you have run the generator. +// end::using-generated-code-intro[] + +// tag::using-generated-code-server[] + +=== Completing the Server +Recall earlier how the OpenAPI generator gathers operations into one or more "APIs" and generates either an abstract class or an interface--your choice--for each API. +You need to extend each generated abstract API class or implement each generated API interface. +Any input parameters to the endpoints are expressed as POJO model objects or Java types, as declared in the OpenAPI document. So your code uses each of the input parameters to accomplish whatever business purpose that endpoint is responsible for, possibly returning a result also as a POJO or Java type. + +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 +ifdef::mp-flavor[] +the Jakarta RESTful web services `Response` +endif::mp-flavor[] +ifdef::se-flavor[] +the Helidon SE `ServerResponse` +endif::se-flavor[] +and your code has complete control--and therefore responsibility--over setting the status, writing the response entity (if any), and assigning any returned headers. + +Your code plus the server code from the Helidon generator--all running on Helidon {flavor-uc}--combine to implement fully the server API declared in the original API document. Build your project to get a tailored Helidon {flavor-uc} server `.jar` file or Docker image that is ready to run. + +// end::using-generated-code-server[] + +// tag::using-generated-code-client-intro[] +=== Using the Client Library + +The generated client code represents a true library. Typically, you do not need to customize the generated client code itself. You _do_ need to write code to invoke the code in that library. + +// using the SE and MP clients are so different there's not much common content. + +// end::using-generated-code-client-intro[] + +// tag::common-references[] +== References +* link:{openapi-generator-tool-site-url}[OpenAPI Generator Official Website] +* link:{openapi-generator-tool-base-url}[OpenAPI Generator GitHub Repository] +* link:{openapi-spec-url}[OpenAPI specification] +// end::common-references[] \ No newline at end of file diff --git a/docs/includes/openapi/openapi-overview.adoc b/docs/includes/openapi/openapi-overview.adoc new file mode 100644 index 00000000000..3bcaaa1d8a5 --- /dev/null +++ b/docs/includes/openapi/openapi-overview.adoc @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2022 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] + +Brief discussion plus links to + +* OpenAPI in Helidon +* Generator +* U/I diff --git a/docs/includes/openapi/openapi.adoc b/docs/includes/openapi/openapi.adoc new file mode 100644 index 00000000000..e0a7952378f --- /dev/null +++ b/docs/includes/openapi/openapi.adoc @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2022 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. + +/////////////////////////////////////////////////////////////////////////////// + +ifndef::rootdir[:rootdir: {docdir}/..] + +// tag::overview[] +The link:{openapi-spec-url}[OpenAPI specification] defines a standard way to express the interface exposed by a REST service. + +The link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec] explains how MicroProfile embraces OpenAPI, adding annotations, configuration, and a service provider interface (SPI). + +ifdef::mp-flavor[Helidon {flavor-uc} implements the MicroProfile OpenAPI specification.] +ifdef::se-flavor[OpenAPI support in Helidon {flavor-uc} draws its inspiration from MicroProfile OpenAPI but does not implement the spec because Helidon {flavor-uc} does not support annotations.] + +The OpenAPI support in Helidon {flavor-uc} performs two main tasks: + +* Build an in-memory model of the REST API your service implements. +* Expose the model in text format (typically YAML) via the `/openapi` endpoint. + +To construct the model, Helidon gathers information about the service API from whichever of these sources are present in the application: + +* a _model reader_ ++ +The SPI defines an interface you can implement in your application for programmatically providing part or all of the model; +* a static OpenAPI document file packaged as part of your service; +ifdef::mp-flavor[] +* OpenAPI annotations; +endif::[] +* a _filter_ class ++ +The SPI defines an interface you can implement in your application which can mask parts of the model. + + +// end::overview[] + +// tag::furnish-openapi-info[] + +==== Furnish OpenAPI information about your endpoints +// It's a bit odd to intermix the SE and MP content in this common file this way. +// But I tried having a level 3 section in the SE file include a sequence of +// level 4 sections from here, and that led to errors with headers being out of sequence. +// With the entire level 3 section here and conditional text for SE and MP, AsciiDoctor is happy. +ifdef::se-flavor[] +OpenAPI support in Helidon SE largely follows the link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec]. +But because Helidon SE does not process annotations, your application supplies data for the OpenAPI model in the other ways listed earlier. +endif::[] + +ifdef::mp-flavor[] +Helidon MP OpenAPI combines information from all of the following sources as it +builds its in-memory model of your application's API. It constructs the OpenAPI +document from this internal model. Your application can use one or more of these +techniques. + +===== Annotate the endpoints in your app +You can add MicroProfile OpenAPI annotations to the endpoints in your source code. +These annotations allow the Helidon MP OpenAPI runtime to discover the endpoints +and information about them via CDI at app start-up. + +Here is one of the endpoints, annotated for OpenAPI, from the example mentioned earlier: + +[source,java] +---- +@GET +@Operation(summary = "Returns a generic greeting", // <1> + description = "Greets the user generically") +@APIResponse(description = "Simple JSON containing the greeting", // <2> + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = GreetingMessage.class))) +@Produces(MediaType.APPLICATION_JSON) +public JsonObject getDefaultMessage() {...} +---- +<1> `@Operation` gives information about this endpoint. +<2> `@APIResponse` describes the HTTP response and declares its media type and contents. + +You can also define any request parameters the endpoint expects, although this +endpoint uses none. + +This excerpt shows only a few annotations for illustration. The +link:{helidon-github-tree-url}/examples/microprofile/openapi-basic[Helidon MP OpenAPI example] illustrates more, +and the link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI spec] describes them all. + +===== Provide a static OpenAPI file +Add a static file at `META-INF/openapi.yml`, `META-INF/openapi.yaml`, +or `META-INF/openapi.json`. Tools such as Swagger let you describe your app's API +and they then generate an OpenAPI document file which you can include in your application +so OpenAPI can use it. + +===== Write and configure a model reader class +Write a Java class that implements the OpenAPI +link:{microprofile-open-api-javadoc-url}/OASModelReader.html[`org.eclipse.microprofile.openapi.OASModelReader`] interface. Your +model reader code programmatically adds elements to the internal model that OpenAPI +builds. + +endif::[] + +===== Provide a static OpenAPI file +Add a static file at `META-INF/openapi.yml`, `META-INF/openapi.yaml`, +or `META-INF/openapi.json`. Tools such as Swagger let you describe your app's API +and they then generate an OpenAPI document file which you can include in your application +so OpenAPI can use it. + +===== Write and configure a model reader class +Write a Java class that implements the OpenAPI +link:{microprofile-open-api-javadoc-url}/OASModelReader.html[`org.eclipse.microprofile.openapi.OASModelReader`] interface. Your +model reader code programmatically adds elements to the internal model that OpenAPI +builds. + +Change your application's MP configuration to set `mp.openapi.model.reader` as the +fully-qualified class name of your class. + +===== Write and configure a filter class +Write a Java class that implements the OpenAPI +link:{microprofile-open-api-javadoc-url}/OASFilter.html[`org.eclipse.microprofile.openapi.OASFilter`] interface. +As OpenAPI composes its internal model, it invokes your filter with each +model element _before_ adding the element to the model. Your filter can +accept the element as-is, modify it, or suppress it. + +Change your application's configuration to set `mp.openapi.filter` as the full-qualified +class name of your class. + +// end::furnish-openapi-info[] + +// tag::usage-access-endpoint[] +=== Accessing the REST Endpoint +Once you add the {flavor-uc} OpenAPI dependency to your +ifdef::mp-flavor[project,] +ifdef::se-flavor[project and add code to create the `OpenAPISupport` object to your routing,] +your application will automatically respond to the built-in endpoint -- +`/openapi` -- and it will return the OpenAPI document describing the endpoints +in your application. + +By default, per the MicroProfile OpenAPI spec, the default format of the OpenAPI document is YAML. +There is not yet an adopted IANA YAML media type, but a proposed one specifically +for OpenAPI documents that has some support is `application/vnd.oai.openapi`. +That is what Helidon returns, by default. + +In addition, a client can specify the HTTP header `Accept` as either `application/vnd.oai.openapi+json` or +`application/json` to request JSON. Alternatively, the client can pass the query parameter `format` as either `JSON` +or `YAML` to receive `application/json` or `application/vnd.oai.openapi` (YAML) output, respectively. +// end::usage-access-endpoint[] + +// tag::api[] +ifdef::mp-flavor[] +The link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI specification] gives a listing and brief examples of the annotations you can add to your code to convey OpenAPI information. +endif::[] + +The link:{microprofile-open-api-javadoc-base-url}[MicroProfile OpenAPI JavaDocs] give full details of the +ifdef::mp-flavor[annotations and the other] +classes and interfaces you can use in your code. +ifdef::se-flavor[] +Remember that, although the JavaDocs describe annotations, Helidon {flavor-uc} does not support them. +endif::[] + +// end::api[] + + +// tag::additional-building-jandex[] + +=== Building the Jandex index + +A Jandex index stores information about the classes and methods in your app and +what annotations they have. It allows CDI to process annotations faster during your +application's start-up. + +Add the link:https://github.com/wildfly/jandex-maven-plugin[Jandex maven plug-in] to the `` +section of your `pom.xml`: + +[source,xml,subs="attributes+"] +---- + + org.jboss.jandex + jandex-maven-plugin + {jandex-plugin-version} + + + make-index + + jandex + + + + +---- +When you build your app `maven` should include the index `META-INF/jandex.idx` in +the JAR. + +[NOTE] +==== +If you _do not_ modify your build to create +the index then the Helidon MP OpenAPI runtime automatically creates one in memory during +app start-up. This slows down your app start-up and, depending on how CDI is +configured, might inadvertently miss information. + +We _strongly recommend_ using the Jandex plug-in to build the index into your app. +==== +// end::additional-building-jandex[] \ No newline at end of file diff --git a/docs/includes/pages.adoc b/docs/includes/pages.adoc index b31b6acb5b1..8fb99fe60d5 100644 --- a/docs/includes/pages.adoc +++ b/docs/includes/pages.adoc @@ -18,10 +18,12 @@ ifdef::se-flavor[] :health-page: {rootdir}/se/health.adoc :metrics-page: {rootdir}/se/metrics/metrics.adoc -:openapi-page: {rootdir}/se/openapi.adoc +:openapi-page: {rootdir}/se/openapi/openapi.adoc endif::[] ifdef::mp-flavor[] :health-page: {rootdir}/mp/health.adoc :metrics-page: {rootdir}/mp/metrics/metrics.adoc -:openapi-page: {rootdir}/mp/openapi.adoc -endif::[] \ No newline at end of file +:openapi-page: {rootdir}/mp/openapi/openapi.adoc +endif::[] +:webclient-page: {rootdir}/se/webclient.adoc +:restclient-page: {rootdir}/mp/restclient.adoc diff --git a/docs/mp/introduction.adoc b/docs/mp/introduction.adoc index d0cc35f6f94..03d0e8ebdc6 100644 --- a/docs/mp/introduction.adoc +++ b/docs/mp/introduction.adoc @@ -22,11 +22,11 @@ :rootdir: {docdir}/.. include::{rootdir}/includes/mp.adoc[] +include::{rootdir}/includes/pages.adoc[] == Introduction - -Helidon MP includes a compatible implementation of Eclipse MicroProfile {version-lib-microprofile-api} . MicroProfile is a set of specifications created by the Eclipse Foundation, MicroProfile Community that support application portability between different compatible implementation providers. Additionally, MicroProfile requires compatible implementations of certain component specifications from the Eclipse Jakarta EE community as well. +Helidon MP is an Eclipse MicroProfile {version-lib-microprofile-api} runtime that allows the Jakarta EE community to run microservices in a portable way. It designed for ease of use and provides Spring Boot like development experience with heavily usage of dependency injection and annotations. == Supported Jakarta EE Specifications @@ -101,7 +101,7 @@ Helidon MP includes a compatible implementation of Eclipse MicroProfile {version | link:{microprofile-metrics-spec-url}[{version-lib-microprofile-metrics-api}] | Defining and exposing telemetry data in Prometheus and JSON formats -| xref:{rootdir}/mp/openapi.adoc[MicroProfile Open API] +| xref:{openapi-page}[MicroProfile Open API] | link:{microprofile-open-api-spec-url}[{version-lib-microprofile-openapi-api}] | Annotations for documenting your application endpoints @@ -147,13 +147,13 @@ Helidon MP includes a compatible implementation of Eclipse MicroProfile {version == Migration -Use the following migration guides to upgrade your current Helidon version. +In case you need to upgrade the version of Helidon, follow the `Migration Guides`. -To migrate from Helidon 1.x to 2.x: +For migration from Helidon 1.x to 2.x: * xref:{rootdir}/mp/guides/migration.adoc[Helidon MP 2x Migration Guide] -To migrate from Helidon 2.x to 3.x: +For migration from Helidon 2.x to 3.x: * xref:{rootdir}/mp/guides/migration_3x.adoc[Helidon MP 3x Migration Guide] diff --git a/docs/mp/openapi/openapi-generator.adoc b/docs/mp/openapi/openapi-generator.adoc new file mode 100644 index 00000000000..d6b6d6ef2bc --- /dev/null +++ b/docs/mp/openapi/openapi-generator.adoc @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2022 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-based Code Generation + +:toc: +:toc-placement: preamble +:description: Helidon MP OpenAPI Generator +:keywords: helidon, mp, microprofile, openapi, generator +:feature-name: MicroProfile OpenAPI Generator +:rootdir: {docdir}/../.. +:incdir: {rootdir}/includes/openapi +:gen-inc: {incdir}/openapi-generator.adoc +include::{rootdir}/includes/pages.adoc[] +:helidon-client-xref: {restclient-page} + +include::{gen-inc}[tag=preamble] + +include::{rootdir}/includes/mp.adoc[] + +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` from a separate service +---- +@Path("/example") // <1> +public class ExampleResource { + + @Inject // <2> + @RestClient // <3> + private PetApi petApi; // <4> + + @GET + @Path("/getPet/{petId}") + public Response getPetUsingId(@PathParam("petId") Long petId) { + Pet pet = petApi.getPetById(petId); // <5> + //... + } +} +---- +<1> Uses a bean-defining annotation so CDI can inject into this class. +<2> Requests that CDI inject the following +<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/mp/openapi/openapi-overview.adoc b/docs/mp/openapi/openapi-overview.adoc new file mode 100644 index 00000000000..bd1ca1fa1df --- /dev/null +++ b/docs/mp/openapi/openapi-overview.adoc @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2022 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 Overview +:toc: +:toc-placement: preamble +:description: Helidon MP OpenAPI Support +:keywords: helidon, mp, openapi +:feature-name: OpenAPI +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/mp.adoc[] + +Brief discussion plus links to + +* OpenAPI in Helidon +* Generator +* U/I + diff --git a/docs/mp/openapi/openapi.adoc b/docs/mp/openapi/openapi.adoc new file mode 100644 index 00000000000..5e3db05910c --- /dev/null +++ b/docs/mp/openapi/openapi.adoc @@ -0,0 +1,273 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2019, 2022 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 in Helidon +:toc: +:toc-placement: preamble +:description: Helidon MP OpenAPI Support +:keywords: helidon, mp, microprofile, openapi +:feature-name: MicroProfile OpenAPI +:microprofile-bundle: true +:rootdir: {docdir}/../.. +:incdir: {rootdir}/includes/openapi + +include::{rootdir}/includes/mp.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +include::{incdir}/openapi.adoc[tag=overview] + +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml,subs="attributes+"] +---- + + + org.eclipse.microprofile.openapi + microprofile-openapi-api + + + io.helidon.microprofile.openapi + helidon-microprofile-openapi + runtime + + +---- +<1> Defines the MicroProfile OpenAPI annotations so you can use them in your code. +<2> Adds the Helidon MP OpenAPI runtime support. + +== Usage + +=== OpenAPI support in Helidon MP + +You can very simply add support for OpenAPI to your Helidon MP application. This +document shows what changes you need to make to your application and how to access +the OpenAPI document for your application at runtime. + +=== Changing your application + +To use OpenAPI from your Helidon MP app, in addition to adding dependencies as described above: + +1. Furnish OpenAPI information about your application's endpoints. +2. Update your application's configuration (optional). + +include::{incdir}/openapi.adoc[tag=furnish-openapi-info] + +=== Update your application configuration +Beyond the two config properties that denote the model reader and filter, Helidon +MP OpenAPI supports a number of other mandated settings. These are described in the +link:{microprofile-open-api-spec-url}#configuration[configuration section] of the MicroProfile +OpenAPI spec. + +include::{incdir}/openapi.adoc[tag=usage-access-endpoint] + +== API + +include::{incdir}/openapi.adoc[tag=api] + +== Configuration + +Helidon OpenAPI configuration supports the following settings: + +include::{rootdir}/config/io_helidon_microprofile_openapi_MPOpenAPISupport.adoc[leveloffset=+1,tag=config] + +== Examples + +Helidon MP includes a link:{helidon-github-tree-url}/examples/microprofile/openapi-basic[complete OpenAPI example] +based on the MP quick-start sample app. The rest of this section shows, step-by-step, how one might change the original QuickStart service to adopt OpenAPI. + +=== Helidon MP Basic OpenAPI Example + +This example shows a simple greeting application, similar to the one from the +Helidon MP QuickStart, enhanced with OpenAPI support. + +[source,java] +---- +@Path("/greeting") +@PUT +@Operation(summary = "Set the greeting prefix", + description = "Permits the client to set the prefix part of the greeting (\"Hello\")") //<1> +@RequestBody( //<2> + name = "greeting", + description = "Conveys the new greeting prefix to use in building greetings", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = GreetingMessage.class), + examples = @ExampleObject( + name = "greeting", + summary = "Example greeting message to update", + value = "New greeting message"))) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public Response updateGreeting(JsonObject jsonObject) { + ... +} +---- +<1> With `@Operation` annotation we document the current method. +<2> With `@RequestBody` annotation we document the content produced. Internal annotations `@Content`, `@Schema` and + `@ExampleObjects` are used to give more details about the returned data. + +If we want to hide a specific path an `OASFilter` is used. + +The OASFilter interface allows application developers to receive callbacks for various key OpenAPI elements. The + interface has a default implementation for every method, which allows application developers to only override the + methods they care about. To use it, simply create an implementation of this interface and register it using the + `mp.openapi.filter configuration` key, where the value is the fully qualified name of the filter class. + +The following example filter prevents information about a given path from appearing in the OpenAPI document. + +[source, java] +---- +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; + +public class SimpleAPIFilter implements OASFilter { + + @Override + public PathItem filterPathItem(PathItem pathItem) { + for (Map.Entry methodOp + : pathItem.getOperations().entrySet()) { + if (SimpleAPIModelReader.DOOMED_OPERATION_ID + .equals(methodOp.getValue().getOperationId())) { + return null; + } + } + return OASFilter.super.filterPathItem(pathItem); + } +} +---- + +You can implement a model reader to provide all or part of the in-memory `OpenAPI` model programmatically. Helidon + `OpenAPI` merges the model from the model reader with models from the other sources (a static file and annotations). + +The example model reader below creates an `OpenAPI` object describing two paths. It turns out that the filter described +earlier will suppress one of the paths, but the model reader does not know or care. + +[source,java] +---- +import org.eclipse.microprofile.openapi.OASFactory; +import org.eclipse.microprofile.openapi.OASModelReader; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.Paths; + +/** + * Defines two paths using the OpenAPI model reader mechanism, one that should + * be suppressed by the filter class and one that should appear in the published + * OpenAPI document. + */ +public class SimpleAPIModelReader implements OASModelReader { + + /** + * Path for the example endpoint added by this model reader that should be visible. + */ + public static final String MODEL_READER_PATH = "/test/newpath"; + + /** + * Path for an endpoint that the filter should hide. + */ + public static final String DOOMED_PATH = "/test/doomed"; + + /** + * ID for an endpoint that the filter should hide. + */ + public static final String DOOMED_OPERATION_ID = "doomedPath"; + + /** + * Summary text for the endpoint. + */ + public static final String SUMMARY = "A sample test endpoint from ModelReader"; + + @Override + public OpenAPI buildModel() { + /* + * Add two path items, one of which we expect to be removed by + * the filter and a very simple one that will appear in the + * published OpenAPI document. + */ + PathItem newPathItem = OASFactory.createPathItem() + .GET(OASFactory.createOperation() + .operationId("newPath") + .summary(SUMMARY)); + PathItem doomedPathItem = OASFactory.createPathItem() + .GET(OASFactory.createOperation() + .operationId(DOOMED_OPERATION_ID) + .summary("This should become invisible")); + OpenAPI openAPI = OASFactory.createOpenAPI(); + Paths paths = OASFactory.createPaths() + .addPathItem(MODEL_READER_PATH, newPathItem) + .addPathItem(DOOMED_PATH, doomedPathItem); + openAPI.paths(paths); + + return openAPI; + } +} +---- + +Having written the filter and model reader classes, identify them by adding configuration to + `META-INF/microprofile-config.properties` as the following example shows. + +[source,properties] +---- +mp.openapi.filter=io.helidon.microprofile.examples.openapi.basic.internal.SimpleAPIFilter +mp.openapi.model.reader=io.helidon.microprofile.examples.openapi.basic.internal.SimpleAPIModelReader +---- + + +Now just build and run: + +[source,bash] +---- +mvn package +java -jar target/helidon-examples-microprofile-openapi-basic.jar +---- + +Try the endpoints: + +[source,bash] +---- +curl -X GET http://localhost:8080/greet +{"message":"Hello World!"} + +curl -X GET http://localhost:8080/openapi +[lengthy OpenAPI document] +---- + +The output describes not only then endpoints from `GreetResource` but +also one contributed by the `SimpleAPIModelReader`. + +Full example is available link:{helidon-github-tree-url}}/examples/microprofile/openapi-basic[in our official repository] + + +== Additional Information +include::{incdir}/openapi.adoc[tag=additional-building-jandex] + +== Reference + +* link:https://github.com/eclipse/microprofile-open-api[MicroProfile OpenAPI GitHub Repository] +* link:{microprofile-open-api-spec-url}[MicroProfile OpenAPI Specification] diff --git a/docs/se/health.adoc b/docs/se/health.adoc index 0914ecbc479..eac8a348e9b 100644 --- a/docs/se/health.adoc +++ b/docs/se/health.adoc @@ -55,8 +55,8 @@ include::{rootdir}/includes/dependencies.adoc[] [source,xml] ---- - io.helidon.health - helidon-health + io.helidon.reactive.health + helidon-reactive-health ---- @@ -184,11 +184,11 @@ The following table provides a summary of the Health Check API classes. | `org.eclipse.microprofile.health.HealthCheckResponseBuilder` | Builder class to create `HealthCheckResponse` instances -| `io.helidon.health.HealthSupport` +| `io.helidon.reactive.health.HealthSupport` | WebServer service that exposes `/health` and invokes the registered health checks -| `io.helidon.health.HealthSupport.Builder` +| `io.helidon.reactive.health.HealthSupport.Builder` | Builder class to create `HealthSupport` instances |======= @@ -229,7 +229,7 @@ The following code adds the default built-in health checks to your application: [source,java] ---- HealthSupport health = HealthSupport.builder() - .addLiveness(HealthChecks.healthChecks()) // <1> + .add(HealthChecks.healthChecks()) // <1> .build(); Routing.builder() @@ -419,7 +419,7 @@ Routing healthRouting = Routing.builder() .register(JsonSupport.create()) .register(HealthSupport.builder() .webContext("/live") // <1> - .addLiveness(HealthChecks.healthChecks()) // <2> + .add(HealthChecks.healthChecks()) // <2> .build()) .register(HealthSupport.builder() .webContext("/ready") // <3> @@ -432,7 +432,7 @@ Routing defaultRouting = Routing.builder() .build(); WebServer server = WebServer.builder(defaultRouting) - .config(WebServer.builder() + .config(ServerConfiguration.builder() .port(8080) // <6> .addSocket("health", SocketConfiguration.builder() // <7> .port(8081) diff --git a/docs/se/introduction.adoc b/docs/se/introduction.adoc index dd1dbd84492..03b9f20b554 100644 --- a/docs/se/introduction.adoc +++ b/docs/se/introduction.adoc @@ -23,6 +23,7 @@ :rootdir: {docdir}/.. include::{rootdir}/includes/se.adoc[] +include::{rootdir}/includes/pages.adoc[] Helidon SE is a compact toolkit that embraces the latest Java SE features: reactive streams, asynchronous and functional programming, and fluent-style @@ -97,7 +98,7 @@ Instrumentation to expose metrics of your applications. //Openapi [CARD] .OpenAPI -[icon=donut_large,link=openapi.adoc] +[icon=donut_large,link={openapi-page}] -- Support OpenAPI from your application. -- @@ -160,22 +161,22 @@ Enables Java applications to participate in WebSocket interactions as both serve == Migration -In case you need to upgrade the version of Helidon, follow the `migration guides`. +In case you need to upgrade the version of Helidon, follow the `upgrade guides`. [PILLARS] ==== [CARD] -.Helidon SE 2.x Migration Guide +.Helidon SE 2x Migration Guide [icon=upgrade,link=guides/migration.adoc] -- -Follow this guide to migrate your application from Helidon 1.x to 2.x. +Follow this guide to migrate your application from Helidon 1x to 2x. -- [CARD] -.Helidon SE 3.x Migration Guide +.Helidon SE 3x Migration Guide [icon=upgrade,link=guides/migration_3x.adoc] -- -Follow this guide to migrate your application from Helidon 2.x to 3.x. +Follow this guide to migrate your application from Helidon 2x to 3x. -- ==== @@ -199,7 +200,7 @@ Follow step-by-step guides to build your applications using Helidon SE. [CARD] .Javadocs // suppress inspection "AsciiDocLinkResolve" -[icon=link,link={javadoc-base-url}/index.html?overview-summary.html,link-type=url] +[icon=library_books,link={javadoc-base-url}/index.html?overview-summary.html,link-type=url] -- Browse the Helidon Javadocs. -- diff --git a/docs/se/openapi/openapi-generator.adoc b/docs/se/openapi/openapi-generator.adoc new file mode 100644 index 00000000000..fa1639f1cc6 --- /dev/null +++ b/docs/se/openapi/openapi-generator.adoc @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2022 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-based Code Generation +:rootdir: {docdir}/../.. +:incdir: {rootdir}/includes/openapi +:gen-inc: {incdir}/openapi-generator.adoc + +include::{rootdir}/includes/se.adoc[] +include::{rootdir}/includes/pages.adoc[] +:helidon-client-xref: {webclient-page} +include::{gen-inc}[tag=preamble] + +include::{gen-inc}[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;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}/io/helidon/webclient/WebClient.html[`WebClient`], representing the connection to the remote server. The generator also creates one or more `Api` interfaces and corresponding implementation classes. The brief examples below use the `PetApi` interface and the `PetApiImpl` class. + +Your code must: + +. Create an instance of `ApiClient`. +. Use that `ApiClient` instance to instantiate a `PetApi`. +. Invoke the methods on the `PetApi` instance to access the remote services. +. Use the returned results. + +==== Creating an `ApiClient` Instance +In the simplest case, your code can invoke `ApiClient.builder().build()` to get the instance it needs. +Or, if needed, it can use the builder to fine-tune the settings for the `ApiClient` before building it. +This fine-tuning can include setting an object mapper for Jackson processing or the `JsonbConfig` for JSON-B, depending on which serialization library you chose when you ran the generator. + +If necessary, your code has full access to the Helidon +link:{webclient-javadoc-base-url}/io/helidon/webclient/WebClient.Builder.html[`WebClient.Builder`] that the `ApiClient.Builder` uses internally, via the `ApiClient.Builder.webClientBuilder()` method. + +Once your code has prepared the `ApiClient.Builder` as it needs, it invokes `build()` to initialize the `ApiClient`. + +==== Creating a `PetApi` Instance +The `ApiClient` represents the connection to the remote server but not the individual RESTful operations. Rather, the `PetApi` interface declares a method for each operation pertaining to pets declared in the OpenAPI document. + +To invoke an operation defined on the `PetApi` interface, your code instantiates a `PetApi` using the `ApiClient` it just prepared, often as simply as: + +.Preparing PetStore Client API +[source,java] +---- +ApiClient apiClient = ApiClient.builder().build(); +PetApi petApi = PetApiImpl.create(apiClient); +---- + +==== Invoking Remote Endpoints +Once your code has the `petApi` object, it can invoke any of the methods on `PetApi` to contact the remote service. + +The Helidon WebClient support uses a reactive programming model, and the Helidon SE client generator creates an `ApiResponse` interface which follows this model. +In fact, 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: + +* `Single webClientResponse()` ++ +Provides reactive access to the Helidon `WebClientResponse` object. +Your code can find out the HTTP return status, read headers in the response, and process the content (if any) in the response. +* `Single result()` ++ +Provides reactive access to the value returned by the remote service in the response. +This method lets your code fetch the return value without you having to process the `WebClientResponse` content yourself. + +In the reactive Helidon WebClient model, the response can begin to arrive (the status and headers become available) before the entity in the body of the response is readable. So there are two reactive 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. + +===== Using the Fully-reactive Programming Model +The following example shows how your code might invoke the remote service and process the response using the reactive programming model. + +.Invoking a Remote Service and Processing the Result (fully reactive) +[source,java] +---- +// Assumes the petApi field is initialized as above. +ApiResponse> availablePetsResponse = + petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> + +availablePetsResponse.webClientResponse() + .thenAccept(resp -> { + if (resp.status().code() == 200) { + try { + availablePetsResponse.result() // <2> + .thenAccept(availablePets -> { + // Process the List of available pets. // <3> + + }) + .exceptionally(throwable -> { + // Handle whatever problem occurred in retrieving the results. // <4> + return null; + }); + } catch (ExecutionException | InterruptedException e) { + // Handle errors while waiting for the response content to arrive. // <5> + } + } else { + // Handle non-200 HTTP status. // <6> + }) + .exceptionally(throwable -> { + // Handle whatever problem occurred in receiving the response. // <7> + return null; + }); +---- +<1> Starts the remote service invocation. +<2> Reactively processes a successfully-received HTTP response. +<3> Reactively processes the successfully-returned list of available pets. +<4> Reactively handles any errors in retrieving the list of available pets. +<5> Handle problems that occurred while waiting for the response content to arrive. +<6> Handle a non-200 response status. +<7> Reactively handles any errors in receiving the HTTP response. + +The fully-reactive approach lets you avoid blocking the thread which initiates the outbound remote service access. +Avoiding blocking is especially important if the code which uses the generated client runs in a server. + +The code is complicated because receiving the beginning of the response and receiving the entity content of the response both occur asynchronously. There are several ways this processing can fail, and your code should handle each of them in whatever ways make sense for your application. + +===== Using a Blocking Programming Model +In other situations, you might be willing to block the thread until the response arrives and the entity data becomes available. +This might be the case in a command-line client that cannot continue doing work until it can process the response and the entity contents returned from the remote service. +The following example illustrates the blocking approach. + +.Invoking a Remote Service and Processing the Result (blocking) +[source,java] +---- +ApiResponse> availablePetsResponse = + petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE)); // <1> + +WebClientResponse resp = availablePetsResponse.webClientResponse().await(); // <2> +if (resp.status().code() == 200) { + try { + List availablePets = availablePetsResponse.result().await(); // <3> + } catch (ExecutionException | InterruptedException e) { + // Handle errors while waiting for the response content to arrive. // <4> + } +} else { + // Handle non-200 response status. // <5> +} +---- +<1> Start the remote service call. +<2> Wait for the response preamble (status, headers) to arrive. +<3> Wait for the entity to arrive and convert it to the result type. +<4> Handle problems that occurred while waiting for the response content to arrive. +<5> Handle a non-200 response status. + +The blocking style is easier to program but you should still address the various error conditions in whatever way makes sense for your application. + +include::{gen-inc}[tag=common-references] +* xref:{helidon-client-xref}[Helidon WebClient documentation] \ No newline at end of file diff --git a/docs/se/openapi/openapi-overview.adoc b/docs/se/openapi/openapi-overview.adoc new file mode 100644 index 00000000000..0a27c4a6a7f --- /dev/null +++ b/docs/se/openapi/openapi-overview.adoc @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2022 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 Overview +:toc: +:toc-placement: preamble +:description: Helidon SE OpenAPI Support +:keywords: helidon, se, openapi +:feature-name: OpenAPI +:rootdir: {docdir}/../.. + +include::{rootdir}/includes/se.adoc[] + +include::{rootDir}/includes/openapi/openapi-overview.adoc[] + diff --git a/docs/se/openapi/openapi.adoc b/docs/se/openapi/openapi.adoc new file mode 100644 index 00000000000..0c9f1b1e814 --- /dev/null +++ b/docs/se/openapi/openapi.adoc @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////// + + Copyright (c) 2019, 2022 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 in Helidon +:toc: +:toc-placement: preamble +:description: Helidon SE OpenAPI Support +:keywords: helidon, se, openapi +:feature-name: OpenAPI +:rootdir: {docdir}/../.. +:incdir: {rootdir}/includes/openapi + +include::{rootdir}/includes/se.adoc[] + +== Contents + +- <> +- <> +- <> +- <> +- <> +- <> +- <> + +== Overview + +include::{incdir}/openapi.adoc[tag=overview] + +include::{rootdir}/includes/dependencies.adoc[] + +[source,xml] +---- + + io.helidon.openapi + helidon-openapi + +---- + +== Usage + +You can very simply add support for OpenAPI to your Helidon SE application. This +document shows what changes you need to make to your application and how to access +the OpenAPI document for your application at runtime. + +=== Changing your application + +==== Register `OpenAPISupport` in your application routing + +Helidon SE provides the link:{openapi-javadoc-base-url}/OpenAPISupport.html[`OpenAPISupport`] class which your application uses to assemble the in-memory model and expose the `/openapi` endpoint to clients. You can create an instance either using a static `create` method or by instantiating its link:{openapi-javadoc-base-url}/OpenAPISupport.Builder.html[`Builder`]. The xref:#register_openapisupport[example below] illustrates one way to do this. + +include::{incdir}/openapi.adoc[tag=furnish-openapi-info] + +==== Add OpenAPI dependency +If you implement either a model reader or a filter, add this dependency to your +`pom.xml`: + +[source,xml,subs="attributes+"] +---- + + org.eclipse.microprofile.openapi + microprofile-openapi-api + {microprofile-openapi-version} + +---- + +include::{incdir}/openapi.adoc[tag=usage-access-endpoint] + +== API + +include::{incdir}/openapi.adoc[tag=api] + +Helidon {flavor-uc} provides an API for creating and setting up the REST endpoint which serves OpenAPI documents to clients at the `/openapi` path. Use either static methods on link:{openapi-javadoc-base-url}/OpenAPISupport.html[`OpenAPISupport`] or use its link:{openapi-javadoc-base-url}/OpenAPISupport.Builder.html[`Builder`] to create an instance of `OpenAPISupport`. Then add that instance to your application's routing. The <<#register_openapisupport,example>> below shows how to do this. + +== Configuration + +Helidon SE OpenAPI configuration supports the following settings: + +include::{rootdir}/config/io_helidon_openapi_SEOpenAPISupport.adoc[leveloffset=+1,tag=config] + + + +== Examples + +Helidon SE provides a link:{helidon-github-tree-url}/examples/openapi[complete OpenAPI example] +based on the SE QuickStart sample app which includes a model reader and a filter. + +Most Helidon {flavor-uc} applications need only to create and register `OpenAPISupport`. + +[#register_openapisupport] +=== Register `OpenAPISupport` + +.Java Code to Register `OpenAPISupport` for Routing +[source,java] +---- +Config config = Config.create(); +return Routing.builder() + .register(JsonSupport.create()) + .register(OpenAPISupport.create(config)) // <1> + .register(health) // Health at "/health" + .register(metrics) // Metrics at "/metrics" + .register("/greet", greetService) + .build(); +---- +<1> Adds the `OpenAPISupport` service to your server. + +If you need more control over the `OpenAPISupport` instance, invoke `OpenAPISupport.builder()` to get an `OpenAPISupport.Builder` object and work with it. + +== Additional Information +include::{incdir}/openapi.adoc[tag=additional-building-jandex] \ No newline at end of file diff --git a/docs/se/tracing.adoc b/docs/se/tracing.adoc index 5e40372b5a0..239da734c84 100644 --- a/docs/se/tracing.adoc +++ b/docs/se/tracing.adoc @@ -85,11 +85,11 @@ Helidon provides such an implementation for: [source,java] .Configuring OpenTracing `Tracer` ---- -WebServer.builder() - .tracer(TracerBuilder.create("my-application") // <1> - .collectorUri(URI.create("http://10.0.0.18:9411")) // <2> - .build()) - .build() +ServerConfiguration.builder() + .tracer(TracerBuilder.create("my-application") // <1> + .collectorUri(URI.create("http://10.0.0.18:9411")) // <2> + .build()) + .build() ---- <1> The name of the application (service) to associate with the tracing events <2> The endpoint for tracing events, specific to the tracer used, usually loaded from Config @@ -286,12 +286,12 @@ Tracing propagation is automatic as long as the current span context is availabl [source,xml] ---- - io.helidon.webclient - helidon-webclient + io.helidon.reactive.webclient + helidon-reactive-webclient - io.helidon.webclient - helidon-webclient-tracing + io.helidon.reactive.webclient + helidon-reactive-webclient-tracing ---- diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index a5e79bb3988..06f09f6cb37 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -57,19 +57,19 @@ backend: source: "about/introduction.adoc" glyph: type: "icon" - value: "lightbulb" + value: "widgets" - type: "PAGE" title: "Get Started" source: "about/prerequisites.adoc" glyph: type: "icon" - value: "rocket_launch" + value: "widgets" - type: "PAGE" title: "Helidon CLI" source: "about/cli.adoc" glyph: type: "icon" - value: "terminal" + value: "widgets" - type: "GROUPS" items: - type: "GROUP" @@ -81,7 +81,7 @@ backend: source: "introduction.adoc" glyph: type: "icon" - value: "lightbulb" + value: "widgets" - type: "MENU" title: "Guides" dir: "guides" @@ -164,12 +164,15 @@ backend: - "micrometer.adoc" - "prometheus-exemplar-support.adoc" - "metrics-capable-components.adoc" - - type: "PAGE" + - type: "MENU" title: "OpenAPI" - source: "openapi.adoc" + dir: "openapi" glyph: type: "icon" value: "donut_large" + sources: + - "openapi.adoc" + - "openapi-generator.adoc" - type: "MENU" title: "Integrations" dir: "integrations" @@ -259,7 +262,7 @@ backend: source: "introduction.adoc" glyph: type: "icon" - value: "lightbulb" + value: "assistant" - type: "MENU" title: "Guides" dir: "guides" @@ -372,12 +375,15 @@ backend: - "micrometer.adoc" - "prometheus-exemplar-support.adoc" - "metrics-capable-components.adoc" - - type: "PAGE" + - type: "MENU" title: "OpenAPI" - source: "openapi.adoc" + dir: "openapi" glyph: type: "icon" value: "donut_large" + sources: + - "openapi.adoc" + - "openapi-generator.adoc" - type: "MENU" title: "Integrations" dir: "integrations" From e3121df1d8eb335ebf4de9c7cc4484afe1bf6514 Mon Sep 17 00:00:00 2001 From: "tim.quinn@oracle.com" Date: Wed, 26 Oct 2022 18:39:03 -0500 Subject: [PATCH 2/5] Various improvements --- docs/includes/openapi/openapi-generator.adoc | 169 +++++++++-------- docs/includes/pages.adoc | 1 + docs/mp/openapi/openapi-generator.adoc | 2 +- docs/se/openapi/openapi-generator.adoc | 189 +++++++++++++------ docs/sitegen.yaml | 12 +- 5 files changed, 232 insertions(+), 141 deletions(-) diff --git a/docs/includes/openapi/openapi-generator.adoc b/docs/includes/openapi/openapi-generator.adoc index 556528ae33d..97011dd28f4 100644 --- a/docs/includes/openapi/openapi-generator.adoc +++ b/docs/includes/openapi/openapi-generator.adoc @@ -40,9 +40,7 @@ == Overview The link:{openapi-spec-url}[OpenAPI specification] provides a standard way to express RESTful APIs. -Separately, the link:{openapi-generator-tool-site-url}[OpenAPI generator] project has created a powerful code generator tool that 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. +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 gained particularly strong support for Helidon in release {first-version-with-strong-helidon-support}. @@ -65,10 +63,11 @@ endif::mp-flavor[] ifdef::se-flavor[] Helidon SE client based on xref:{helidon-client-xref}[Helidon WebClients]. endif::se-flavor[] -The resulting client library works with any server that implements the API declared in the OpenAPI document. -The library provides an abstraction similar to remote procedure calls (RPC). To access a remote service that implements the endpoints declared in the OpenAPI document, your code uses the generated library to establish a connection to the remote service and then calls remote service endpoints by invoking a local API using POJO business objects. +The resulting client library works with any server that implements the API declared in the OpenAPI document you specified when you ran the generator. +The library provides an abstraction similar to remote procedure calls (RPC). +To access a remote service that implements the endpoints declared in the OpenAPI document, your code uses the generated library first to establish a connection to the remote service and then to call remote service endpoints by invoking local methods using POJO business objects. -Use the tool's Helidon _server_ generator and its `{flavor-lc}` library to create server endpoint stubs for a Helidon {flavor-uc} service. You build on these stubs by extending a generated abstract class or implementing a generated interface, adding your specific business logic to finish the implementation of the endpoints in your API. The combination of the generated server code with Helidon {flavor-uc}} underneath it allows you to focus on the business details instead of the networking. +Use the tool's Helidon _server_ generator and its `{flavor-lc}` library to create server endpoint stubs for a Helidon {flavor-uc} service. You build on these stubs by extending a generated class or implementing a generated interface, adding your specific business logic to finish the implementation of the endpoints. The combination of the generated server code plus Helidon {flavor-uc} underneath it allows you to focus on the business details instead of the networking. You can run the OpenAPI generators in three ways: @@ -78,17 +77,19 @@ You can run the OpenAPI generators in three ways: * using the online OpenAPI generator website // end::three-ways-to-run[] -The rest of this document walks you through <> each technique and how to <> the generators to produce code as you want. +The rest of this document walks you through <> each technique and how to <> the generators to produce the code you want. // end::intro[] // tag::coords[] == Maven Coordinates -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. You can choose whichever version of the generator plug-in meets your needs as long as it is at least {first-version-with-strong-helidon-support}. +Your project does not need any run-time 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}. -[source,xml,subs="+attributes"] -.Declare the OpenAPI generator Plug-in +[source,xml,subs="+attributes,+macros"] +.Declaring the OpenAPI Generator Plug-in ---- {generator-version} @@ -101,7 +102,7 @@ To use the OpenAPI generator plug-in to generate or regenerate files during your org.openapitools openapi-generator-maven-plugin - $\{openapi-generator-version} + ++${openapi-generator-version}++ ... @@ -109,8 +110,7 @@ To use the OpenAPI generator plug-in to generate or regenerate files during your ---- -Your project does not need dependencies on the OpenAPI generator itself. - +A <> describes how to invoke the plug-in during your build. // end::coords[] // tag::config[] @@ -118,9 +118,13 @@ Your project does not need dependencies on the OpenAPI generator itself. == Configuration The OpenAPI generators support a substantial, powerful, and sometimes bewildering group of configuration settings. -[[links-to-settings]]For complete lists see the -link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-client.md[Helidon client generator options] and -link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-server.md[Helidon server generator options] pages. +For complete lists see these pages: + +[[links-to-settings]] +* link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-client.md[Helidon client generator options] and +* link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-server.md[Helidon server generator options] + +pages. The OpenAPI generator divides its settings into two types: @@ -174,7 +178,9 @@ For the Maven plug-in, use elements within the `` Keep this distinction between options and additional properties in mind so you know how to express the configuration you want. The <> to the lists of configuration options for the Helidon generators groups options and additional properties in separate tables. -=== Required Options +The next few sections describe, in turn, required settings, settings we recommend, and other common settings most developers will want to use. + +=== Required Settings You must specify the following options: @@ -198,14 +204,14 @@ You must specify the following options: |`--library` + `` -| +| {nbsp} |Library you want to use |`mp` + `se` |=== === Recommended Settings -Your project might have different needs, but in general we advise developers to use the following settings. +Your project might have different needs, but in general we advise developers to use the following options and additional properties. .Recommended OpenAPI Generator Options [cols="4,1,6,5"] @@ -215,17 +221,17 @@ Your project might have different needs, but in general we advise developers to |`--output` + `` |`-o` -| Directory where the generator should place files. - -We strongly recommend `-o target/generated-sources` or a subdirectory below there. +| Directory where the generator should place files. + + + +We strongly recommend `target/generated-sources` or a subdirectory below there. | `.` + (current directory) | `` (plug-in only) -| -| Whether Maven should include the output directory as a source root (include it automatically in the build). - +| {nbsp} +| Whether Maven should include the output directory as a source root (that is, include it automatically in the build). + + + We advise `true`. | `false` |=== @@ -241,7 +247,7 @@ We advise `true`. `org.openapitools.client.api` | `modelPackage` -| Name of the package for generated model classes +| Name of the package for generated model (POJO) classes | `org.openapitools.server.model` or + `org.openapitools.client.model` @@ -265,17 +271,17 @@ We advise `true`. |=== === Common Settings -Among the many configuration settings available to you, the table below includes several you should particularly consider. Refer to the <> for complete lists. +Among the many configuration settings available to you, some you should particularly consider are summarized in the table below. Refer to the <> for complete lists. .Common OpenAPI Generator Additional Properties -[cols="4,6,3,3,6"] +[cols="4,5,3,3,7"] |=== |Property |Description |Values |Default |Notes | `helidonVersion` |Version of Helidon for which to generate the files -| +| {nbsp} |`2.5.2` a|Affects: @@ -287,7 +293,7 @@ a|Affects: |Whether to generate all the normal files or only API files |`true`/`false` |`false` -| The "API files" include files developers do not normally modify after they are generated: the interfaces or abstract classes for the declared API +| The "API files" include files developers do not normally modify after they are generated: the interfaces or classes for the declared API and the model classes. |`serializationLibrary` @@ -313,63 +319,59 @@ This section covers two major topics: [[usage-planning]] === Planning Your Use of the OpenAPI Generators -Beyond the conventions listed above, there are several important choices you need to make when planning your project and running the OpenAPI generators which this section describes. +Beyond the settings listed above, there are several important choices you need to make when planning your project and when running the OpenAPI generators. This section addresses those choices. ==== Generating a New Project and Generating _Into_ an Existing Project You can use the OpenAPI generator to create a new project or to generate files into an existing project. -Some developers do both, using the generator to create the project at first and then to update the project as they evolve the OpenAPI document or change the generation options they select. The OpenAPI generator CLI and plug-in both support both types of generation. +Some developers do both, using the generator to create the project at first and then to update the project as they evolve the OpenAPI document or change the generation options they select. +Others create the project in some other way--for example, using the xref:{cli-page}[Helidon CLI]. The OpenAPI generator CLI and plug-in both support each type of usage. -Once you have an existing project (either from using the OpenAPI generator or from creating your project in another way), you can run the generator any number of times to update generated files inside your project to account for changes in your OpenAPI document changes or in the generation options you choose. +Once you have an existing project, you can run the generator any number of times to update generated files inside your project. You might regenerate to account for changes in your OpenAPI document or in the generation options you choose. Unlike with the generated API or model files, if the generator detects an existing `pom.xml` it does not overwrite it. -Certain generation options can influence the generated dependencies in the `pom.xml` file--for example, the `serializationLibrary` setting creates dependencies on either JSON-B or Jackson artifacts. +Certain generation options can influence the generated dependencies in the `pom.xml` file. For example, the `serializationLibrary` setting creates dependencies on either JSON-B or Jackson artifacts. As a result, changing the generation options can change the dependencies your project should have. To see the changes in the dependencies which the generator infers: * Be sure to follow the recommendation mentioned above to generate output into `generated-sources`. -* Include `clean` in your `mvn` command. This, combined with the previous step, removes previously-generated files including the `pom.xml` from `generated-sources`. +* Include `clean` in your `mvn` command. This, combined with the previous step, removes previously-generated files including the `target/generated-sources/pom.xml` file. * Run the generator again. -The newly-created `generated-sources/pom.xml` file shows the dependencies the generator inferred from the most recent options. You can manually update your project's `pom.xml` accordingly. - +Look at the newly-created `target/generated-sources/pom.xml` file to see what dependencies the generator has inferred from the most recent options. Manually update your project's actual `pom.xml` accordingly. -==== Generating Interfaces or Abstract Classes -As you generate a Helidon {flavor-uc} server, you can choose whether to create Java interfaces or abstract classes to represent the RESTful API endpoints. +==== Generating Interfaces or Classes +As you generate a Helidon {flavor-uc} _server_, you can choose whether you want Java interfaces or classes to represent the RESTful API endpoints. -By default, the Helidon OpenAPI server generator creates abstract classes. -You write your own concrete subclasses which extend those generated abstract classes, supplying the business logic for each REST endpoint. -_Do not_ modify the generated abstract classes. +By default, the Helidon OpenAPI server generator creates classes. +You write your own concrete subclasses which extend those generated classes, supplying the business logic for each REST endpoint. +_Do not_ modify the generated classes. -If you set `useAbstractClass=false` then the generator creates Java interfaces instead of abstract classes. -You write classes which implement the generated interfaces. +If you set `useAbstractClasses=false` then the generator creates Java interfaces instead of classes. +You then write classes which implement those generated interfaces. -Either way, you can safely regenerate the code later--for example if your OpenAPI document has changed--so long as you have not edited the generated code (which you should not do). -The generator replaces the abstract classes or interfaces but does not touch other classes you wrote. +Either way, you can safely regenerate the code later--for example if your OpenAPI document has changed--so long as you have not edited the generated code. +The generator replaces the generated classes or interfaces but does not touch other classes you wrote. -The Helidon client generator always creates concrete classes. Typically, you do not need to customize the behavior in the generated client API classes, but if you choose to do so, write your own subclass of the generated client API class; _do not_ modify the generated file. +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" Each operation in an OpenAPI document can have a `tags` attribute. -The generator groups operations with the same `tags` value into the same API. +The generators group operations with the same `tags` value into the same API. -When you generate a Helidon {flavor-uc} server, the generator creates a separate interface or abstract class for each API. -You implement each interface or extend each abstract class. +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. -A generated client contains a separate API class for each distinct API. +When you generate a Helidon {flavor-uc} client, the generated code contains a separate API class for each distinct API your code might _invoke_. [[usage-running]] === Running the OpenAPI Generators -This sections explains the various ways you can run the OpenAPI generators - -You already know there are many settings--as command-line options or configuration of the generator's Maven plug-in--with which you control how the generators work. The examples in this document adopt a few conventions we encourage you to follow. - Earlier we listed the ways you can run the OpenAPI generator: include::openapi-generator.adoc[tag=three-ways-to-run] -The next sections describe each in detail. +The next sections describe each of these techniques in detail. ==== Using the OpenAPI Generator CLI @@ -377,7 +379,7 @@ The next sections describe each in detail. .Downloading the OpenAPI Generator CLI You need to download the CLI `.jar` file before you can run the CLI. Follow these link:https://github.com/OpenAPITools/openapi-generator#13---download-jar[instructions] and remember where you save the `.jar` file. -This document uses the placeholder `path-to-generator` to represent the directory where you store that downloaded file. +The examples below use the placeholder `path-to-generator` to represent the directory where you store that downloaded file. The following example uses the Helidon server generator to create a project or regenerate files into an existing project. @@ -386,9 +388,9 @@ The following example uses the Helidon server generator to create a project or r // tag::example-cli-usage[] [source,bash,subs="attributes+"] ---- -java -jar path-to-generator/openapi-generator-cli.jar \ +java -jar $\{path-to-generator}/openapi-generator-cli.jar \ generate \ - -i petstore.yaml \ + -i src/main/resources/petstore.yaml \ -g java-helidon-{example-project-type} \ --library {flavor-lc} \ -o target/generated-sources/{example-project-type} \ @@ -409,46 +411,51 @@ include::openapi-generator.adoc[tag=example-cli-usage] The key differences between the commands are: -* the generator selected by the `-g` option, -* the output directory, and +* the generator selected by the `-g` option (`client` vs. `server`), +* the output directory (includes either `client` or `server`), and * the artifact ID and package names (`client` vs. `server`). -You could use these two commands to generate a server submodule and a client submodule in a Maven multi-module project. - -You can use the resulting client project to interact with any server which implements the API described in the `petstore.yaml` OpenAPI document, whether it was generated using the OpenAPI generator tool or not. +You could use these two commands together to generate a server submodule and a client submodule in a pre-existing multi-module Maven project. Remember that the resulting client project can access any server which implements the API described in the `petstore.yaml` OpenAPI document, whether it was generated using the OpenAPI generator tool or not. In both examples, the generator creates the entire project if it does not exist and recreates the generated files if the project already exists. The generator does not overwrite an existing `pom.xml` file, previously-generated test files, or files you created yourself. +[[invoking-the-plugin]] ==== Invoking the OpenAPI Generator Maven Plug-in -You can run the OpenAPI generator plug-in as part of your project build. +You can run the OpenAPI generator plug-in as part of your project build to generate or regenerate files. -First, declare the plug-in as explained in the earlier <>. +First, declare the plug-in as explained in the <>. -Then, in the `` section of your `pom.xml` file add an execution of the plug-in with the configuration you want. By default, the plug-in runs during the `generate-sources` phase of the Maven build. +Then, in the `` section of your `pom.xml` file, add an execution of the plug-in with the configuration you want. By default, the plug-in runs during the `generate-sources` phase of the Maven build. -For example, this Maven plugin can be used to generate/regenerate files (API interfaces or abstract classes that represent REST endpoints, models and other files that the end user does not modify) : +The plug-in execution in the following example is equivalent to the CLI example above for generating server files: -[source,xml] +[source,xml,subs="+attributes,+macros"] +.Creating or updating a {example-project-type} project using the OpenAPI Maven plug-in ---- org.openapitools openapi-generator-maven-plugin - ${openapi-generator-version} generate - ${project.basedir}/src/main/resources/petstore.yaml - java-helidon-server - se + ++${project.basedir}++/src/main/resources/petstore.yaml + java-helidon-{example-project-type} + {flavor-lc} + true - false + io.helidon.examples + helidon-openapigen-{flavor-lc}-{example-project-type} + 1.0.0-SNAPSHOT + io.helidon.examples.openapigen.{flavor-lc}.{example-project-type}.api + io.helidon.examples.openapigen.{flavor-lc}.{example-project-type}.model + io.helidon.examples.openapigen.{flavor-lc}.{example-project-type} @@ -460,7 +467,11 @@ For example, this Maven plugin can be used to generate/regenerate files (API int The OpenAPI tools project hosts and maintains the online OpenAPI generator at http://api.openapi-generator.tech. You can use the site's API browser to explore the available generators and the settings each supports, expressed as JSON. -To generate your project, you supply the options and additional properties as JSON. The online generator provides you with a file ID which you then use to retrieve it as a single file. +To generate your project, you supply the options and additional properties as JSON. The online generator provides you with a file ID, and you refer to the file ID in a subsequent HTTP request to retrieve your project. + +[NOTE] +The online generator stores your project on the server which you then retrieve using a separate HTTP request. +Before you use the online generator, consider whether any of the input you provide--the OpenAPI document, package or Maven coordinates--and therefore the generated project will reveal any sensitive information. This document does not explore further the use of the online generator. @@ -477,9 +488,9 @@ The Helidon generators go a long way in helping you write your client or server. // tag::using-generated-code-server[] === Completing the Server -Recall earlier how the OpenAPI generator gathers operations into one or more "APIs" and generates either an abstract class or an interface--your choice--for each API. -You need to extend each generated abstract API class or implement each generated API interface. -Any input parameters to the endpoints are expressed as POJO model objects or Java types, as declared in the OpenAPI document. So your code uses each of the input parameters to accomplish whatever business purpose that endpoint is responsible for, possibly returning a result also as a POJO or Java type. +Recall 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. +You need to extend each generated API class or implement each generated API interface. +Any input parameters to the endpoints are expressed as POJO model objects or Java types, as declared in the OpenAPI document. So 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. 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 ifdef::mp-flavor[] @@ -490,7 +501,7 @@ the Helidon SE `ServerResponse` endif::se-flavor[] and your code has complete control--and therefore responsibility--over setting the status, writing the response entity (if any), and assigning any returned headers. -Your code plus the server code from the Helidon generator--all running on Helidon {flavor-uc}--combine to implement fully the server API declared in the original API document. Build your project to get a tailored Helidon {flavor-uc} server `.jar` file or Docker image that is ready to run. +Your code plus the server code from the Helidon generator--all running on Helidon {flavor-uc}--combine to implement fully the server API declared in the original OpenAPI document. Build your project to get a tailored Helidon {flavor-uc} server `.jar` file or Docker image and your server is ready to run. // end::using-generated-code-server[] diff --git a/docs/includes/pages.adoc b/docs/includes/pages.adoc index 8fb99fe60d5..6d8d08ee304 100644 --- a/docs/includes/pages.adoc +++ b/docs/includes/pages.adoc @@ -27,3 +27,4 @@ ifdef::mp-flavor[] endif::[] :webclient-page: {rootdir}/se/webclient.adoc :restclient-page: {rootdir}/mp/restclient.adoc +:cli-page: {rootdir}/about/cli.adoc diff --git a/docs/mp/openapi/openapi-generator.adoc b/docs/mp/openapi/openapi-generator.adoc index d6b6d6ef2bc..415dfdefa48 100644 --- a/docs/mp/openapi/openapi-generator.adoc +++ b/docs/mp/openapi/openapi-generator.adoc @@ -61,7 +61,7 @@ public class ExampleResource { } ---- <1> Uses a bean-defining annotation so CDI can inject into this class. -<2> Requests that CDI inject the following +<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. diff --git a/docs/se/openapi/openapi-generator.adoc b/docs/se/openapi/openapi-generator.adoc index fa1639f1cc6..2778405e328 100644 --- a/docs/se/openapi/openapi-generator.adoc +++ b/docs/se/openapi/openapi-generator.adoc @@ -29,63 +29,171 @@ include::{gen-inc}[tag=preamble] include::{gen-inc}[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;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}/io/helidon/webclient/WebClient.html[`WebClient`], representing the connection to the remote server. The generator also creates one or more `Api` interfaces and corresponding implementation classes. The brief examples below use the `PetApi` interface and the `PetApiImpl` class. +the Helidon link:{webclient-javadoc-base-url}/io/helidon/webclient/WebClient.html[`WebClient`] and represents the connection between your client 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. -Your code must: +To invoke the remote service your code must: . Create an instance of `ApiClient`. -. Use that `ApiClient` instance to instantiate a `PetApi`. -. Invoke the methods on the `PetApi` instance to access the remote services. +. Use that `ApiClient` instance to instantiate a `PetApi` object. +. Invoke the methods on the `PetApi` object to access the remote services. . Use the returned results. +The following sections explain these steps. + ==== Creating an `ApiClient` Instance -In the simplest case, your code can invoke `ApiClient.builder().build()` to get the instance it needs. -Or, if needed, it can use the builder to fine-tune the settings for the `ApiClient` before building it. -This fine-tuning can include setting an object mapper for Jackson processing or the `JsonbConfig` for JSON-B, depending on which serialization library you chose when you ran the generator. +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}/io/helidon/webclient/WebClient.Builder.html[`WebClient.Builder`] object using settings 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 almost 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. + +===== 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. -If necessary, your code has full access to the Helidon -link:{webclient-javadoc-base-url}/io/helidon/webclient/WebClient.Builder.html[`WebClient.Builder`] that the `ApiClient.Builder` uses internally, via the `ApiClient.Builder.webClientBuilder()` method. +[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 work with the `WebClient.Builder` the `ApiClient.Builder` creates. + +[source,java] +.Creating an `ApiClient` instance - adjusting the `WebClient.Builder` +---- +ApiClient.Builder apiClientBuilder = ApiClient.builder(); + +apiClientBuilder.webClientBuilder() + .connectTimeout(4, TimeUnit.SECONDS); + +ApiClient apiClient = apiClientBuilder.build(); +---- + +===== 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 used to prepare the internal `WebClient.Builder` is lost. -Once your code has prepared the `ApiClient.Builder` as it needs, it invokes `build()` to initialize the `ApiClient`. ==== Creating a `PetApi` Instance -The `ApiClient` represents the connection to the remote server but not the individual RESTful operations. Rather, the `PetApi` interface declares a method for each operation pertaining to pets declared in the OpenAPI document. +The `ApiClient` represents the connection to the remote server but not the individual RESTful operations. Rather, 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 the `ApiClient` it just prepared, often as simply as: +To invoke an operation defined on the `PetApi` interface, your code instantiates a `PetApi` using an `ApiClient` object: -.Preparing PetStore Client API [source,java] +.Preparing the PetStore Client API ---- ApiClient apiClient = ApiClient.builder().build(); PetApi petApi = PetApiImpl.create(apiClient); ---- ==== Invoking Remote Endpoints -Once your code has the `petApi` object, it can invoke any of the methods on `PetApi` to contact the remote service. +With the `petApi` object, your code can invoke any of the methods on the `PetApi` interface to contact the remote service. -The Helidon WebClient support uses a reactive programming model, and the Helidon SE client generator creates an `ApiResponse` interface which follows this model. -In fact, each generated `PetApi` method returns an `ApiResponse` where the `returnType` is the return type declared in the OpenAPI document for the corresponding operation. +The Helidon WebClient follows a reactive programming model, and the Helidon SE client generator creates an `ApiResponse` interface which follows the same approach. +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: +* `Single result()` ++ +Provides reactive access to the value returned by the remote service in the response. +This method lets your code fetch the return value directly. * `Single webClientResponse()` + Provides reactive access to the Helidon `WebClientResponse` object. Your code can find out the HTTP return status, read headers in the response, and process the content (if any) in the response. -* `Single result()` -+ -Provides reactive access to the value returned by the remote service in the response. -This method lets your code fetch the return value without you having to process the `WebClientResponse` content yourself. -In the reactive Helidon WebClient model, the response can begin to arrive (the status and headers become available) before the entity in the body of the response is readable. So there are two reactive events associated with an incoming HTTP response: +In the reactive Helidon WebClient model, the response can begin to arrive (the status and headers are available) before the entity in the body of the response is readable. +So there are two reactive 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. -===== Using the Fully-reactive Programming Model +You can adopt different styles of retrieving the results, depending on the specific needs of the code you are writing. + +===== Synchronous access to the result +This example shows the simplest way to invoke the remote service and work with the result. + +[source,java] +.Synchronous access to the return value +---- +// Assumes the petApi field is initialized as above. +List availablePets = petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())) // <1> + .result() + .await(4, TimeUnit.SECONDS); // <2> +---- +<1> Start the remote service invocation. +<2> Wait for the result to arrive. + +This code blocks the current thread, waiting up to four seconds for the response to arrive. +This approach might work for a CLI client where the thread has no other meaningful work to do until it has the result. +This is _not_ an appropriate style for server code that uses the generated client to invoke another service. + +Note that this approach offers no access to the HTTP status for the response or any headers that might have been returned. + +===== Synchronous access with status checking +The Helidon WebClient programming model includes a `WebClientResponse` interface which exposes all aspects of the HTTP response returned from the remote service. + +The next example shows how your code can use the `WebClientResponse`. + +[source,java] +.Synchronous access with status checking +---- +ApiResponse> apiResponse = petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> + +WebClientResponse webClientResponse = apiResponse.webClientResponse() + .await(4, TimeUnit.SECONDS); // <2> + +if (webClientResponse.status().code() != 200) { // <3> + // Handle a non-successful status. +} + +List> availablePets = apiResponse.result() + .await(4, TimeUnit.SECONDS); // <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. + +This code also blocks the current thread, first to wait for the initial response and then to wait for the content. + +===== Fully-reactive access The following example shows how your code might invoke the remote service and process the response using the reactive programming model. -.Invoking a Remote Service and Processing the Result (fully reactive) +.Fully-reactive access with status checking [source,java] ---- // Assumes the petApi field is initialized as above. @@ -124,40 +232,11 @@ availablePetsResponse.webClientResponse() <6> Handle a non-200 response status. <7> Reactively handles any errors in receiving the HTTP response. -The fully-reactive approach lets you avoid blocking the thread which initiates the outbound remote service access. +The fully-reactive approach brings with it some complexity, but it lets your code avoid blocking the thread that initiates the outbound remote service access. Avoiding blocking is especially important if the code which uses the generated client runs in a server. -The code is complicated because receiving the beginning of the response and receiving the entity content of the response both occur asynchronously. There are several ways this processing can fail, and your code should handle each of them in whatever ways make sense for your application. - -===== Using a Blocking Programming Model -In other situations, you might be willing to block the thread until the response arrives and the entity data becomes available. -This might be the case in a command-line client that cannot continue doing work until it can process the response and the entity contents returned from the remote service. -The following example illustrates the blocking approach. - -.Invoking a Remote Service and Processing the Result (blocking) -[source,java] ----- -ApiResponse> availablePetsResponse = - petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE)); // <1> - -WebClientResponse resp = availablePetsResponse.webClientResponse().await(); // <2> -if (resp.status().code() == 200) { - try { - List availablePets = availablePetsResponse.result().await(); // <3> - } catch (ExecutionException | InterruptedException e) { - // Handle errors while waiting for the response content to arrive. // <4> - } -} else { - // Handle non-200 response status. // <5> -} ----- -<1> Start the remote service call. -<2> Wait for the response preamble (status, headers) to arrive. -<3> Wait for the entity to arrive and convert it to the result type. -<4> Handle problems that occurred while waiting for the response content to arrive. -<5> Handle a non-200 response status. - -The blocking style is easier to program but you should still address the various error conditions in whatever way makes sense for your application. +Some of the complexity enters because there are several ways this processing can fail. +Your code should handle each of them in whatever ways make sense for your application, and that might mean dealing with each different error scenario in a different way. include::{gen-inc}[tag=common-references] * xref:{helidon-client-xref}[Helidon WebClient documentation] \ No newline at end of file diff --git a/docs/sitegen.yaml b/docs/sitegen.yaml index 06f09f6cb37..d300fe9b10f 100644 --- a/docs/sitegen.yaml +++ b/docs/sitegen.yaml @@ -170,9 +170,9 @@ backend: glyph: type: "icon" value: "donut_large" - sources: - - "openapi.adoc" - - "openapi-generator.adoc" + sources: + - "openapi.adoc" + - "openapi-generator.adoc" - type: "MENU" title: "Integrations" dir: "integrations" @@ -381,9 +381,9 @@ backend: glyph: type: "icon" value: "donut_large" - sources: - - "openapi.adoc" - - "openapi-generator.adoc" + sources: + - "openapi.adoc" + - "openapi-generator.adoc" - type: "MENU" title: "Integrations" dir: "integrations" From a636db1af5d4f8bb4ab810aa40cb8123e9d3b393 Mon Sep 17 00:00:00 2001 From: "tim.quinn@oracle.com" Date: Thu, 27 Oct 2022 14:38:04 -0500 Subject: [PATCH 3/5] Revs to documents; remove unneeded overview pages --- docs/includes/attributes.adoc | 7 + docs/includes/openapi/openapi-generator.adoc | 141 +++++++++++-------- docs/includes/openapi/openapi-overview.adoc | 25 ---- docs/mp/openapi/openapi-generator.adoc | 2 +- docs/mp/openapi/openapi-overview.adoc | 34 ----- docs/se/openapi/openapi-generator.adoc | 88 ++++++------ docs/se/openapi/openapi-overview.adoc | 30 ---- 7 files changed, 136 insertions(+), 191 deletions(-) delete mode 100644 docs/includes/openapi/openapi-overview.adoc delete mode 100644 docs/mp/openapi/openapi-overview.adoc delete mode 100644 docs/se/openapi/openapi-overview.adoc diff --git a/docs/includes/attributes.adoc b/docs/includes/attributes.adoc index 0bbcf8fadf3..19161d6c025 100644 --- a/docs/includes/attributes.adoc +++ b/docs/includes/attributes.adoc @@ -265,6 +265,13 @@ endif::[] // OpenAPI generator :openapi-generator-version: 6.2.1 :openapi-generator-tool-base-url: https://github.com/OpenAPITools/openapi-generator +:openapi-generator-site-version: v{openapi-generator-version} + +:openapi-generator-tool-docs-url: {openapi-generator-tool-base-url}/blob/{openapi-generator-site-version}/docs +:openapi-generator-tool-generators-docs-url: {openapi-generator-tool-docs-url}/generators :openapi-generator-tool-site-url: https://openapi-generator.tech +// Maven plug-ins +:maven-build-helper-plugin-url: https://www.mojohaus.org/build-helper-maven-plugin/usage.html + endif::attributes-included[] diff --git a/docs/includes/openapi/openapi-generator.adoc b/docs/includes/openapi/openapi-generator.adoc index 97011dd28f4..078f9a94bff 100644 --- a/docs/includes/openapi/openapi-generator.adoc +++ b/docs/includes/openapi/openapi-generator.adoc @@ -24,8 +24,6 @@ // 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 -:generator-version: 6.2.1 -:generator-site-version: v{openapi-generator-version} // end::preamble[] // tag::intro[] @@ -64,8 +62,8 @@ ifdef::se-flavor[] Helidon SE client based on xref:{helidon-client-xref}[Helidon WebClients]. endif::se-flavor[] The resulting client library works with any server that implements the API declared in the OpenAPI document you specified when you ran the generator. -The library provides an abstraction similar to remote procedure calls (RPC). -To access a remote service that implements the endpoints declared in the OpenAPI document, your code uses the generated library first to establish a connection to the remote service and then to call remote service endpoints by invoking local methods using POJO business objects. +The client library provides an abstraction similar to remote procedure calls (RPC). +To access a remote service that implements the endpoints declared in the OpenAPI document, your code uses the generated client library first to establish a connection to the remote service and then to call remote service endpoints by invoking local methods passing POJO business objects or Java types as arguments. Use the tool's Helidon _server_ generator and its `{flavor-lc}` library to create server endpoint stubs for a Helidon {flavor-uc} service. You build on these stubs by extending a generated class or implementing a generated interface, adding your specific business logic to finish the implementation of the endpoints. The combination of the generated server code plus Helidon {flavor-uc} underneath it allows you to focus on the business details instead of the networking. @@ -84,7 +82,7 @@ The rest of this document walks you through <> each te // tag::coords[] == Maven Coordinates -Your project does not need any run-time dependencies on the OpenAPI generator. +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}. @@ -92,7 +90,7 @@ To use the OpenAPI generator plug-in to generate or regenerate files during your .Declaring the OpenAPI Generator Plug-in ---- - {generator-version} + {openapi-generator-version} ... @@ -121,10 +119,10 @@ The OpenAPI generators support a substantial, powerful, and sometimes bewilderin For complete lists see these pages: [[links-to-settings]] -* link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-client.md[Helidon client generator options] and -* link:https://github.com/OpenAPITools/openapi-generator/blob/{generator-site-version}/docs/generators/java-helidon-server.md[Helidon server generator options] -pages. +* link:{openapi-generator-tool-docs-url}/usage.md#generate[generic options] +* link:{openapi-generator-tool-generators-docs-url}/java-helidon-client.md[Helidon client generator options] and +* link:{openapi-generator-tool-generators-docs-url}/java-helidon-server.md[Helidon server generator options] The OpenAPI generator divides its settings into two types: @@ -134,21 +132,21 @@ These high-level settings generally govern the overall behavior of the tool. + For the CLI, use the common option style: + -`-o target/generated-sources` +`-i petstore.yaml` + -`--output target/generated-sources` +`--input-spec petstore.yaml` + For the Maven plug-in, use elements within the `` section of the plug-in: + [source,xml] ---- - ${project.build.directory}/generated-sources + petstore.yaml ---- * _additional properties_ + -These settings typically affect how a specific generator or library behaves. +These settings typically affect how a specific generator or library generates the code. + For the CLI: + @@ -185,19 +183,19 @@ The next few sections describe, in turn, required settings, settings we recommen You must specify the following options: .Required OpenAPI Generator Options -[cols="4,1,6,5"] +[cols="4,1,4,6,5"] |=== -|Option / Plug-in Setting | Short Option | Description | Values +|Option | Short Option |Plug-in Setting | Description | Values -|`--inputSpec` + -`` +|`--inputSpec` |`-i` +|`` |Path to the OpenAPI document defining the REST API | -|`--generatorName` + -`` +|`--generatorName` |`-g` +|`` | Generator you want to use (`java-helidon-server` or `java-helidon-client`) | `java-helidon-server` + `java-helidon-client` @@ -210,31 +208,9 @@ You must specify the following options: `se` |=== -=== Recommended Settings -Your project might have different needs, but in general we advise developers to use the following options and additional properties. +=== 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. -.Recommended OpenAPI Generator Options -[cols="4,1,6,5"] -|=== -|Option |Short Option | Description |Default - -|`--output` + -`` -|`-o` -| Directory where the generator should place files. + - + -We strongly recommend `target/generated-sources` or a subdirectory below there. -| `.` + -(current directory) - -| `` -(plug-in only) -| {nbsp} -| Whether Maven should include the output directory as a source root (that is, include it automatically in the build). + - + -We advise `true`. -| `false` -|=== .Recommended OpenAPI Generator Additional Properties [cols="3,6,5"] @@ -270,6 +246,30 @@ We advise `true`. | `1.0.0` |=== +[NOTE] +The next table contains recommendations only for using the OpenAPI generator plug-in (not for using the CLI). + + +.Recommended OpenAPI Generator Plug-in Options +[cols="4,8,5"] +|=== +|Plug-in Option | Description |Default + +|`` +| Directory where the generator should place files. + ++ +We strongly recommend `target/generated-sources` or a subdirectory below there. +| `.` + +(current directory) + +| `` +| Whether Maven should include the output directory as a source root (that is, include it automatically in the build). + ++ +We advise `true`. +| `false` +|=== + + === Common Settings Among the many configuration settings available to you, some you should particularly consider are summarized in the table below. Refer to the <> for complete lists. @@ -327,18 +327,38 @@ You can use the OpenAPI generator to create a new project or to generate files i Some developers do both, using the generator to create the project at first and then to update the project as they evolve the OpenAPI document or change the generation options they select. Others create the project in some other way--for example, using the xref:{cli-page}[Helidon CLI]. The OpenAPI generator CLI and plug-in both support each type of usage. -Once you have an existing project, you can run the generator any number of times to update generated files inside your project. You might regenerate to account for changes in your OpenAPI document or in the generation options you choose. - -Unlike with the generated API or model files, if the generator detects an existing `pom.xml` it does not overwrite it. -Certain generation options can influence the generated dependencies in the `pom.xml` file. For example, the `serializationLibrary` setting creates dependencies on either JSON-B or Jackson artifacts. -As a result, changing the generation options can change the dependencies your project should have. -To see the changes in the dependencies which the generator infers: +If the OpenAPI generator finds a pre-existing API or model file, it overwrites it with the latest content. +It does _not_ overwrite a `pom.xml` file or test files. +This is important because certain generation settings can influence the generated dependencies in the `pom.xml` file. +For example, the `serializationLibrary` setting creates dependencies on either JSON-B or Jackson artifacts. +As a result, changing the generation options can change the dependencies your project should have. If you rerun the generator, the old `pom.xml` remains and does not reflect the revised depencencies. -* Be sure to follow the recommendation mentioned above to generate output into `generated-sources`. -* Include `clean` in your `mvn` command. This, combined with the previous step, removes previously-generated files including the `target/generated-sources/pom.xml` file. -* Run the generator again. -Look at the newly-created `target/generated-sources/pom.xml` file to see what dependencies the generator has inferred from the most recent options. Manually update your project's actual `pom.xml` accordingly. +As a practical matter, many developers use the OpenAPI generators in one of the following ways: +* Use the generator CLI once to create a new project. ++ +By default, the generator CLI creates files in the normal Maven project structure: `src/main/java`, etc. +Then you add your own files to that same project structure. +Because the generated files are in the standard places, the project build includes them by default. ++ +[NOTE] +==== +You _can_ run the generator CLI again to update the generated files. +Because this happens outside the project's build lifecycle, you need to remember to rerun the CLI yourself when you change the OpenAPI document. + +You also need to identify and manually remove any previously-generated files that become obsolete. +Similarly, you must understand how changes in the OpenAPI document or the generation options affect the project dependencies and update the project `pom.xml` accordingly. +==== +* Use the generator plug-in to (re)generate files during each build. ++ +Specify in the plug-in configuration that the generated files should reside in `target/generated-sources` directory (the conventional location for generated sources) or a subdirectory below there. +Each project build runs the OpenAPI generator which reads the then-current OpenAPI document file. +With the generated files under `target`, you can use `mvn clean` to remove any obsolete generated files left over from previous builds. ++ +[NOTE] +==== +In particular, with `mvn clean` each build regenerates the candidate `pom.xml` under `target/generated-sources`. You can inspect the generated `pom.xml` file for changes in dependencies and make any necessary changes in the actual project `pom.xml` file. +==== ==== Generating Interfaces or Classes As you generate a Helidon {flavor-uc} _server_, you can choose whether you want Java interfaces or classes to represent the RESTful API endpoints. @@ -350,7 +370,7 @@ _Do not_ modify the generated classes. If you set `useAbstractClasses=false` then the generator creates Java interfaces instead of classes. You then write classes which implement those generated interfaces. -Either way, you can safely regenerate the code later--for example if your OpenAPI document has changed--so long as you have not edited the generated code. +Either way, you can safely regenerate the code later so long as you have not edited the generated code. The generator replaces the generated classes or interfaces but does not touch other classes you wrote. 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. @@ -393,7 +413,6 @@ java -jar $\{path-to-generator}/openapi-generator-cli.jar \ -i src/main/resources/petstore.yaml \ -g java-helidon-{example-project-type} \ --library {flavor-lc} \ - -o target/generated-sources/{example-project-type} \ -p groupId=io.helidon.examples \ -p artifactId=helidon-openapigen-{flavor-lc}-{example-project-type} \ -p artifactVersion=1.0.0-SNAPSHOT \ @@ -412,14 +431,12 @@ include::openapi-generator.adoc[tag=example-cli-usage] The key differences between the commands are: * the generator selected by the `-g` option (`client` vs. `server`), -* the output directory (includes either `client` or `server`), and * the artifact ID and package names (`client` vs. `server`). You could use these two commands together to generate a server submodule and a client submodule in a pre-existing multi-module Maven project. Remember that the resulting client project can access any server which implements the API described in the `petstore.yaml` OpenAPI document, whether it was generated using the OpenAPI generator tool or not. -In both examples, the generator creates the entire project if it does not exist and recreates the generated files if the project already exists. -The generator does not overwrite an existing `pom.xml` file, previously-generated test files, or files you created yourself. - +In both examples, the generator creates the entire project if it does not exist and recreates the generated API and model files if the project already exists. +The generator does not overwrite an existing `pom.xml` file, previously-generated test files, or files you create yourself. [[invoking-the-plugin]] ==== Invoking the OpenAPI Generator Maven Plug-in @@ -448,6 +465,7 @@ The plug-in execution in the following example is equivalent to the CLI example ++${project.basedir}++/src/main/resources/petstore.yaml java-helidon-{example-project-type} {flavor-lc} + ++${project.build.directory}++/generated-sources/{example-project-type} true io.helidon.examples @@ -462,6 +480,7 @@ The plug-in execution in the following example is equivalent to the CLI example ---- +<1> Specifies that the generated files should reside in the `target/generated-sources/{example-project-type}` directory. ==== Using the Online Generator @@ -489,8 +508,8 @@ The Helidon generators go a long way in helping you write your client or server. === Completing the Server Recall 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. -You need to extend each generated API class or implement each generated API interface. -Any input parameters to the endpoints are expressed as POJO model objects or Java types, as declared in the OpenAPI document. So 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. +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. 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 ifdef::mp-flavor[] diff --git a/docs/includes/openapi/openapi-overview.adoc b/docs/includes/openapi/openapi-overview.adoc deleted file mode 100644 index 3bcaaa1d8a5..00000000000 --- a/docs/includes/openapi/openapi-overview.adoc +++ /dev/null @@ -1,25 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2022 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. - -/////////////////////////////////////////////////////////////////////////////// - -ifndef::rootdir[:rootdir: {docdir}/..] - -Brief discussion plus links to - -* OpenAPI in Helidon -* Generator -* U/I diff --git a/docs/mp/openapi/openapi-generator.adoc b/docs/mp/openapi/openapi-generator.adoc index 415dfdefa48..d71389bdb9d 100644 --- a/docs/mp/openapi/openapi-generator.adoc +++ b/docs/mp/openapi/openapi-generator.adoc @@ -45,7 +45,7 @@ In the following example, `ExampleResource` (itself running in a server) invokes [source,java] .Using the generated `PetApi` from a separate service ---- -@Path("/example") // <1> +@Path("/exampleServiceCallingService") // <1> public class ExampleResource { @Inject // <2> diff --git a/docs/mp/openapi/openapi-overview.adoc b/docs/mp/openapi/openapi-overview.adoc deleted file mode 100644 index bd1ca1fa1df..00000000000 --- a/docs/mp/openapi/openapi-overview.adoc +++ /dev/null @@ -1,34 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2022 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 Overview -:toc: -:toc-placement: preamble -:description: Helidon MP OpenAPI Support -:keywords: helidon, mp, openapi -:feature-name: OpenAPI -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/mp.adoc[] - -Brief discussion plus links to - -* OpenAPI in Helidon -* Generator -* U/I - diff --git a/docs/se/openapi/openapi-generator.adoc b/docs/se/openapi/openapi-generator.adoc index 2778405e328..86b48448ae9 100644 --- a/docs/se/openapi/openapi-generator.adoc +++ b/docs/se/openapi/openapi-generator.adoc @@ -29,14 +29,13 @@ include::{gen-inc}[tag=preamble] include::{gen-inc}[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;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}/io/helidon/webclient/WebClient.html[`WebClient`] and represents the connection between your client 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. +the Helidon link:{webclient-javadoc-base-url}/io/helidon/webclient/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`. +. 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. -. Use the returned results. +. 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. @@ -45,12 +44,12 @@ The Helidon SE client generator gives you as much flexibility as you need in con 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}/io/helidon/webclient/WebClient.Builder.html[`WebClient.Builder`] object using settings from the OpenAPI document. +link:{webclient-javadoc-base-url}/io/helidon/webclient/WebClient.Builder.html[`WebClient.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 almost directly. +In the simplest case, your code can get an `ApiClient` instance directly. [source,java] .Creating an `ApiClient` instance - simple case @@ -59,7 +58,7 @@ ApiClient apiClient = ApiClient.builder().build(); ---- Your code relies fully on the automatic `WebClient.Builder`. -In many cases, this approach works very well. +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`. @@ -76,18 +75,23 @@ ApiClient apiClient = apiClient.builder() ---- ===== Adjusting the Automatic `WebClient.Builder` -In more complicated situations, your code can work with the `WebClient.Builder` the `ApiClient.Builder` creates. +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() - .connectTimeout(4, TimeUnit.SECONDS); +apiClientBuilder.webClientBuilder() // <1> + .connectTimeout(4, TimeUnit.SECONDS); // <2> -ApiClient apiClient = apiClientBuilder.build(); +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. @@ -103,11 +107,13 @@ 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 used to prepare the internal `WebClient.Builder` is lost. +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. Rather, the `PetApi` interface exposes a method for each operation in the OpenAPI document that pertains to pets. +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: @@ -121,7 +127,7 @@ 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 WebClient follows a reactive programming model, and the Helidon SE client generator creates an `ApiResponse` interface which follows the same approach. +The Helidon WebClient follows a reactive programming model, and the Helidon SE client generator creates an `ApiResponse` interface, also reactive. 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: @@ -133,9 +139,9 @@ This method lets your code fetch the return value directly. * `Single webClientResponse()` + Provides reactive access to the Helidon `WebClientResponse` object. -Your code can find out the HTTP return status, read headers in the response, and process the content (if any) in the response. +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 reactive Helidon WebClient model, the response can begin to arrive (the status and headers are available) before the entity in the body of the response is readable. +In the reactive 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 reactive events associated with an incoming HTTP response: . when the response _excluding_ the entity content has arrived, and @@ -151,14 +157,15 @@ This example shows the simplest way to invoke the remote service and work with t ---- // Assumes the petApi field is initialized as above. List availablePets = petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())) // <1> - .result() - .await(4, TimeUnit.SECONDS); // <2> + .result() // <2> + .await(4, TimeUnit.SECONDS); // <3> ---- <1> Start the remote service invocation. -<2> Wait for the result to arrive. +<2> Access the reactive result. +<3> Wait for the result to arrive subject to a four-second timeout. This code blocks the current thread, waiting up to four seconds for the response to arrive. -This approach might work for a CLI client where the thread has no other meaningful work to do until it has the result. +This approach might be adequate if you are developing a command-line client where the thread has no other meaningful work to do until it has the result. This is _not_ an appropriate style for server code that uses the generated client to invoke another service. Note that this approach offers no access to the HTTP status for the response or any headers that might have been returned. @@ -186,9 +193,9 @@ List> availablePets = apiResponse.result() <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. +<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 content. +This code also blocks the current thread, first to wait for the initial response and then to wait for the result content. ===== Fully-reactive access The following example shows how your code might invoke the remote service and process the response using the reactive programming model. @@ -201,41 +208,42 @@ ApiResponse> availablePetsResponse = petApi.findPetsByStatus(List.of(Pet.StatusEnum.AVAILABLE.value())); // <1> availablePetsResponse.webClientResponse() - .thenAccept(resp -> { + .thenAccept(resp -> { // <2> if (resp.status().code() == 200) { try { - availablePetsResponse.result() // <2> + availablePetsResponse.result() // <3> .thenAccept(availablePets -> { - // Process the List of available pets. // <3> + // Process the List of available pets. // <4> }) .exceptionally(throwable -> { - // Handle whatever problem occurred in retrieving the results. // <4> + // Handle whatever problem occurred in retrieving the results. // <5> return null; }); - } catch (ExecutionException | InterruptedException e) { - // Handle errors while waiting for the response content to arrive. // <5> - } + } catch (ExecutionException | InterruptedException e) { + // Handle errors while waiting for the response content to arrive. // <6> + } } else { - // Handle non-200 HTTP status. // <6> + // Handle non-200 HTTP status. // <7> }) .exceptionally(throwable -> { - // Handle whatever problem occurred in receiving the response. // <7> + // Handle whatever problem occurred in receiving the response. // <8> return null; }); ---- <1> Starts the remote service invocation. -<2> Reactively processes a successfully-received HTTP response. -<3> Reactively processes the successfully-returned list of available pets. -<4> Reactively handles any errors in retrieving the list of available pets. -<5> Handle problems that occurred while waiting for the response content to arrive. -<6> Handle a non-200 response status. -<7> Reactively handles any errors in receiving the HTTP response. - -The fully-reactive approach brings with it some complexity, but it lets your code avoid blocking the thread that initiates the outbound remote service access. +<2> Reactively processes the first portion of the HTTP response. +<3> Reactively processes a successfully-received HTTP response. +<4> Reactively processes the successfully-returned list of available pets. +<5> Reactively handles any errors in retrieving the list of available pets. +<6> Handle problems that occurred while waiting for the response content to arrive. +<7> Handle a non-200 response status. +<8> Reactively handles any errors in receiving the HTTP response. + +The fully-reactive approach brings with it some complexity, but it lets your code completely avoid blocking the thread that initiates the outbound remote service access. Avoiding blocking is especially important if the code which uses the generated client runs in a server. -Some of the complexity enters because there are several ways this processing can fail. +Some of this complexity enters because there are several ways this processing can fail. Your code should handle each of them in whatever ways make sense for your application, and that might mean dealing with each different error scenario in a different way. include::{gen-inc}[tag=common-references] diff --git a/docs/se/openapi/openapi-overview.adoc b/docs/se/openapi/openapi-overview.adoc deleted file mode 100644 index 0a27c4a6a7f..00000000000 --- a/docs/se/openapi/openapi-overview.adoc +++ /dev/null @@ -1,30 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// - - Copyright (c) 2022 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 Overview -:toc: -:toc-placement: preamble -:description: Helidon SE OpenAPI Support -:keywords: helidon, se, openapi -:feature-name: OpenAPI -:rootdir: {docdir}/../.. - -include::{rootdir}/includes/se.adoc[] - -include::{rootDir}/includes/openapi/openapi-overview.adoc[] - From 75bd507bd4484de06b91aeb03820efbd2e7b482b Mon Sep 17 00:00:00 2001 From: "tim.quinn@oracle.com" Date: Mon, 31 Oct 2022 09:28:43 -0500 Subject: [PATCH 4/5] Clarify and expand on SE server completion --- docs/includes/openapi/openapi-generator.adoc | 4 +- docs/se/openapi/openapi-generator.adoc | 55 +++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/docs/includes/openapi/openapi-generator.adoc b/docs/includes/openapi/openapi-generator.adoc index 078f9a94bff..26bd28c693f 100644 --- a/docs/includes/openapi/openapi-generator.adoc +++ b/docs/includes/openapi/openapi-generator.adoc @@ -505,11 +505,13 @@ The Helidon generators go a long way in helping you write your client or server. // end::using-generated-code-intro[] // tag::using-generated-code-server[] +// tag::using-generated-code-server-intro[] === Completing the Server -Recall 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" and generates either a 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[] 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 ifdef::mp-flavor[] diff --git a/docs/se/openapi/openapi-generator.adoc b/docs/se/openapi/openapi-generator.adoc index 86b48448ae9..b96c68abacb 100644 --- a/docs/se/openapi/openapi-generator.adoc +++ b/docs/se/openapi/openapi-generator.adoc @@ -26,7 +26,60 @@ include::{rootdir}/includes/pages.adoc[] :helidon-client-xref: {webclient-page} include::{gen-inc}[tag=preamble] -include::{gen-inc}[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server;using-generated-code-client-intro] +include::{gen-inc}[tags=intro;coords;config;usage;using-generated-code-intro;using-generated-code-server-intro] + + +The Helidon SE server generator also creates, for each API, a separate class containing handler methods for each endpoint. +Along with the `PetService` interface or abstract class which has methods such as `addPet` and `getPetById`, the tool generates `PetServiceImpl` with methods such as `handleAddPet` and `handleGetPetById`. + +[source,java] +.Generated `PetService` abstract class +---- +public abstract class PetService implements Service { + void addPet(ServerRequest request, ServerResponse response, Pet pet) { + // ... + } + abstract void handleAddPet(ServerRequest request, ServerResponse response, Pet pet); + + void getPetById(ServerRequest request, ServerResponse response) { + // ... + } + abstract void handleGetPetById(ServerRequest request, ServerResponse response, Long petId); +} +---- + +[source,java] +.Generated skeleton `PetServiceImpl` class (which you extend) +---- +public class PetServiceImpl extends PetService { + public void handleAddPet(ServerRequest request, ServerResponse response, Pet pet) { + response.status(HTTP_CODE_NOT_IMPLEMENTED.send()); + } + + public void handleGetPetById(ServerRequest request, ServerResponse response, Long petId) { + response.status(HTTP_CODE_NOT_IMPLEMENTED).send(); + } +} +---- +You write your own classes which extend `PetServiceImpl` and the other generated `xxxImpl` classes, overriding the `handle...` methods. + +You have control over--and therefore responsibility for--preparing the response to be sent to the client, including the status, any response headers, and any returned entity. +Your overriding implementation of `handleGetPetById` might look like the following example. + +[source,java] +.Example override of `handleGetPetById` +---- +public void handleGetPetById(ServerRequest request, ServerResponse response, Long petId) { + Pet pet = locatePetInDatabase(petId); + if (pet == null) { + response.status(404).send(); + } + response.send(pet); // Respnose status is 200 by default. + } +} +---- + +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}/io/helidon/webclient/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. From 3d94bd6f30d03d49dd98b79bfa0d7adb9bbbd232 Mon Sep 17 00:00:00 2001 From: "tim.quinn@oracle.com" Date: Thu, 3 Nov 2022 09:40:29 -0500 Subject: [PATCH 5/5] Adopt review comments. --- docs/includes/openapi/openapi-generator.adoc | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/includes/openapi/openapi-generator.adoc b/docs/includes/openapi/openapi-generator.adoc index 26bd28c693f..7333a5d4c76 100644 --- a/docs/includes/openapi/openapi-generator.adoc +++ b/docs/includes/openapi/openapi-generator.adoc @@ -40,14 +40,13 @@ 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 gained particularly strong support for Helidon in release -{first-version-with-strong-helidon-support}. +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. In the vocabulary of the tool, there are two _generators_ for Helidon: -* `java-helidon-client` (which this document refers to as the Helidon client generator) -* `java-helidon-server` (Helidon server generator). +* `java-helidon-client` (hereafter the Helidon client generator) +* `java-helidon-server` (hereafter the Helidon server generator). Each of these generators supports two _libraries_: @@ -65,13 +64,13 @@ The resulting client library works with any server that implements the API decla The client library provides an abstraction similar to remote procedure calls (RPC). To access a remote service that implements the endpoints declared in the OpenAPI document, your code uses the generated client library first to establish a connection to the remote service and then to call remote service endpoints by invoking local methods passing POJO business objects or Java types as arguments. -Use the tool's Helidon _server_ generator and its `{flavor-lc}` library to create server endpoint stubs for a Helidon {flavor-uc} service. You build on these stubs by extending a generated class or implementing a generated interface, adding your specific business logic to finish the implementation of the endpoints. The combination of the generated server code plus Helidon {flavor-uc} underneath it allows you to focus on the business details instead of the networking. +Use the tool's Helidon _server_ generator and its `{flavor-lc}` library to create server endpoint stubs for a Helidon {flavor-uc} service. You build on these stubs by extending a generated class or implementing a generated interface, adding your specific business logic to finish the implementation of the endpoints. The combination of the generated server code plus Helidon {flavor-uc} underneath it allows you to focus on the business details instead of resource boilerplate. You can run the OpenAPI generators in three ways: // tag::three-ways-to-run[] * using the OpenAPI generator CLI -* using the OpenAPI generator Maven or Gradle plug-in +* using the OpenAPI generator Maven plug-in * using the online OpenAPI generator website // end::three-ways-to-run[] @@ -522,7 +521,7 @@ the Helidon SE `ServerResponse` endif::se-flavor[] and your code has complete control--and therefore responsibility--over setting the status, writing the response entity (if any), and assigning any returned headers. -Your code plus the server code from the Helidon generator--all running on Helidon {flavor-uc}--combine to implement fully the server API declared in the original OpenAPI document. Build your project to get a tailored Helidon {flavor-uc} server `.jar` file or Docker image and your server is ready to run. +Your code plus the server code from the Helidon generator--all running on Helidon {flavor-uc}--combine to fully implement the server API declared in the original OpenAPI document. Build your project to get a tailored Helidon {flavor-uc} server `.jar` file or Docker image and your server is ready to run. // end::using-generated-code-server[]