OpenAPITools / openapi-generator-bazel

Bazel plugin for OpenAPI Generator
Apache License 2.0
66 stars 46 forks source link

Cannot depend on generated client #22

Open AndrewGuenther opened 3 years ago

AndrewGuenther commented 3 years ago

Hopefully I'm missing something simple, but so far as I can tell, when using openapi_generator it doesn't appear to declare and outputs. So in my use case, I am generating a Python client which will then be depended on by a py_library. This doesn't appear to work.

I'd appreciate if someone could provide an example where they depend on a generated client and how that gets wired up. Thanks!

Some additional context on what I've tried:

openapi_generator(
    name = "my_api_client_src",
    generator = "python",
    spec = ":my_api_spec.yaml",
    deps = [
        ":my_api_spec"
    ],
    visibility = ["//visibility:public"]
)

py_library(
    name = "my_library",
    deps = [":my_api_client_src"]
)

The above will claim that the my_api_client_src target doesn't contain any python files, so I've tried depending on it as data instead and making a dedicated py_library target for it, but that doesn't appear to work either.

rajukrishnamurthy commented 2 years ago

This seems to be similar to issue #6 I think it has something to do the implementation returning all the files:

    return DefaultInfo(files = depset([
        declared_dir,
    ]))
juhwan-jeong-hub commented 2 years ago

Any updates on this? I'm trying to use go_library

openapi_generator(
    name="sample-go-src",
    generator="go",
    spec="sample.yaml",
    api_package = "com.swagger.api",
    model_package = "com.swagger.model",
)

go_library(
  name = "sample-go",
#  srcs = glob(["**/*.go"]),
#  deps = [":sample-go-src"],
  srcs = [":sample-go-src"],
  importpath = <omitted>,
)

Building sample-go fails with Error in fail: Unknown source type sample-go-src

ashwin153 commented 2 years ago

I can't depend on the generated code either in typescript. @wing328 Can you post an example of how to depend on the outputs of this?

N1v0k commented 2 years ago

Hi,

I tried to solve this issue but unfortunately I couldn't find a way around a major obstacle.

The generator-cli's output is not transparent, which is the reason why the output files can't be defined in advance, which is required for bazel.

In other words, I couldn't find a way to define the output files based on the yaml, since the way bazel is built, you would have to parse the yaml in advance and additionally know the implementation of the generator-cli, which defines the naming convention (eg. InlineResponse500.ts)

The workaround for me is to just generate the client manually and compiling the resulting typescript files as usual with bazel. (Which is not a solution one is looking for in this repo)

I don't really see any benefit in using this "bazel" tool. Which is a pity if you try to use open-api in a bigger project, where you need to automate these steps.

rajukrishnamurthy commented 2 years ago

I think the only way to do this is have this be a appropriate provider and create a switch statement for every language to filter for the "library" files. I was going to write something specific for the few languages that we needed support for, but we went a completely different route.

ahumesky commented 2 years ago

Hi, this question was raised on stackoverflow, and some workarounds, at least for python, are discussed there: https://stackoverflow.com/a/73411230/6327445

ahumesky commented 2 years ago

Also, the java rules can take a source jar in srcs, so one workaround for the generated java code is to zip up the java files from the generated directory. Something like this:

WORKSPACE:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "openapi_tools_generator_bazel",
    sha256 = "c6e4c253f1ae0fbe4d4ded8a719f6647273141d0dc3c0cd8bb074aa7fc3c8d1c",
    urls = ["https://github.com/OpenAPITools/openapi-generator-bazel/releases/download/0.1.5/openapi-tools-generator-bazel-0.1.5.tar.gz"],
)

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 = "5.1.0",
    sha256 = "62f9842f0fcd91e4afeafc33f19a7af41f2927c7472c601310cedfc72ff1bb19"
)

RULES_JVM_EXTERNAL_TAG = "4.2"
RULES_JVM_EXTERNAL_SHA = "cd1a77b7b02e8e008439ca76fd34f5b07aecb8c752961f9640dea15e9e5ba1ca"
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//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")
rules_jvm_external_setup()
load("@rules_jvm_external//:defs.bzl", "maven_install")

maven_install(
    artifacts = [
        "io.swagger:swagger-annotations:1.5.24",
        "com.google.code.findbugs:jsr305:3.0.2",
        "com.squareup.okhttp3:okhttp:3.14.7",
        "com.squareup.okhttp3:logging-interceptor:3.14.7",
        "com.google.code.gson:gson:2.8.6",
        "io.gsonfire:gson-fire:1.8.4",
        "org.apache.commons:commons-lang3:3.10",
        "org.threeten:threetenbp:1.4.3",
        "javax.annotation:javax.annotation-api:1.3.2",
    ],
    repositories = [
        "https://repo1.maven.org/maven2",
    ],
)

BUILD:

load("@openapi_tools_generator_bazel//:defs.bzl", "openapi_generator")

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

genrule(
  name = "get_petstore_java_srcs",
  srcs = [":gen_petstore_java"],
  outs = ["petstore.srcjar"],
  exec_tools = ["@bazel_tools//tools/zip:zipper"],
  cmd = """
files=$$(find -L $(location :gen_petstore_java)/src/main/java -type f)
zipper_args=()
for f in $${files[@]}; do
  path_in_zip="$${f/bazel-out\\/k8-fastbuild\\/bin\\/gen_petstore_java\\/src\\/main\\/java\\//}"
  zipper_args+=("$$path_in_zip=$$f")
done
$(location @bazel_tools//tools/zip:zipper) c $@ $${zipper_args[@]}
""", 
)

