How to develop a simple REST Client using Swagger codegen?
Asked Answered
V

6

23

I'm learning about Swagger and how to generate REST Client using Swagger codegen. I know how to do documentation with Swagger, also I know how to generate a simple REST Server with Swagger, but I don't know how to generate a simple REST Client with Swagger codegen.

For example, I have a simple app, it is a REST Server and I want to generate REST Client. Can I do that with Swagger codegen?

The controller for the REST Server:

package com.dgs.spring.springbootswagger.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

@RestController
@RequestMapping("/api/v1")
@Api(value = "Employee Management System", description = "Operations pertaining to employee in Employee Management System")
public class EmployeeController {

     @Autowired
     private EmployeeRepository employeeRepository;

        @ApiOperation(value = "View a list of available employees", response = List.class)
        @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Successfully retrieved list"),
            @ApiResponse(code = 401, message = "You are not authorized to view the resource"),
            @ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
            @ApiResponse(code = 404, message = "The resource you were trying to reach is not found")
        })
     @GetMapping("/employees")
     public List<Employee> getAllEmployees() {
         return employeeRepository.findAll();
     }

     @ApiOperation(value = "Get an employee by Id")   
     @GetMapping("/employees/{id}")
     public ResponseEntity<Employee> getEmployeeById(
             @ApiParam(value = "Employee id from which employee object will retrieve", required = true) @PathVariable(value = "id") Long employeeId)
             throws ResourceNotFoundException {

          Employee employee = employeeRepository.findById(employeeId)
            .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

          return ResponseEntity.ok().body(employee);
     }

     @ApiOperation(value = "Add an employee")
     @PostMapping("/employees")
     public Employee createEmployee(
             @ApiParam(value = "Employee object store in database table", required = true) @Valid @RequestBody Employee employee) {
         return employeeRepository.save(employee);
     }

     @ApiOperation(value = "Update an employee")
     @PutMapping("/employees/{id}")
     public ResponseEntity<Employee> updateEmployee(
             @ApiParam(value = "Employee Id to update employee object", required = true) @PathVariable(value = "id") Long employeeId,
             @ApiParam(value = "Update employee object", required = true) @Valid @RequestBody Employee employeeDetails) throws ResourceNotFoundException {

          Employee employee = employeeRepository.findById(employeeId)
            .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

          employee.setEmail(employeeDetails.getEmail());
          employee.setLastName(employeeDetails.getLastName());
          employee.setFirstName(employeeDetails.getFirstName());
          final Employee updatedEmployee = employeeRepository.save(employee);

          return ResponseEntity.ok(updatedEmployee);
     }

     @ApiOperation(value = "Delete an employee")
     @DeleteMapping("/employees/{id}")
     public Map<String, Boolean> deleteEmployee(
             @ApiParam(value = "Employee Id from which employee object will delete from database table", required = true) @PathVariable(value = "id") Long employeeId)
       throws ResourceNotFoundException {

      Employee employee = employeeRepository.findById(employeeId)
        .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

      employeeRepository.delete(employee);
      Map<String, Boolean> response = new HashMap<>();
      response.put("deleted", Boolean.TRUE);

      return response;
     }
}

After that I've developed a simple REST Client:

package com.dgs.restclient.controllers;

@Controller
public class UpdateController {

    @Autowired
    private EmployeeRestClient restClient;

    @GetMapping("/showStartUpdate")
    public String showStartCheckin() {
        return "startUpdate";
    }

    @PostMapping("/startUpdate")
    public String startCheckIn(@RequestParam("employeeId") Long employeeId, ModelMap modelMap) {

        Employee employee = restClient.findEmployee(employeeId);
        modelMap.addAttribute("employee", employee);

        return "displayEmployeeDetails";
    }

    @PostMapping("/completeUpdate")
    public String completeCheckIn(@RequestParam("employeeId") Long employeeId,
            @RequestParam("employeeFirstName") String employeeFirstName,
            @RequestParam("employeeLastName") String employeeLastName,
            @RequestParam("employeeEmail") String employeeEmail) {

        EmployeeUpdateRequest employeeUpdateRequest = new EmployeeUpdateRequest();
        employeeUpdateRequest.setId(employeeId);
        employeeUpdateRequest.setFirstName(employeeFirstName);
        employeeUpdateRequest.setLastName(employeeLastName);
        employeeUpdateRequest.setEmail(employeeEmail);
        restClient.updateEmployee(employeeUpdateRequest);

        return "updateConfirmation";
    }

}

The EmployeeRestClient:

package com.dgs.restclient.integration;

@Component
public class EmployeeRestClientImpl implements EmployeeRestClient {

    private static final String EMPLOYEE_REST_URL = 
            "http://localhost:8080/api/v1/employees/";

