diff --git a/third_party/BUILD b/third_party/BUILD index 3d425bda..fafdf326 100644 --- a/third_party/BUILD +++ b/third_party/BUILD @@ -3,3 +3,10 @@ java_binary( main_class = "com.google.copybara.Main", runtime_deps = [":copybara_deploy.jar"], ) + +java_binary( + name = "openapi_generator", + main_class = "org.openapitools.codegen.OpenAPIGenerator", + visibility = ["//visibility:public"], + runtime_deps = [":openapi-generator-cli-6.6.0.jar"], +) diff --git a/third_party/openapi-generator-cli-6.6.0.jar b/third_party/openapi-generator-cli-6.6.0.jar new file mode 100644 index 00000000..a7b8736b Binary files /dev/null and b/third_party/openapi-generator-cli-6.6.0.jar differ diff --git a/tools/build_rules/oapi_gen/BUILD b/tools/build_rules/oapi_gen/BUILD new file mode 100644 index 00000000..c49420c0 --- /dev/null +++ b/tools/build_rules/oapi_gen/BUILD @@ -0,0 +1,3 @@ +exports_files([ + "oapi.sh", +]) diff --git a/tools/build_rules/oapi_gen/defs.bzl b/tools/build_rules/oapi_gen/defs.bzl new file mode 100644 index 00000000..fb790331 --- /dev/null +++ b/tools/build_rules/oapi_gen/defs.bzl @@ -0,0 +1,28 @@ +load("@aspect_rules_js//js:defs.bzl", "js_library") +load("//third_party/grpc-gateway/protoc-gen-openapiv2:defs.bzl", "protoc_gen_openapiv2") +load("//tools/build_rules/ui_library:defs.bzl", "ui_library") +load(":ts.bzl", _oapi_gen_ts_sdk = "oapi_gen_ts_sdk") + +oapi_gen_ts_sdk = _oapi_gen_ts_sdk + +def oapi_gen_ts(name, proto, visibility = ["//visibility:private"], **kwargs): + protoc_gen_openapiv2( + name = name + "_openapiv2", + proto = proto, + simple_operation_ids = True, + single_output = True, + visibility = ["//visibility:private"], + ) + + oapi_gen_ts_sdk( + name = name + "_gen", + openapi = ":" + name + "_openapiv2", + visibility = ["//visibility:private"], + **kwargs + ) + + js_library( + name = name, + srcs = [name + "_gen"], + visibility = visibility, + ) diff --git a/tools/build_rules/oapi_gen/oapi.sh b/tools/build_rules/oapi_gen/oapi.sh new file mode 100755 index 00000000..453e67b1 --- /dev/null +++ b/tools/build_rules/oapi_gen/oapi.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cli_bin="{{generator_cli_bin}}" +generator="{{generator}}" +openapi_file="{{openapi_file}}" +output_dir="{{output_dir}}" + +$cli_bin generate -i $openapi_file -g $generator -o $output_dir --skip-validate-spec > /dev/null diff --git a/tools/build_rules/oapi_gen/ts.bzl b/tools/build_rules/oapi_gen/ts.bzl new file mode 100644 index 00000000..cbe55574 --- /dev/null +++ b/tools/build_rules/oapi_gen/ts.bzl @@ -0,0 +1,67 @@ +def _oapi_gen_ts_sdk_impl(ctx): + """ + :param ctx: rule context + + Converts an OpenAPI spec to a TypeScript SDK using openapi-generator. + """ + openapi = ctx.file.openapi + + # java_binary returns the jar and a wrapper binary that runs it. + # we need the wrapper binary + oapi_cli_outputs = ctx.files._oapi_bin + oapi_cli = "" + for output in oapi_cli_outputs: + if output.path.endswith(".jar"): + continue + oapi_cli = output.path + break + + # Run openapi-generator. With typescript we can just export a directory + # containing the generated code. + output_dir = ctx.actions.declare_directory(ctx.attr.name) + index_ts = ctx.actions.declare_file(ctx.attr.name + "/index.ts") + gen = ctx.actions.declare_file(ctx.attr.name + "_gen.sh") + ctx.actions.expand_template( + template = ctx.file._oapi_converter, + output = gen, + substitutions = { + "{{generator_cli_bin}}": oapi_cli, + "{{openapi_file}}": openapi.path, + "{{generator}}": "typescript-fetch", + "{{output_dir}}": output_dir.path, + }, + is_executable = True, + ) + + ctx.actions.run( + inputs = [openapi] + ctx.files._oapi_bin, + outputs = [output_dir, index_ts], + executable = gen, + ) + + return [ + DefaultInfo( + files = depset([output_dir, index_ts]), + ), + ] + +oapi_gen_ts_sdk = rule( + implementation = _oapi_gen_ts_sdk_impl, + attrs = { + "openapi": attr.label( + allow_single_file = True, + mandatory = True, + doc = "The OpenAPI spec for the API, can be generated with protoc_gen_openapiv2", + ), + "_oapi_bin": attr.label( + allow_files = True, + default = Label("//third_party:openapi_generator"), + doc = "The openapi-generator CLI binary", + ), + "_oapi_converter": attr.label( + allow_single_file = True, + default = Label("//tools/build_rules/oapi_gen:oapi.sh"), + doc = "The script that converts an OpenAPI spec to a fern generators.yaml file", + ), + }, +)