OpenAPI, Code Generation, Bazel, and Spring Boot

OpenAPI

openapi: 3.0.3
info:
title: Hello World API
description: Say hello API
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
version: 1.0.0
servers:
- url: /hello-service/v1
tags:
- name: Hello Service
description: Hello Service to say hello
# - name: Other Service
# description: Other Service with different tag & API interface
paths:
/hello:
put:
tags:
- Hello Service
summary: update the way to say hello.
operationId: updateHello
responses:
200:
description: Updated the way to say hello
content:
application/json:
schema:
$ref: '#/components/schemas/Hello' # this can be in another file 'path/to/file#/components/schemas/Hello'
500:
description: Error to update
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
get:
tags:
- Hello Service
summary: Get hello
operationId: getHello
responses:
200:
description: Got hello object
content:
application/json:
schema:
$ref: '#/components/schemas/Hello'
500:
description: Error getting hello
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/hello/{language}:
get:
tags:
- Hello Service
summary: Get hello by language
operationId: getHelloByLang
parameters:
- name: language
in: path
required: true
schema:
type: string
responses:
200:
description: Got hello object
content:
application/json:
schema:
$ref: '#/components/schemas/Hello'
500:
description: Error getting hello
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Hello:
type: object
properties:
language:
type: string
enum:
- English
- French
- Chinese
default: English
Content:
type: string
Error:
type: object
properties:
code:
type: integer
description: Error code
message:
type: string
description: Error message
Quite nice content

Code Generation

$ java -jar openapi-generator-cli-5.1.0.jar  generate -i hello.yaml -g spring --package-name com.awesome.tom --model-package com.awesome.tom.model --api-package com.awesome.tom.api --invoker-package com.awesome.tom -o generated
The awesome files

Bazel

workspace(name = "hello")load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")RULES_JVM_EXTERNAL_TAG = "4.1"
RULES_JVM_EXTERNAL_SHA = "f36441aa876c4f6427bfb2d1f2d723b48e9d930b62662bf723ddfb8fc80f0140"
http_archive(
name = "rules_jvm_external",
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
sha256 = RULES_JVM_EXTERNAL_SHA,
url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)
load("@rules_jvm_external//:defs.bzl", "maven_install")maven_install(
artifacts = [
"org.springframework.boot:spring-boot-autoconfigure:2.3.9.RELEASE",
"org.springframework.boot:spring-boot-test-autoconfigure:2.3.9.RELEASE",
"org.springframework.boot:spring-boot-test:2.3.9.RELEASE",
"org.springframework.boot:spring-boot:2.3.9.RELEASE",
"org.springframework.boot:spring-boot-starter-web:2.3.9.RELEASE",
"org.springframework.boot:spring-boot-starter-tomcat:2.3.9.RELEASE",
"org.springframework.boot:spring-boot-loader:2.3.9.RELEASE",
"org.springframework:spring-beans:5.2.13.RELEASE",
"org.springframework:spring-context:5.2.13.RELEASE",
"org.springframework:spring-test:5.2.13.RELEASE",
"org.springframework:spring-web:5.2.13.RELEASE",
"org.springframework:spring-webmvc:5.2.13.RELEASE",
"io.springfox:springfox-core:2.9.2",
"io.springfox:springfox-spi:2.9.2",
"io.springfox:springfox-spring-web:2.9.2",
"io.springfox:springfox-swagger2:2.9.2",
"io.springfox:springfox-swagger-ui:2.9.2",
"org.openapitools:jackson-databind-nullable:0.2.1",
"com.fasterxml.jackson.core:jackson-annotations:2.11.4",
"com.fasterxml.jackson.core:jackson-databind:2.11.4",
"io.swagger:swagger-annotations:1.5.20",
"javax.servlet:javax.servlet-api:4.0.0",
"javax.validation:validation-api:2.0.1.Final",
],
fetch_sources = True,
repositories = [
"https://repo1.maven.org/maven2",
],
)
......The method's class, ..., is available from the following locations:......Action:Correct the classpath of your application so that it contains a single, compatible version of ...
$ touch BUILD
$ bazel run @maven//:pin
Next, please update your WORKSPACE file by adding the maven_install_json attribute and loading pinned_maven_install from @maven//:defs.bzl.For example:=============================================================maven_install(
artifacts = # ...,
repositories = # ...,
maven_install_json = "@//:maven_install.json",
)
load("@maven//:defs.bzl", "pinned_maven_install")
pinned_maven_install()
=============================================================To update maven_install.json, run this command to re-pin the unpinned repository:bazel run @unpinned_maven//:pin
......maven_install(    ......    maven_install_json = "//:maven_install.json",
repositories = [
"https://repo1.maven.org/maven2",
],
)
load("@maven//:defs.bzl", "pinned_maven_install")
pinned_maven_install()
$ bazel run @unpinned_maven//:pin
load("@rules_jvm_external//:defs.bzl", "artifact")
package(default_visibility = ["//visibility:public"])
java_library(
name = "hello",
srcs = glob(["generated/src/main/java/**/*.java"]),
deps = [
artifact("org.springframework:spring-context"),
artifact("org.springframework:spring-beans"),
artifact("io.springfox:springfox-spi"),
artifact("io.springfox:springfox-swagger2"),
artifact("io.springfox:springfox-swagger-ui"),
artifact("io.springfox:springfox-core"),
artifact("io.springfox:springfox-spring-web"),
artifact("io.swagger:swagger-annotations"),
artifact("org.springframework.boot:spring-boot"),
artifact("org.springframework.boot:spring-boot-autoconfigure"),
artifact("org.springframework.boot:spring-boot-starter-web"),
artifact("org.springframework:spring-web"),
artifact("org.springframework:spring-webmvc"),
artifact("com.fasterxml.jackson.core:jackson-annotations"),
artifact("org.openapitools:jackson-databind-nullable"),
artifact("com.fasterxml.jackson.core:jackson-databind"),
artifact("javax.servlet:javax.servlet-api"),
artifact("javax.validation:validation-api"),
],
runtime_deps = [
artifact("org.springframework.boot:spring-boot-starter-tomcat")
]
)
java_binary(
name = "app",
main_class = "com.awesome.tom.OpenAPI2SpringBoot",
resources = [
"generated/src/main/resources/application.properties"
],
runtime_deps = [
":hello",
],
)
......The method's class, ..., is available from the following locations:......Action:Correct the classpath of your application so that it contains a single, compatible version of ...