    @Override
    public Employee findEmployee(Long id) {

        RestTemplate restTemplate = new RestTemplate();
        Employee employee = restTemplate
                .getForObject(EMPLOYEE_REST_URL + id, Employee.class);

        return employee;
    }

    @Override
    public Employee updateEmployee(EmployeeUpdateRequest request) {

        RestTemplate restTemplate = new RestTemplate();
        restTemplate
                .put(EMPLOYEE_REST_URL + request.getId(), request, Employee.class); 

        Employee employee = restTemplate
                .getForObject(EMPLOYEE_REST_URL + request.getId(), Employee.class);

        return employee;
    }

}

This REST Client is developed by me and I want to know if I can do this REST Client development with Swagger codegen and how? Do I need just to add the swagger-codegen-maven-plugin in the pom.xml? I've heard about adding this plugin and a yml file and Swagger will create the REST Client. Any feedback will be appreciated!

Verner answered 18/8, 2019 at 15:17 Comment(2)
Yes. It is possible by just adding the swagger file and configuring the plugin in pom.xml which generates the Rest Client class automatically in target folder classes during maven build command. Is it possible for you add swagger file here so I can show you one working demo?!!Uredo
i think what Elvis want to do is to generate the rest clinet from the source codes directly without manually writting the swagger fileRanunculus
R
29

Yes. You can use swagger-codegen-maven-plugin to generate a REST client. But before that , you need to describe the REST API in YAML or JSON in OpenAPI Specification mainly because swagger-codegen-maven-plugin only can generate a REST client from a file written in this specification.

Other answers assume that you need to write the specification manually while my solution take a step further to automatically generating the specification from the REST controller source codes.

The latest OpenAPI version is 3.0 .But based on the package of your imported swagger annotation , you are using version 2.0 (or before). So my solution assume you are using OpenAPI 2.0.

Generating Open API Specification

First, you can use swagger-maven-plugin to generate an OpenAPI spec from the RestController source codes. It basically analyses the Swagger annotations annotated in the @RestController classes that specified in <locations> and dump the OpenAPI spec to /src/main/resources/swagger.json :

<plugin>
    <groupId>com.github.kongchen</groupId>
    <artifactId>swagger-maven-plugin</artifactId>
    <version>3.1.5</version>
    <configuration>
        <apiSources>
            <apiSource>
                <springmvc>true</springmvc>
                <locations>
                    <location>com.dgs.spring.springbootswagger.controller.EmployeeController</location>
                    <location>com.dgs.spring.springbootswagger.controller.FooController</location>
                </locations>
                <schemes>
                    <scheme>http</scheme>
                </schemes>
                <host>127.0.0.1:8080</host>
                <basePath>/</basePath>
                <info>
                    <title>My API</title>
                    <version>1.1.1</version>
                </info>
                <swaggerDirectory>${basedir}/src/main/resources/</swaggerDirectory>
            </apiSource>
        </apiSources>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Execute the followings maven command to start generation:

mvn clean compile

Generating Rest Client

After swagger.json is generated, you can copy and paste it to your client project (e.g. /src/main/resources/swagger.json) . We can then use swagger-codegen-maven-plugin to generate a HTTP client .

By default , it will generate the whole maven project which includes test cases and other documentation stuff. But what I want is just the HttpClient 's source codes without other things. After several trial and error , I settle down to the following configuration :

<plugin>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-codegen-maven-plugin</artifactId>
    <version>2.4.7</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${basedir}/src/main/resources/swagger.json</inputSpec>
                <language>java</language>
                <library>resttemplate</library>
                <output>${project.basedir}/target/generated-sources/</output>

                <apiPackage>com.example.demo.restclient.api</apiPackage>
                <modelPackage>com.example.demo.restclient.model</modelPackage>
                <invokerPackage>com.example.demo.restclient</invokerPackage>

                <generateApiTests>false</generateApiTests>
                <generateModelTests>false</generateModelTests>
                <generateApiDocumentation>false</generateApiDocumentation>
                <generateModelDocumentation>false</generateModelDocumentation>
                <configOptions>
                    <dateLibrary>java8</dateLibrary>
                    <sourceFolder>restclient</sourceFolder>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

The generated HTTP client is based on RestTemplate and will be generated to the folder target/generated-sources/restclient. You may have to configure your IDE to import the generated client in order to use it . (In case of Eclipse, you can configure in Project Properties ➡️ Java Build Path ➡️ Add the folder of the generated rest client)

To start generating the client , just execute the maven command :

mvn clean compile

To use the generated HTTP client :

ApiClient apiClient = new ApiClient();

//Override the default API base path configured in Maven
apiClient.setBasePath("http://api.example.com/api");

EmployeeManagementSystemApi api = new EmployeeManagementSystemApi(apiClient);
api.getEmployeeById(1l);

