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

  1. https://github.com/bazelbuild/rules_jvm_external/tree/master/examples/spring_boot
  2. https://docs.bazel.build/versions/4.2.1/be/java.html#java_binary.deploy_manifest_lines
  3. https://github.com/salesforce/rules_spring
  4. https://openapi-generator.tech/docs/usage/

--

--

--

Life is beautiful…

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How to use a virtual camera for Zoom,Teams,Meet etc in OBS with NDI Tools. Support for Windows,Mac.

How Ionic Became a Top 50 Github Project (And You Can Too)

9 Odd and Fun Income Ideas for Coders

Unity Development — AI Sight

The PixieBrix Manifesto: Web Customization for Everyone

In automation testing everyone is familiar with Selenium.

[18/12, 12:26 pm] Sanjay Soni UMT Katangi: Career counselling online free for students करियर…

Upload files to your Google Drive through Google Drive API– OAuth2 tutorial

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
Tom Liu

Tom Liu

Life is beautiful…

More from Medium

Compiling and running Java Source code using GraalVM and Oracle Java Development Kit

Building better GUIs using DDD and Spring HATEOAS

A Deep-dive on logging frameworks

The Apigee Edge — Intelligent API Platform