diff --git a/api-first-approach/files/PetstoreRestService.java b/api-first-approach/files/PetstoreRestService.java new file mode 100644 index 00000000..5331c7f7 --- /dev/null +++ b/api-first-approach/files/PetstoreRestService.java @@ -0,0 +1,43 @@ +package com.devonfw.quarkus.petstore.rest.v1; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.validation.Valid; + +import com.devonfw.quarkus.openapi.petstore.domain.Pet; +import com.devonfw.quarkus.openapi.petstore.rest.v1.PetsApi; + +public class PetstoreRestService implements PetsApi { + + private Set pets = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>())); + + @Override + public void createPets(@Valid Pet pet) { + + pet.setId(Long.valueOf(this.pets.size())); + this.pets.add(pet); + } + + @Override + public List listPets(Integer limit) { + + if (limit == null) + return this.pets.stream().collect(Collectors.toList()); + return this.pets.stream().limit(limit).collect(Collectors.toList()); + } + + @Override + public Pet showPetById(String petId) { + + List petList = this.pets.stream().filter(pet -> pet.getId() == Long.valueOf(petId)) + .collect(Collectors.toList()); + if (petList.size() == 0) + return null; + return petList.get(0); + } + +} diff --git a/api-first-approach/files/create_pet.md b/api-first-approach/files/create_pet.md new file mode 100644 index 00000000..a704c121 --- /dev/null +++ b/api-first-approach/files/create_pet.md @@ -0,0 +1,10 @@ +Finally, test the REST service. + +Open a new terminal and type in the following command to make a POST request and create a new pet: + +curl -X POST -H "Content-Type: application/json" -d '{"name": "Alex", "tag": "Dog"}' http://localhost:8080/pets + +After executing the command, there should be a pet stored in the list. +Let's check this by getting the entire list using the `listPets` method of the REST service. + +Open https://[[HOST_SUBDOMAIN]]-8080-[[KATACODA_HOST]].environments.katacoda.com/pets in the browser and see if the pet has been saved. diff --git a/api-first-approach/files/petstore-api.yaml b/api-first-approach/files/petstore-api.yaml new file mode 100644 index 00000000..1b1ad562 --- /dev/null +++ b/api-first-approach/files/petstore-api.yaml @@ -0,0 +1,116 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/Pets" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + post: + summary: Create a pet + operationId: createPets + tags: + - pets + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: "#/components/schemas/Pet" + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/api-first-approach/files/pom.xml b/api-first-approach/files/pom.xml new file mode 100644 index 00000000..6fe00e80 --- /dev/null +++ b/api-first-approach/files/pom.xml @@ -0,0 +1,162 @@ + + + 4.0.0 + com.devonfw.quarkus + api-first-tutorial + 1.0.0-SNAPSHOT + + 3.8.1 + 11 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 2.6.1.Final + 3.0.0-M5 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + + + apigen + true + + + + org.openapitools + openapi-generator-maven-plugin + 5.3.1 + + + generate-sources + + generate + + + ${project.basedir}/src/main/resources/petstore-api.yaml + jaxrs-spec + com.devonfw.quarkus.openapi.petstore.rest.v1 + com.devonfw.quarkus.openapi.petstore.domain + + src/main/gen + false + true + true + true + + + + + + + + + + diff --git a/api-first-approach/index.asciidoc b/api-first-approach/index.asciidoc new file mode 100644 index 00000000..3fad57b0 --- /dev/null +++ b/api-first-approach/index.asciidoc @@ -0,0 +1,97 @@ += API first approach with OpenAPI generator +[tags] +-- +Framework=Quarkus;Spring +Category=REST;OpenAPI +Tools=devonfw;OpenAPI generator +Difficulty=Medium +-- +==== +This tutorial will show you how to use an API first approach to create a RESTful web service from an OpenAPI specification using the OpenAPI generator plugin. We will use a [Quarkus](https://quarkus.io/) project to demonstrate the use case. However, you can also use the OpenAPI generator in the same way for Spring. + +For more information about the API fist approach, see the [devon4j documentation](https://github.com/devonfw/devon4j/blob/master/documentation/guide-api-first.asciidoc). +For more details on the OpenAPI generator, have a look at the [official webpage](https://openapi-generator.tech/). + +## Prerequisites +* Installed devonfw-ide (or at least Java and Maven installed) + +## Learning goals +* You will learn how to generate REST services from OpenAPI specifications using OpenAPI generator + +==== + +[step] +-- +restoreDevonfwIde(["java", "mvn"]) +-- + +==== +In the first step, create the initial Quarkus project using the quarkus-maven-plugin on the command line. + +When using this plugin, you need to pass the groupId and the artifactId. We additionally add the Quarkus resteasy-jackson extension, because we need it later. + +[step] +== Create the Quarkus project +-- +executeCommand("mvn io.quarkus.platform:quarkus-maven-plugin:2.6.1.Final:create \"-DprojectGroupId=com.devonfw.quarkus\" \"-DprojectArtifactId=api-first-tutorial\" \"-Dextensions=resteasy-jackson\"","mvn io.quarkus.platform:quarkus-maven-plugin:2.6.1.Final:create -DprojectGroupId=com.devonfw.quarkus -DprojectArtifactId=api-first-tutorial -Dextensions=resteasy-jackson") +-- + +This command will create a folder `api-first-tutorial` in the workspace of the devonfw-ide. +==== + +==== +To create the REST service with the OpenAPI generator, you need the OpenAPI specification for the service. When developing with the "API first" strategy, the API specification is planned before developing the actual REST service. + +For this tutorial we will use a predefined API called 'Petstore'. You can find this and some more examples on the [GitHub repository of OpenAPI-Specification](https://github.com/OAI/OpenAPI-Specification/tree/main/schemas). + +So, create a file `petstore-api.yaml` in the folder `src/main/resources` of the Quarkus project. +[step] +== Create the REST API specification +-- +createFile("api-first-tutorial/src/main/resources/petstore-api.yaml", "files/petstore-api.yaml") +-- +==== + +==== +The next step is to add the OpenAPI generator maven plugin to the project. Open the `pom.xml` file of your project and add the `openapi-generator-maven-plugin` as shown in the snippet below. + +[step] +== Add the OpenAPI generator +-- +changeFile("api-first-tutorial/pom.xml", { "file": "files/pom.xml" }) +-- + +Now every time you build the project, the plugin will generate the correspong Java code for this API in the `target` folder of the project. +==== + +==== +The OpenAPI generator is able to generate full REST services. Since we used the option 'true', only the interface for this API is generated. +This allows us to create the REST service and implement the logic in it ourselves. + +So, create a class `PetstoreRestService` which implements the API generated from the generator plugin. Use the code shown below. + +[step] +== Create the REST service +-- +createFile("api-first-tutorial/src/main/java/com/devonfw/quarkus/petstore/rest/v1/PetstoreRestService.java", "files/PetstoreRestService.java") +-- + +This code uses a simple list to store the entities. You can, of course, use more complex logic and store the data in a database, for example. +==== + +==== +Now build and run the application. + +We pass the option `quarkus.http.host=0.0.0.0` here so that the service is available in Katacoda from outside. For local testing, you do not need this. + +[step] +== Build and run the application +-- +executeCommand("mvn clean compile quarkus:dev \"-Dquarkus.http.host=0.0.0.0\"", "mvn clean compile quarkus:dev -Dquarkus.http.host=0.0.0.0", { "dir": "api-first-tutorial", "asynchronous": true }, {"port":8080 , "startupTime": 120, "path": "/", "interval": 4}) +-- +==== + +[step] +-- +displayContent("Test the application", [{ "file": "files/create_pet.md" }]) +--