Note :

  • If you come across javax/xml/bind/annotation/XmlRootElement exception during generation when using java8+ , you may need to refer to this.
Ranunculus answered 22/8, 2019 at 5:10 Comment(4)
Thank you can Ken, very good explanation. I want to ask you something, if I want to make a JAR from this Spring Boot app, and use it as a dependency in another app how should I do? I know that a Spring Boot Fat JAR is different from a traditional JAR and it doesn't contain classes from src/main/java, but I've added the spring-boot-maven-plugin in the pom.xml and it creates 2 JARs, one is the Fat JAR and another JAR that contains the classes from src/main/java, but it still doesn't contain the classes from target/generated-sources. Do you know how can I create this kind of JAR?Verner
Hi @Verner , refer to the pom.xml of that JAR dependency , you can find its <groupId>,,<artifactId> and <version>. Then in the project that want to use this JAR dependency, simply revise its pom.xml to include the JAR dependency in <dependency> section . You have to go to the dependency project first to build and install the dependency JAR to your local maven repository by executing something like mvn clean package install .Then the project that include the dependency should be able to get the JAR from your local maven repositoryRanunculus
Can the same configuration be used for the projects having build.gradle instead of pom.xmlZosi
some response classes are not including in swagger.json, while it generate some classes are not available inside definition tag in apidocBobbibobbie
R
9

Updated:

Your question was answered in another post. Take a look at: related post

...

FYI a simple approach using command line:

There is a good tutorial at baeldung about it: how to create rest client with swagger codegen

E.g. Execute command:

java -jar swagger-codegen-cli.jar generate \
  -i http://mydomain/v2/swagger.json \
  --api-package com.mypackage.api \
  --model-package com.mypackage.model \
  --invoker-package com.mypackage.invoker \
  --group-id com.mygroup \
  --artifact-id spring-swagger-codegen-api-client \
  --artifact-version 0.0.1-SNAPSHOT \
  -l java \
  --library resttemplate \
  -o spring-swagger-codegen-api-client

Swagger Codegen supports the following Client implementations:

  1. jersey1 + Jackson
  2. Jersey2 + Jackson
  3. Feign + Jackson
  4. OkHttp + Gson
  5. Retrofit2/OkHttp + Gson
  6. Spring RestTemplate + Jackson
  7. Resteasy + Jackson

P.S. As you can see, the rest client is generated from swagger spec definition and it is definided with “-i” argument.

Robrobaina answered 21/8, 2019 at 0:16 Comment(2)
--library resttemplate isn't available in swagger-codegen 3. I can't find an alternativeAltheaalthee
RestTemplate is still present in swagger-codegen-cli 3. You could try the following example: java -jar swagger-codegen-cli-3.0.41.jar generate -i petstore.swagger.io/v2/swagger.json --api-package com.mypackage.api --model-package com.mypackage.model --invoker-package com.mypackage.invoker --group-id com.mygroup --artifact-id spring-swagger-codegen-api-client --artifact-version 0.0.1-SNAPSHOT -l java --library resttemplate -o spring-swagger-codegen-api-clientRobrobaina
G
3

Swagger Endpoints

Let's say your application's Swagger endpoints can be accessed at:

  1. Testing Swagger 2.0 JSON API documentation

    http://localhost:8080/v2/api-docs?group=employee

    http://localhost:8080/v2/api-docs (if you haven't set a group named employee)

  2. Testing Swagger UI

    http://localhost:8080/swagger-ui.html

Download Swagger Codegen Executable

You can download the swagger-codegen-cli-2.4.7.jar from the Maven Central Repository.

Generating Client Code

Now that you have the Swagger Codegen JAR, you can generate the REST client by executing the following command:

java -jar swagger-codegen-cli-2.4.7.jar generate \
  -i http://localhost:8080/v2/api-docs?group=employee \
  -l java \
  -o swagger-codegen-client

if no swagger grouping,

java -jar swagger-codegen-cli-2.4.7.jar generate \
  -i http://localhost:8080/v2/api-docs \
  -l java \
  -o swagger-codegen-client

Options

Though Swagger Codegen CLI comes with a number of options, we're using the options which are absolutely necessary for generating the client code.

  • -i the URL pointing to your application's Swagger api docs.
  • -l the programming language of the client which in this case is java
  • -o the output folder for the generated client code.

Once you execute the previous command for generating the code, you should notice notice the following message on your terminal:

[main] INFO io.swagger.parser.Swagger20Parser - reading from http://localhost:8080/v2/api-docs?group=employee
[main] WARN io.swagger.codegen.ignore.CodegenIgnoreProcessor - Output directory does not exist, or is inaccessible. No file (.swagger-codegen-ignore) will be evaluated.
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/src/main/java/io/swagger/client/model/Employee.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/docs/Employee.md
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/src/main/java/io/swagger/client/api/EmployeeControllerApi.java
...
[main] INFO io.swagger.codegen.AbstractGenerator - writing file swagger-codegen-client/src/main/java/io/swagger/client/ApiClient.java
...

