如何将 OpenAPI 客户端添加为子项目?

huangapple go评论74阅读模式
英文:

How to add OpenAPI client as a subproject?

问题

成功地通过源集将生成的 OpenAPI 客户端添加到我的项目中。但是接下来我必须将依赖项复制到主 build.gradle,解决冲突 -> 我认为更好的设计是将客户端作为一个带有自己 build.gradle 的子项目。

所以我在我的 settings.gradle 中添加了 include = 'build:openapi-java-client',并在我的依赖项中添加了 compile project(':build:openapi-java-client')。这样我就有了以下文件:
build.gradle:

plugins {
    id 'java'
    id 'application'
    id "org.openapi.generator" version "4.3.1"
}

repositories {
    jcenter()
}

openApiGenerate {
    generatorName = "java"
    inputSpec = "$rootDir/specs/petstore.yaml".toString()
    outputDir = "$buildDir/openapi-java-client".toString()
    apiPackage = "org.openapi.example.api"
    invokerPackage = "org.openapi.example.invoker"
    modelPackage = "org.openapi.example.model"
    configOptions = [
        dateLibrary: "java8"
    ]
}

dependencies {
    implementation 'com.google.guava:guava:29.0-jre'
    testImplementation 'junit:junit:4.13'
    
    compile project(':build:openapi-java-client')
}

application {
    mainClassName = 'a.aa.App'
}

settings.gradle:

rootProject.name = 'simple-java-app'
include = 'build:openapi-java-client'

在将其作为子项目添加后,我提前执行了 openApiGenerate,然后执行 Gradle -> 刷新 Gradle 项目和刷新操作。

然后 Eclipse 显示了一个问题:

无法使用 Gradle 分发 'https://services.gradle.org/distributions/gradle-6.5.1-bin.zip' 运行分阶段构建操作。
设置文件 'C:\...\simple-java-app\settings.gradle' 第 11 行
在评估设置 'simple-java-app' 时出现问题。
无法为类型为 org.gradle.initialization.DefaultSettings 的设置 'simple-java-app' 设置未知属性 'include'。

我不知道接下来该怎么办,在我按照 https://guides.gradle.org/creating-multi-project-builds/ 进行操作并将 greeting-library 放入子文件夹时,处理子文件夹中的子项目完全正常。

英文:

I can successfully add a generated openapi client to my project via source sets. But then I have to copy dependencies into the main build-gradle, resolve conflicts -> I think it would be a better design to have the client as a subproject with its own build.gradle.

So I add include = 'build:openapi-java-client' to my settings.gradle and compile project(':build:openapi-java-client') to my dependencies. So that I have the following files:
build.gradle:

plugins {
id 'java'
id 'application'
id "org.openapi.generator" version "4.3.1"
}
repositories {
jcenter()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/openapi-java-client".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'junit:junit:4.13'
compile project(':build:openapi-java-client')
}
application {
mainClassName = 'a.aa.App'
}

and settings.gradle:

rootProject.name = 'simple-java-app'
include = 'build:openapi-java-client'

I execute openApiGenerate in advance, after adding it as a subproject, I do Gradle -> Refresh Gradle Project and Refresh.

Eclipse then shows me a problem:

Could not run phased build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-6.5.1-bin.zip'.
Settings file 'C:\...\simple-java-app\settings.gradle' line: 11
A problem occurred evaluating settings 'simple-java-app'.
Could not set unknown property 'include' for settings 'simple-java-app' of type org.gradle.initialization.DefaultSettings.

I don't know where to go from here, addressing subprojects in subfolders worked just fine when I worked through https://guides.gradle.org/creating-multi-project-builds/ and put greeting-library in a subfolder.

答案1

得分: 1

因为build很可能是一个输出目录,这样配置是不合适的,会产生循环引用。最好尝试添加一个新模块,并将生成器插件添加到该模块中。如果您可以将另一个模块配置为outputDir,则可以进行引用。

即使插件位于根项目中,目标也需要是一个模块。

关键是,根项目总是会执行,与模块配置相反。

英文:

You cannot configure it alike this, because build most certainly is an output directory, which would create a circular reference. Better try to add a new module and add that generator plugin into that module. If you can configure another module as outputDir, this could be referenced.

Even if the plugin resides in the root project, the destination needs to be a module.

The point is, that the root project always executes, opposite to module configutions.

答案2

得分: 1

以下是翻译的内容:

你正在试图将 build/ 目录制作成一个项目,然而该目录并不特意用来作为项目目录。这是 Gradle 的默认 build 目录,很可能其他插件和 Gradle 插件的99% 也是如此。

只需将 output 目录更改为除了 build/ 以外的其他目录:

openApiGenerate {
    generatorName.set("java")
    inputSpec.set("$rootDir/specs/petstore.json")
    outputDir.set("$rootDir/openapi-java-client")
    apiPackage.set("org.openapi.example.api")
    invokerPackage.set("org.openapi.example.invoker")
    modelPackage.set("org.openapi.example.model")
}

然后使用正确的语法将项目包含在你的构建中:

// settings.gradle
include("openapi-java-client")

然而,使用 org.openapi.generator 似乎会生成一个无效的 build.gradle,因为我得到了以下错误:

FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle' line: 23
* What went wrong:
Could not compile build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle'.
> startup failed:
build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle': 23: unexpected char: '\' @ line 23, column 35.
main.java.srcDirs = ['src/main\java']

由于这显然不会按照你的预期工作,因为它似乎是 Gradle 插件本身的问题。如果你只需要将生成的代码包含在项目中,那么只需将生成的 Java 代码作为你主要的 Java 源代码的一部分包含进来:

