1. 概要

名前が示すように、 OpenAPI Generator は、OpenAPI仕様からコードを生成します。 クライアントライブラリ、サーバースタブ、ドキュメント、および構成のコードを作成できます。

さまざまな言語とフレームワークをサポートしています。 特に、C ++、C#、Java、PHP、Python、Ruby、Scala –ほぼ広く使用されているのものすべてがサポートされています。

このチュートリアルでは、Mavenプラグインを介してOpenAPIGeneratorを使用してSpringベースのサーバースタブを実装する方法を学習します。 ジェネレーターを使用する他の方法は、CLIまたはオンラインツールを使用することです。

2. YAMLファイル

まず、APIを指定するYAMLファイルが必要です。 これをジェネレーターへの入力として提供し、サーバースタブを生成します。

petstore.ymlのスニペットは次のとおりです。

openapi: "3.0.0"
paths:
  /pets:
    get:
      summary: List all pets
      operationId: listPets
      tags:
        - pets
      parameters:
        - name: limit
          in: query
          ...
      responses:
        ...
    post:
      summary: Create a pet
      operationId: createPets
      ...
  /pets/{petId}:
    get:
      summary: Info for a specific pet
      operationId: showPetById
      ...
components:
  schemas:
    Pet:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        tag:
          type: string
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string

3. Mavenの依存関係

3.1. OpenAPIジェネレーターのプラグイン

次に、ジェネレータプラグインのMaven依存関係を追加しましょう。

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>5.1.0</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>
                    ${project.basedir}/src/main/resources/petstore.yml
                </inputSpec>
                <generatorName>spring</generatorName>
                <apiPackage>com.baeldung.openapi.api</apiPackage>
                <modelPackage>com.baeldung.openapi.model</modelPackage>
                <supportingFilesToGenerate>
                    ApiUtil.java
                </supportingFilesToGenerate>
                <configOptions>
                    <delegatePattern>true</delegatePattern>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

ご覧のとおり、YAMLファイルをinputSpecとして渡しました。 その後、Springベースのサーバーが必要になるため、generatorNamespringとして使用しました。

次に、 apiPackage は、APIが生成されるパッケージ名を指定します。 次に、ジェネレーターがデータモデルを配置するmodelPackageがあります。 delegatePatterntrueに設定して、カスタマイズされた@Serviceクラスとして実装できるインターフェイスを作成するように求めています。

重要なのは、OpenAPI Generatorのオプションは、CLI、Maven / Gradleプラグイン、またはオンライン生成オプションのいずれを使用していても同じです。

3.2. Mavenの依存関係

Springサーバーを生成するので、生成されたコードが期待どおりにコンパイルおよび実行されるように、その依存関係(Spring BootStarterWebおよびSpringDataJPA)も必要です

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.4.4</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>2.4.6</version>
    </dependency>
</dependencies>

上記のSpring依存関係とは別に、生成されたコードが正常にコンパイルされるように、jackson-databindおよびswagger2依存関係も必要です。

<dependency>
    <groupId>org.openapitools</groupId>
    <artifactId>jackson-databind-nullable</artifactId>
    <version>0.2.1</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

4. コード生成

サーバースタブを生成するには、以下を実行する必要があります。

mvn clean install

その結果、次のようになります。

次に、apiPackageの内容から始めてコードを見てみましょう。

