Skip to content

Commit

Permalink
Initial release of openapi_tools_generator_bazel
Browse files Browse the repository at this point in the history
  • Loading branch information
mrmeku committed Oct 21, 2019
0 parents commit 7f97f49
Show file tree
Hide file tree
Showing 9 changed files with 345 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bazel-*
Empty file added BUILD.bazel
Empty file.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# OpenAPI Generator Bazel

This repo was created to integrate the OpenAPI code generation CLI with Bazel.

## Quickstart

To use the Bazel bindings provided by this repo within a Bazel workspace,
you must do the following steps:

1. Add the following code to your WORKSPACE file at the root of your repository

```
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
openapi_tools_generator_bazel_release = "0.1.0"
http_archive(
name = "openapi_tools_generator_bazel",
sha256 = "", # Determine the SHA for the specific release you're targeting
url = "https://github.com/OpenAPITools/openapi-generator-bazel/releases/download/RELEASE/rules_nodejs-RELEASE.tar.gz".format(RELEASE = openapi_tools_generator_bazel_release),
)
load("@openapi_tools_generator_bazel//:defs.bzl", "openapi_tools_generator_bazel_repositories")
# You can provide any version of the CLI that has been uploaded to Maven
openapi_tools_generator_bazel_repositories(
openapi_generator_cli_version = "4.1.0"
)
```

2. Create a BUILD.bazel file next to the .yaml file you wish to generate code from.
The below example generates a go library within a generated directory named `petstore_go`

```
load("@openapi_tools_generator_bazel//:defs.bzl", "openapi_generator")
openapi_generator(
name = "petstore_go",
generator = "go",
spec = "petstore.yaml",
)
```
5 changes: 5 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
workspace(name = "openapi_tools_generator_bazel")

load("//:defs.bzl", "openapi_tools_generator_bazel_repositories")

openapi_tools_generator_bazel_repositories()
9 changes: 9 additions & 0 deletions defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load(
"//internal:openapi_generator.bzl",
_openapi_generator = "openapi_generator",
_openapi_tools_generator_bazel_repositories = "openapi_tools_generator_bazel_repositories",
)

openapi_generator = _openapi_generator

openapi_tools_generator_bazel_repositories = _openapi_tools_generator_bazel_repositories
Empty file added internal/BUILD.bazel
Empty file.
147 changes: 147 additions & 0 deletions internal/openapi_generator.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
def openapi_tools_generator_bazel_repositories(openapi_generator_cli_version = "4.1.0", prefix = "openapi_tools_generator_bazel"):
native.maven_jar(
name = prefix + "_cli",
artifact = "org.openapitools:openapi-generator-cli:" + openapi_generator_cli_version,
)
native.bind(
name = prefix + "/dependency/openapi-generator-cli",
actual = "@" + prefix + "_cli//jar",
)

def _comma_separated_pairs(pairs):
return ",".join([
"{}={}".format(k, v)
for k, v in pairs.items()
])

def _new_generator_command(ctx, declared_dir, rjars):
java_path = ctx.attr._jdk[java_common.JavaRuntimeInfo].java_executable_exec_path
gen_cmd = str(java_path)

gen_cmd += " -cp {cli_jar}:{jars} org.openapitools.codegen.OpenAPIGenerator generate -i {spec} -g {generator} -o {output}".format(
java = java_path,
cli_jar = ctx.file.openapi_generator_cli.path,
jars = ":".join([j.path for j in rjars.to_list()]),
spec = ctx.file.spec.path,
generator = ctx.attr.generator,
output = declared_dir.path,
)

gen_cmd += ' -p "{properties}"'.format(
properties = _comma_separated_pairs(ctx.attr.system_properties),
)

additional_properties = dict(ctx.attr.additional_properties)

# This is needed to ensure reproducible Java output
if ctx.attr.generator == "java" and \
"hideGenerationTimestamp" not in ctx.attr.additional_properties:
additional_properties["hideGenerationTimestamp"] = "true"

gen_cmd += ' --additional-properties "{properties}"'.format(
properties = _comma_separated_pairs(additional_properties),
)

gen_cmd += ' --type-mappings "{mappings}"'.format(
mappings = _comma_separated_pairs(ctx.attr.type_mappings),
)

if ctx.attr.api_package:
gen_cmd += " --api-package {package}".format(
package = ctx.attr.api_package,
)
if ctx.attr.invoker_package:
gen_cmd += " --invoker-package {package}".format(
package = ctx.attr.invoker_package,
)
if ctx.attr.model_package:
gen_cmd += " --model-package {package}".format(
package = ctx.attr.model_package,
)