openApiGenerate {
    generatorName.set("java")
    inputSpec.set("$rootDir/specs/petstore.json")
    outputDir.set("$buildDir/openapi-java-client")
    apiPackage.set("org.openapi.example.api")
    invokerPackage.set("org.openapi.example.invoker")
    modelPackage.set("org.openapi.example.model")
}

tasks {
    compileJava {
        dependsOn(openApiGenerate)
    }
}

sourceSets {
    main {
        java {
            srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
        }
    }
}

但是使用这种方法,你可能会遇到一些缺少的导入/依赖。看起来这个插件并没有提供只生成模型/POJO 的能力,因此通过library 属性更新为 native,并手动包含一些缺少的依赖,就可以使其工作:

plugins {
    java
    id("org.openapi.generator") version "5.0.0-beta"
}

repositories {
    mavenCentral()
}

group = "io.mateo.test"

dependencies {
    implementation(platform("com.fasterxml.jackson:jackson-bom:2.11.1"))
    implementation("com.fasterxml.jackson.core:jackson-databind")
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
    implementation("org.openapitools:jackson-databind-nullable:0.2.1")
    implementation("com.google.code.findbugs:jsr305:3.0.2")
    implementation("io.swagger:swagger-core:1.6.2")
}

openApiGenerate {
    generatorName.set("java")
    inputSpec.set("$rootDir/specs/petstore.json")
    outputDir.set("$buildDir/openapi-java-client")
    apiPackage.set("org.openapi.example.api")
    invokerPackage.set("org.openapi.example.invoker")
    modelPackage.set("org.openapi.example.model")
    library.set("native")
    configOptions.put("dateLibrary", "java8")
}

tasks {
    compileJava {
        dependsOn(openApiGenerate)
    }
}

sourceSets {
    main {
        java {
            srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
        }
    }
}
英文:

You are trying to make build/ a project when that directory specifically is not meant to be a project directory. It's Gradle default build directory and likely 99% of other plugins and other Gradle plugins.

Simply change output directory to something else other than build/:

openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$rootDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}

Then include the project in your build with the correct syntax:

// settings.gradle
include("openapi-java-client")

However, using the org.openapi.generator seems to generate an invalid build.gradle since I get the following error:

FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle' line: 23
* What went wrong:
Could not compile build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle'.
> startup failed:
build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle': 23: unexpected char: '\' @ line 23, column 35.
main.java.srcDirs = ['src/main\java']

This obviously won't work how you wanted it to since it appears to be an issue with the Gradle plugin itself. If you just need to include the generate code in your project, then just include the generated Java code as part of your main Java source:

openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}

But with this approach, you'll run into missing imports/dependencies. It doesn't appear this plugin offers the ability to just generate the models/POJOs only, so updating the library property to native and including some missing dependencies manually, it all works:

plugins {
java
id("org.openapi.generator") version "5.0.0-beta"
}
repositories {
mavenCentral()
}
group = "io.mateo.test"
dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.11.1"))
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("org.openapitools:jackson-databind-nullable:0.2.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("io.swagger:swagger-core:1.6.2")
}
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
library.set("native")
configOptions.put("dateLibrary", "java8")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}

答案3

得分: 0

我刚刚回答了一个非常类似的问题。虽然我在那里的回答并不完美,但就我个人而言,我仍然更喜欢在那里提到的方法,也在这里重复提到:

建议的方法

我建议将依赖于生成的 API 的模块的_build_与生成 API 的_build_ 完全分开。这些构建之间唯一的连接应该是一个依赖声明。这意味着,您将不得不手动确保首先构建生成 API 的项目,然后再构建依赖的项目。

默认情况下,这意味着在构建依赖项目之前也必须发布 API 模块。这个默认情况的替代方案是Gradle 组合构建 - 例如,允许您在发布之前首先在本地测试新生成的 API。然而,在创建/运行组合构建之前,您必须在每次 OpenAPI 文档更改时手动运行生成 API 的构建。

示例

假设项目 A 依赖于生成的 API。它的 Gradle 构建将包含类似于以下内容:

dependencies {
    implementation 'com.example:api:1.0'
}

当然,问题中描述的simple-java-app 构建必须进行调整,以生成具有这些坐标的模块:

openApiGenerate {
    // …
    groupId = "com.example"
    id = "api"
    version = "1.0"
}

在运行 A 的构建之前,您首先必须运行:

  1. 从您的 simple-java-app 项目中运行 ./gradlew openApiGenerate
  2. simple-java-app/build/openapi-java-client/ 目录中运行 ./gradlew publish

然后 A 的构建可以从发布仓库中获取已发布的依赖项。

或者,您可以在本地跳过步骤 2,并使用额外的 Gradle CLI 选项运行 A 的构建:

./gradlew --include-build $path_to/simple-java-app/build/openapi-java-client/ …
英文:

I’ve just answered a very similar question. While my answer there is not perfect, I would personally still prefer the approach suggested there – and kind of repeated here:

Suggested Approach

I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.

By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would be Gradle composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.

Example

Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:

dependencies {
    implementation 'com.example:api:1.0'
}

Of course, the simple-java-app build described in the question would have to be adapted to produce a module with these coordinates:

openApiGenerate {
    // …
    groupId = "com.example"
    id = "api"
    version = "1.0"
}

Before running A’s build, you’d first have to run

  1. ./gradlew openApiGenerate from your simple-java-app project.
  2. ./gradlew publish from the simple-java-app/build/openapi-java-client/ directory.

Then A’s build could fetch the published dependency from the publishing repository.

Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:

./gradlew --include-build $path_to/simple-java-app/build/openapi-java-client/ …

huangapple
  • 本文由 发表于 2020年7月27日 06:26:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/63106391.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定