REST Client Project

Once the code generation is completed, you should notice a gradle/maven project with the following structure:

__ swagger-codegen-client
  |__ README.md
  |__ build.gradle
  |__ build.sbt
  |__ docs
  |__ git_push.sh
  |__ gradle
  |__ gradle.properties
  |__ gradlew
  |__ gradlew.bat
  |__ pom.xml
  |__ settings.gradle
  |__ src
     |__ main
        |__ java
          |__ io.swagger.client.api
             |__ EmployeeControllerApi.java
     |__ test
        |__ java
          |__ io.swagger.client.api
             |__ EmployeeControllerApiTest.java

An example of a generated client project can be found here.

Using the REST Client

The client project contains lot of java classes. However the most important class is the EmployeeControllerApi.java. This's the class which contains all the logic for making REST client classes.

The other important class is EmployeeControllerApiTest.java. It shows you how to use the EmployeeControllerApi.java. The generated client project also provides a README file which is very helpful.

URL Changes

The ApiClient class contains information related to establishing a HTTP client connection. Please make sure the basePath to your REST application is correct. In the generated example, the basePath had a https://localhost:8080 URL instead of http://localhost:8080.

Java 12 Changes

The generated project works well with Java 8. If you are using Java 12, you'll have to add the following dependencies to make the project compile:

    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.3.0</version>
    </dependency>

    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version>
    </dependency>

Example REST Call

Here's an example of creating an employee by making a REST POST method method call.

Employee employee = new Employee();
employee.setId(3L);
employee.setFirstName("Sam");
employee.setLastName("Fox");
employee.setEmail("[email protected]");

EmployeeControllerApi api = new EmployeeControllerApi();
Employee response = api.createEmployeeUsingPOST(employee);
System.out.println(response);

You should a response similar to this:

class Employee {
    email: [email protected]
    firstName: Sam
    id: 3
    lastName: Fox
}

You can find a complete example here.

Gardiner answered 24/8, 2019 at 0:54 Comment(0)
H
1

1) Go to https://editor.swagger.io create your swagger documentation, i am using "Swagger Petstore" as example

2) Now select File, Import File and upload the downloaded swagger.json file

3) Open https://swagger.io/tools/swagger-codegen/

4) Use following steps :

i) Clone the repository to disk git clone https://github.com/swagger-api/swagger-codegen.git

ii) Run mvn clean package

iii) Copy the swagger-codegen-cli.jar file from the target folder to a local drive on your computer.

iv) Next execute the following command to generate a client:

     java -jar swagger-codegen-cli.jar -i <json_file> -l python -o my_client

There are three arguments for this command:

 -i Specifies the path to the input file. This can be a URL

 -l Specifies the programming language for the client

 -o Specifies the output directory where the generate code should be located

Swagger Codegen is an open source project which allows generation of API client libraries (SDK generation), server stubs, and documentation automatically from an OpenAPI Specification. Swagger Codegen is available for download in the GitHub repository, or can be generated for any new or existing OpenAPI-defined API in the integrated SwaggerHub platform. SwaggerHub brings the Swagger Editor, UI, and Codegen tools to the cloud in an integrated API design and documentation, built for API teams working with the Swagger (OpenAPI) specification.

There are plugins for build tools like Maven and Gradle, since already given on few answerso not adding here

Horwitz answered 22/8, 2019 at 16:33 Comment(0)
D
0

just add a swagger plugin does not generate a rest client you need to follow these steps as below.

write down the specification in YAML format. Based on the specification outcome, will be generated. Save the specification as YAML file. It will be saved as swagger.yaml follow the tutorial: https://howtodoinjava.com/swagger2/code-generation-for-rest-api/

Deadly answered 22/8, 2019 at 6:47 Comment(0)
D
0

I struggled a lot with this and ended up making my own tool. It is really small and depends only on spring-web. I even filed a PR to see if it can be part of Spring.

I call it Spring RestTemplate Client and it does what Feign and other tools does, but much more lightweight and only supports Spring.

final MyApiInterface myClient = SpringRestTemplateClientBuilder
  .create(MyApiInterface.class)
  .setUrl(this.getMockUrl())
  .setRestTemplate(restTemplate)         // Optional
  .setHeader("header-name", "the value") // Optional
  .setHeaders(HttpHeaders)               // Optional
  .build();

Any call on myClient will be translated to a HTTP call using the annotations of MyApiInterface:

final ResponseEntity<MyDTO> response = myClient.getMyDto();
Dearly answered 26/2, 2022 at 16:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.