Spring Boot

package com.awesome.tom.api;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
@Value("${my.defaul.hello:'Say Hello word'}")
private String hi;
public String getHello() {
return hi;
}
}
package com.awesome.tom.api;import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.NativeWebRequest;
import java.util.Optional;
import com.awesome.tom.model.Hello;
import com.awesome.tom.model.Hello.LanguageEnum;

@RestController
@RequestMapping("${openapi.helloWorld.base-path:/hello-service/v1}")
public class HelloApiController implements HelloApi {
@Autowired
private HelloService svc;
private final NativeWebRequest request; @org.springframework.beans.factory.annotation.Autowired
public HelloApiController(NativeWebRequest request) {
this.request = request;
}
@Override
public Optional<NativeWebRequest> getRequest() {
return Optional.ofNullable(request);
}
public ResponseEntity<Hello> getHello() {
try {
Hello hello = new Hello().language(LanguageEnum.ENGLISH).content(this.svc.getHello());
return new ResponseEntity<>(hello, HttpStatus.OK);
} catch (Exception e) {
com.awesome.tom.model.Error error =
new com.awesome.tom.model.Error().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).message("something went wrong");
return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

}
$ bazel run //:app
......load("@maven//:defs.bzl", "pinned_maven_install")
pinned_maven_install()
http_archive(
name = "rules_spring",
sha256 = "9385652bb92d365675d1ca7c963672a8091dc5940a9e307104d3c92e7a789c8e",
urls = [
"
https://github.com/salesforce/rules_spring/releases/download/2.1.4/rules-spring-2.1.4.zip",
],
)
load("@rules_jvm_external//:defs.bzl", "artifact")
load("@rules_spring//springboot:springboot.bzl", "springboot")
...... runtime_deps = [
artifact("org.springframework.boot:spring-boot-loader"),
artifact("org.springframework.boot:spring-boot-starter-tomcat")
]
......springboot(
name = "app-springboot",
boot_app_class = "com.awesome.tom.OpenAPI2SpringBoot",
java_library = ":hello",
)
$ bazel run //:app-springboot
$ bazel run //:app-springboot......Target //:app-springboot up-to-date:
bazel-bin/libhello.jar
bazel-bin/MANIFEST.MF
bazel-bin/bazelrun_env.sh
bazel-bin/git.properties
bazel-bin/app-springboot.jar
$ java -jar bazel-bin/app-springboot.jar
$ curl -X GET "http://localhost:8080/hello-service/v1/hello" -H "accept: application/json"
{"language":"English","Content":"Say Hello word"}

Ref

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store