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

Introducing Python Package CVXOPT: Implementing SVM from Scratch

DevOps Pipeline with Microsoft Azure Services

Azure DevOps Cover Photo

GitOps Quick Start with Kubernetes KIND Cluster

April Recap for Razor network

Usefull MongoDB Queries

MongoDB Usefull Queries

Send Mail Example In Laravel 8

Send Mail Example In Laravel 8

Making a Pulse Oximeter using Arduino

[SOLUTION] Xbox Wireless Adapter/Xbox Elite Series 2 Wireless Controller disconnects constantly on…

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

Decouple Configuration from Application Code with Spring Cloud Config

Using TLS on your Spring-Boot SOAP Service

Read, Write and Delete Operations for S3 Bucket using Spring boot

Development Environment Setup for Spring Boot-based Microservices With Maven and GitLab