From dbdf4c8e3185daf6db2b370c1eb39820e750b719 Mon Sep 17 00:00:00 2001
From: mrmeku <mrmeku@gmail.com>
Date: Fri, 26 Jul 2019 14:02:25 -0600
Subject: [PATCH] feat: Enable bazel generation

---
 .bazelrc                                      |   0
 .gitignore                                    |   1 +
 BUILD.bazel                                   |   9 ++
 WORKSPACE                                     |   5 +
 defs.bzl                                      |   9 ++
 .../openapi-generator-bazel/src/BUILD.bazel   |   1 +
 modules/openapi-generator-bazel/src/defs.bzl  | 147 ++++++++++++++++++
 .../openapi-generator-bazel/src/test/BUILD    |  41 +++++
 .../src/test/petstore.yaml                    | 101 ++++++++++++
 openapi/BUILD                                 |   0
 10 files changed, 314 insertions(+)
 create mode 100644 .bazelrc
 create mode 100644 BUILD.bazel
 create mode 100644 WORKSPACE
 create mode 100644 defs.bzl
 create mode 100644 modules/openapi-generator-bazel/src/BUILD.bazel
 create mode 100644 modules/openapi-generator-bazel/src/defs.bzl
 create mode 100644 modules/openapi-generator-bazel/src/test/BUILD
 create mode 100644 modules/openapi-generator-bazel/src/test/petstore.yaml
 create mode 100644 openapi/BUILD

diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/.gitignore b/.gitignore
index 18d2a7a1027..f77f474fe58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ modules/openapi-generator-gradle-plugin/bin/
 .classpath
 lib/*
 build/*
+bazel-*
 generated-files/*
 generated-sources/*
 generated-code/*
diff --git a/BUILD.bazel b/BUILD.bazel
new file mode 100644
index 00000000000..8cfb7fb8a19
--- /dev/null
+++ b/BUILD.bazel
@@ -0,0 +1,9 @@
+load(
+    "//modules/openapi-generator-bazel/src:defs.bzl",
+    _openapi_generator = "openapi_generator",
+    _openapi_generator_repositories = "openapi_generator_repositories",
+)
+
+openapi_generator_repositories = _openapi_generator_repositories
+
+openapi_generator = _openapi_generator
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 00000000000..90b02304d1d
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,5 @@
+workspace(name = "io_bazel_rules_openapi_generator")
+
+load("//:defs.bzl", "openapi_generator_repositories")
+
+openapi_generator_repositories()
diff --git a/defs.bzl b/defs.bzl
new file mode 100644
index 00000000000..b95c342544b
--- /dev/null
+++ b/defs.bzl
@@ -0,0 +1,9 @@
+load(
+    "//modules/openapi-generator-bazel/src:defs.bzl",
+    _openapi_generator = "openapi_generator",
+    _openapi_generator_repositories = "openapi_generator_repositories",
+)
+
+openapi_generator = _openapi_generator
+
+openapi_generator_repositories = _openapi_generator_repositories
diff --git a/modules/openapi-generator-bazel/src/BUILD.bazel b/modules/openapi-generator-bazel/src/BUILD.bazel
new file mode 100644
index 00000000000..5a203b3ab13
--- /dev/null
+++ b/modules/openapi-generator-bazel/src/BUILD.bazel
@@ -0,0 +1 @@
+exports_files(["defs.bzl"])
diff --git a/modules/openapi-generator-bazel/src/defs.bzl b/modules/openapi-generator-bazel/src/defs.bzl
new file mode 100644
index 00000000000..b9368068b32
--- /dev/null
+++ b/modules/openapi-generator-bazel/src/defs.bzl
@@ -0,0 +1,147 @@
+def openapi_generator_repositories(openapi_generator_cli_version = "4.1.0", prefix = "io_bazel_rules_openapi_generator"):
+    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:io_bazel_rules_openapi_generator/dependency/openapi-generator-cli"),
+            allow_single_file = True,
+        ),
+    },
+    implementation = _impl,
+)
diff --git a/modules/openapi-generator-bazel/src/test/BUILD b/modules/openapi-generator-bazel/src/test/BUILD
new file mode 100644
index 00000000000..5fd5e818428
--- /dev/null
+++ b/modules/openapi-generator-bazel/src/test/BUILD
@@ -0,0 +1,41 @@
+load("//: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",
+    },
+)
diff --git a/modules/openapi-generator-bazel/src/test/petstore.yaml b/modules/openapi-generator-bazel/src/test/petstore.yaml
new file mode 100644
index 00000000000..790948cb93d
--- /dev/null
+++ b/modules/openapi-generator-bazel/src/test/petstore.yaml
@@ -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
diff --git a/openapi/BUILD b/openapi/BUILD
new file mode 100644
index 00000000000..e69de29bb2d
-- 
GitLab