まず、 PetsApi と呼ばれるAPIインターフェースを取得します。これには、YAML仕様で定義されているすべてのリクエストマッピングが含まれています。 スニペットは次のとおりです。

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
@Validated
@Api(value = "pets", description = "the pets API")
public interface PetsApi {
    /**
     * GET /pets : List all pets
     *
     * @param limit How many items to return at one time (max 100) (optional)
     * @return A paged array of pets (status code 200)
     *         or unexpected error (status code 200)
     */
    @ApiOperation(value = "List all pets", nickname = "listPets", notes = "", 
      response = Pet.class, responseContainer = "List", tags={ "pets", })
    @ApiResponses(value = { @ApiResponse(code = 200, message = "A paged array of pets", 
      response = Pet.class, responseContainer = "List"),
      @ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
    @GetMapping(value = "/pets", produces = { "application/json" })
    default ResponseEntity<List> listPets(@ApiParam(
      value = "How many items to return at one time (max 100)") 
      @Valid @RequestParam(value = "limit", required = false) Integer limit) {
        return getDelegate().listPets(limit);
    }

    // other generated methods
}

次に、デリゲートパターンを使用しているため、OpenAPIはPetsApiDelegateというデリゲーターインターフェイスも生成します。 特に、このインターフェースで宣言されたメソッドは、デフォルトで501 NotImplementationedのHTTPステータスを返します。

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
public interface PetsApiDelegate {
    /**
     * GET /pets : List all pets
     *
     * @param limit How many items to return at one time (max 100) (optional)
     * @return A paged array of pets (status code 200)
     *         or unexpected error (status code 200)
     * @see PetsApi#listPets
     */
    default ResponseEntity<List<Pet>> listPets(Integer limit) {
        getRequest().ifPresent(request -> {
            for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
                if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
                    String exampleString = "{ \"name\" : \"name\", \"id\" : 0, \"tag\" : \"tag\" }";
                    ApiUtil.setExampleResponse(request, "application/json", exampleString);
                    break;
                }
            }
        });
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }

    // other generated method declarations
}

その後、 PetsApiControllerクラスがあり、デリゲーターに接続するだけです。

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
@Controller
@RequestMapping("${openapi.swaggerPetstore.base-path:}")
public class PetsApiController implements PetsApi {

    private final PetsApiDelegate delegate;

    public PetsApiController(
      @org.springframework.beans.factory.annotation.Autowired(required = false) PetsApiDelegate delegate) {
        this.delegate = Optional.ofNullable(delegate).orElse(new PetsApiDelegate() {});
    }

    @Override
    public PetsApiDelegate getDelegate() {
        return delegate;
    }
}

modelPackage では、YAML入力で定義されたスキーマに基づいて、ErrorとPetと呼ばれるデータモデルPOJOのカップルが生成されます

Petの1つを見てみましょう。

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", 
  date = "2021-03-22T23:26:32.308871+05:30[Asia/Kolkata]")
public class Pet {
  @JsonProperty("id")
  private Long id;

  @JsonProperty("name")
  private String name;

  @JsonProperty("tag")
  private String tag;

  // constructor

  @ApiModelProperty(required = true, value = "")
  @NotNull
  public Long getId() {
    return id;
  }

  // other getters and setters

  // equals, hashcode, and toString methods
}

5. サーバーのテスト

サーバースタブがサーバーとして機能するために必要なのは、デリゲーターインターフェイスの実装を追加することだけです。

簡単にするために、ここではそれを行わず、スタブのみをテストします。 さらに、その前に、Springアプリケーションが必要です。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5.1. curlを使用したテスト

アプリケーションを起動した後、次のコマンドを実行するだけです。

curl -I http://localhost:8080/pets/

そして、これが期待される結果です:

HTTP/1.1 501 
Content-Length: 0
Date: Fri, 26 Mar 2021 17:29:25 GMT
Connection: close

5.2. 統合テスト

または、同じものに対して単純な統合テストを作成することもできます。

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class OpenApiPetsIntegrationTest {
    private static final String PETS_PATH = "/pets/";

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void whenReadAll_thenStatusIsNotImplemented() throws Exception {
        this.mockMvc.perform(get(PETS_PATH)).andExpect(status().isNotImplemented());
    }

    @Test
    public void whenReadOne_thenStatusIsNotImplemented() throws Exception {
        this.mockMvc.perform(get(PETS_PATH + 1)).andExpect(status().isNotImplemented());
    }
}

6. 結論

このチュートリアルでは、 OpenAPIジェネレーターのMavenプラグインを使用して、YAML仕様からSpringベースのサーバースタブを生成する方法を説明しました。

次のステップとして、これを使用してクライアントを生成することもできます。

いつものように、ソースコードはGitHubから入手できます。