java_library(
  name = "petstore_java",
  srcs = [":petstore.srcjar"],
  deps = [
    "@maven//:io_swagger_swagger_annotations",
    "@maven//:com_google_code_findbugs_jsr305",
    "@maven//:com_squareup_okhttp3_okhttp",
    "@maven//:com_squareup_okhttp3_logging_interceptor",
    "@maven//:com_google_code_gson_gson",
    "@maven//:io_gsonfire_gson_fire",
    "@maven//:org_apache_commons_commons_lang3",
    "@maven//:org_threeten_threetenbp",
    "@maven//:javax_annotation_javax_annotation_api",
  ],
)

java_binary(
  name = "petstore_java_main",
  srcs = ["java/petstore/Main.java"],
  deps = [":petstore_java"],
  main_class = "petstore.Main",
)

The maven deps I got from the generated build.gradle file, but I got compilation errors like this

org/openapitools/client/ApiClient.java:818: error: no suitable method found for create(byte[],MediaType)

so something's not lined up between the generated code and the deps, but the main idea is the get_petstore_java_srcs genrule.

loeffel-io commented 1 year ago

+1

loeffel-io commented 1 year ago

this makes it "impossible" to work with in a multi language repository

PertsevRoman commented 1 year ago

Hi guys, I implemented a simple workaround. Bazel requires to know exact list of input and output files. Also Bazel does not offer anything that could be useful during an analysis (parse swagger/openapi YAML, etc.). So I forked the repo and specified additional attributes which takes generated models list. Now generation works well and I'm able to import models/invokers to java_library target and so on. There is possible solution from my perspective: add something like gazelle functionality and generate bazel code manually but using a convenient tool. WDYT ?

lionelfleury commented 1 year ago

Hi all, A simple Golang example - keeping only relevant *.go files:

load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@openapi_tools_generator_bazel//:defs.bzl", "openapi_generator")
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")

openapi_generator(
    name = "petstore",
    generator = "go-gin-server",
    spec = "petstore.yml",
)

copy_to_directory(
    name = "petstore.go",
    srcs = [":petstore"],
    exclude_srcs_patterns = ["**/main.go"],
    include_srcs_patterns = ["**/*.go"],
)

go_library(
    name = "server_lib",
    srcs = [":petstore.go"],
    importpath = "petstore",
    visibility = ["//visibility:private"],
    deps = ["@com_github_gin_gonic_gin//:go_default_library"],
)

go_binary(
    name = "server",
    srcs = ["main.go"],
    visibility = ["//visibility:public"],
    deps = [":server_lib"],
)

The trick is the petstore.go folder name to fool rules_go that this is valid input.

NOTE: you need your own main.go file:

package main

import (
    "log"
    sw "petstore"
)

func main() {
    log.Printf("Server started")
    router := sw.NewRouter()
    log.Fatal(router.Run(":8080"))
}

Inspired from C++ example here: https://stackoverflow.com/questions/48417712/how-to-build-static-library-from-the-generated-source-files-using-bazel-build

guillaumep commented 12 months ago

Using copy_to_directory and including the result as data instead of srcs seems to have worked for us:

openapi_generator(
    name = "xcover_client_codegen",
    additional_properties = {
        "packageName": "xcover_python_client",
    },
    generator = "python",
    spec = "swagger.json",
)

copy_to_directory(
    name = "xcover_python_client",
    srcs = [":xcover_client_codegen"],
    include_srcs_patterns = ["xcover_client_codegen/xcover_python_client/**"],
    replace_prefixes = {
        "xcover_client_codegen/xcover_python_client": "",
    },
)

py_library(
    name = "xcover_python_client_lib",
    # Output from openapi_generator is a directory and not a list of files, so we
    # include it as data instead of as srcs, in order to prevent Bazel from emitting
    # a warning that no .py files were found.
    data = [
        ":xcover_python_client",
    ],
    imports = ["."],
    visibility = ["//visibility:public"],
    deps = [
        requirement("python_dateutil"),
        requirement("urllib3"),
    ],
)

But really the OpenAPI generator rules should probably be rewritten as repository rules... (https://bazel.build/extending/repo)

fjij commented 9 months ago

Here's a solution to the problem that doesn't use copy_to_directory. It's similar to @guillaumep's solution, but uses the imports attribute of py_library.

openapi_generator(
    name = "my_client",
    additional_properties = {
        "packageName": "my_client",
    },
    generator = "python",
    spec = "swagger.json",
)

py_library(
    name = "my_client_py",
    data = [":my_client"],
    imports = ["./my_client"],
    visibility = ["//visibility:public"],
    deps = [
        requirement("python_dateutil"),
        requirement("urllib3"),
    ],
)
hnoshab75 commented 8 months ago

I have seen some issues while running the go-gin-server generator, has anyone been able to use the go-server generator for go successfully?

If we try to use the copy_to_directory(), the go_library() panics with unused dep statements.

darkrift commented 7 months ago

I have seen some issues while running the go-gin-server generator, has anyone been able to use the go-server generator for go successfully?

If we try to use the copy_to_directory(), the go_library() panics with unused dep statements.

This would be a bug in the code generation and should be fixed as part of the main project. I am able to generate code with the go-server generator in my project.

Make sure you also use the latest version of the cli.

loeffel-io commented 5 months ago

fyi: https://github.com/meetup/rules_openapi/issues/56#issuecomment-2007747966