# fixme: by default, openapi-generator is rather verbose. this helps with that but can also mask useful error messages
# when it fails. look into log configuration options. it's a java app so perhaps just a log4j.properties or something
gen_cmd += " 1>/dev/null"
return gen_cmd

def _impl(ctx):
jars = _collect_jars(ctx.attr.deps)
(cjars, rjars) = (jars.compiletime, jars.runtime)

declared_dir = ctx.actions.declare_directory("%s" % (ctx.attr.name))

inputs = [
ctx.file.openapi_generator_cli,
ctx.file.spec,
] + cjars.to_list() + rjars.to_list()

# TODO: Convert to run
ctx.actions.run_shell(
inputs = inputs,
command = "mkdir -p {gen_dir}".format(
gen_dir = declared_dir.path,
) + " && " + _new_generator_command(ctx, declared_dir, rjars),
outputs = [declared_dir],
tools = ctx.files._jdk,
)

srcs = declared_dir.path

return DefaultInfo(files = depset([
declared_dir,
]))

def _collect_jars(targets):
"""Compute the runtime and compile-time dependencies from the given targets""" # noqa
compile_jars = depset()
runtime_jars = depset()
for target in targets:
found = False
if hasattr(target, "scala"):
if hasattr(target.scala.outputs, "ijar"):
compile_jars = depset(transitive = [compile_jars, [target.scala.outputs.ijar]])
compile_jars = depset(transitive = [compile_jars, target.scala.transitive_compile_exports])
runtime_jars = depset(transitive = [runtime_jars, target.scala.transitive_runtime_deps])
runtime_jars = depset(transitive = [runtime_jars, target.scala.transitive_runtime_exports])
found = True
if hasattr(target, "java"):
compile_jars = depset(transitive = [compile_jars, target.java.transitive_deps])
runtime_jars = depset(transitive = [runtime_jars, target.java.transitive_runtime_deps])
found = True
if not found:
runtime_jars = depset(transitive = [runtime_jars, target.files])
compile_jars = depset(transitive = [compile_jars, target.files])

return struct(compiletime = compile_jars, runtime = runtime_jars)

openapi_generator = rule(
attrs = {
# downstream dependencies
"deps": attr.label_list(),
# openapi spec file
"spec": attr.label(
mandatory = True,
allow_single_file = [
".json",
".yaml",
],
),
"generator": attr.string(mandatory = True),
"api_package": attr.string(),
"invoker_package": attr.string(),
"model_package": attr.string(),
"additional_properties": attr.string_dict(),
"system_properties": attr.string_dict(),
"type_mappings": attr.string_dict(),
"_jdk": attr.label(
default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
providers = [java_common.JavaRuntimeInfo],
),
"openapi_generator_cli": attr.label(
cfg = "host",
default = Label("//external:openapi_tools_generator_bazel/dependency/openapi-generator-cli"),
allow_single_file = True,
),
},
implementation = _impl,
)
41 changes: 41 additions & 0 deletions internal/test/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
load("@openapi_tools_generator_bazel//:defs.bzl", "openapi_generator")

openapi_generator(
name = "petstore",
generator = "go",
spec = "petstore.yaml",
)

openapi_generator(
name = "petstore_java",
generator = "java",
spec = "petstore.yaml",
)

openapi_generator(
name = "petstore_java_feign",
additional_properties = {
"library": "feign",
},
generator = "java",
spec = "petstore.yaml",
)

openapi_generator(
name = "petstore_java_no_tests",
generator = "java",
spec = "petstore.yaml",
system_properties = {
"apiTests": "false",
"modelTests": "false",
},
)

openapi_generator(
name = "petstore_java_bigdecimal",
generator = "java",
spec = "petstore.yaml",
type_mappings = {
"Integer": "java.math.BigDecimal",
},
)
101 changes: 101 additions & 0 deletions internal/test/petstore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
swagger: "2.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
host: petstore.swagger.io
basePath: /v1
schemes:
- http
consumes:
- application/json
produces:
- application/json
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
type: integer
format: int32
responses:
"200":
description: An paged array of pets
headers:
x-next:
type: string
description: A link to the next page of responses
schema:
$ref: "#/definitions/Pets"
default:
description: unexpected error
schema:
$ref: "#/definitions/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
"201":
description: Null response
default:
description: unexpected error
schema:
$ref: "#/definitions/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
type: string
responses:
"200":
description: Expected response to a valid request
schema:
$ref: "#/definitions/Pets"
default:
description: unexpected error
schema:
$ref: "#/definitions/Error"
definitions:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/definitions/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

0 comments on commit 7f97f49

Please sign in to comment.