Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to develop a simple REST Client using Swagger codegen?

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!

like image 206
elvis Avatar asked Aug 18 '19 15:08

elvis


People also ask

What is the use of swagger codegen?

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.

How do I create a REST client?

Generate API code with swagger codegen tool To further generate source code, this swagger. yaml file will be the source of input. To facilitate it, Swagger CodeGen tool is used. Swagger provides utility jar to generate client REST clients for different programming languages and framework.


2 Answers

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.
like image 192
Ken Chan Avatar answered Sep 22 '22 08:09

Ken Chan


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.

like image 42
Ariel Carrera Avatar answered Sep 20 '22 08:09

Ariel Carrera