Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate client code from OpenAPI specification #366

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
4f3946f
OpenAPI generator from scratch
Xtansia Feb 12, 2023
f070ed1
Write to files and generate object shapes
Xtansia Feb 14, 2023
639921b
Output response shapes and enum shapes
Xtansia Feb 14, 2023
f2329a7
Fix builder setters for enums
Xtansia Feb 14, 2023
02dec04
Better handling of required fields
Xtansia Feb 14, 2023
e2c9c5b
Unify templates for ObjectShape/OperationRequest
Xtansia Feb 14, 2023
c9d3df1
Simple http path expression
Xtansia Feb 15, 2023
de863c1
Generate client class
Xtansia Feb 16, 2023
d0ef49e
Cleanup
Xtansia Feb 16, 2023
44c2f24
Generate namespaces remote store client
Xtansia Feb 16, 2023
29d6e6a
Add license header, improve http path builder, fix remote store spec
Xtansia Feb 17, 2023
13489b0
Suppress checkstyle unused imports lint for generated code
Xtansia Feb 17, 2023
d02b957
Centralize some naming
Xtansia Feb 19, 2023
c5a93f5
Allow renaming operations
Xtansia Feb 23, 2023
ec58087
Add generated warning header
Xtansia Feb 28, 2023
055a0fc
Refactor
Xtansia Mar 3, 2023
f92b8eb
Less code more templates
Xtansia Mar 3, 2023
82abb0d
Simplify http path part
Xtansia Jan 25, 2024
2956106
Fixes
Xtansia Jan 30, 2024
6af4a46
Swap to swagger-parser
Xtansia Feb 1, 2024
4d45fda
Handle x-data-type & array response bodies
Xtansia Feb 1, 2024
ce8e448
Handle multiple http paths for same operation
Xtansia Feb 8, 2024
dbd60fb
Improve type handling
Xtansia Feb 12, 2024
2e8060c
Tweak package name determination
Xtansia Feb 13, 2024
68411bf
Handle additionalProperties on ErrorCause
Xtansia Feb 13, 2024
2cb2061
Simplify enum shape
Xtansia Feb 13, 2024
c7d0986
Change default number type
Xtansia Feb 14, 2024
1c39a99
Generate parameter-less operation method when no required fields
Xtansia Feb 16, 2024
c7a00e2
Refactor to parse multi-file spec
Xtansia Feb 20, 2024
716b174
spotlessApply
Xtansia Feb 21, 2024
8acc8dc
Use spotless to format code
Xtansia Feb 21, 2024
d868691
Maintain spec location information
Xtansia Feb 22, 2024
7e07895
Refactor model transformation and type mapper
Xtansia Feb 23, 2024
b80b79b
Match existing naming scheme
Xtansia Feb 27, 2024
ee59e9d
Change OperationGroup matching logic to determine which to generate
Xtansia Feb 28, 2024
f998568
Better handle errors thrown by formatter
Xtansia Feb 28, 2024
d9f61d3
Add typedef comment
Xtansia Feb 28, 2024
fc4e4c2
Support tagged union
Xtansia Feb 28, 2024
0cd6b53
Add additional reserved keywords
Xtansia Feb 28, 2024
a0bceae
Improve parsing logic
Xtansia May 16, 2024
619a507
Add basic tests and implement proper argument parsing
Xtansia May 29, 2024
c905c62
Improve tagged union generation and filter global query params
Xtansia Jun 4, 2024
3714fa7
Change enum naming style
Xtansia Jun 6, 2024
1cb8389
Handle Time type
Xtansia Jun 6, 2024
878f78d
Handle deprecation
Xtansia Jun 6, 2024
779b09f
Clear operations to generate
Xtansia Jun 6, 2024
4d54e70
Change usages of `<stream>.toList()` to `<stream>.collect(Collectors.…
Xtansia Jun 6, 2024
ab4bec2
Remove publishing from build.gradle
Xtansia Jun 9, 2024
996a379
Cleaning up
Xtansia Jun 10, 2024
5120a6b
spotless
Xtansia Jun 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ gradle-app.setting

.ci/output
java-client/bin
samples/bin
samples/bin

.DS_Store
8 changes: 8 additions & 0 deletions java-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ java {
}
}

sourceSets {
main {
java {
srcDir("src/generated/java")
}
}
}

tasks.withType<ProcessResources> {
expand(
"version" to version,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -134,6 +135,7 @@ public static <T> boolean isDefined(List<T> list) {
/**
* Returns an unmodifiable view of a list. If {@code list} is {@code null}, an {@link #undefinedList()} is returned.
*/
@Nonnull
public static <T> List<T> unmodifiable(@Nullable List<T> list) {
if (list == null) {
return undefinedList();
Expand Down
262 changes: 262 additions & 0 deletions java-codegen/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import com.github.jk1.license.ProjectData
import com.github.jk1.license.render.ReportRenderer
import java.io.FileWriter

buildscript {
repositories {
mavenLocal()
maven(url = "https://aws.oss.sonatype.org/content/repositories/snapshots")
mavenCentral()
maven(url = "https://plugins.gradle.org/m2/")
}
dependencies {
"classpath"(group = "org.opensearch.gradle", name = "build-tools", version = "3.0.0-SNAPSHOT")
}
}

plugins {
application
id("com.github.jk1.dependency-license-report") version "2.8"
id("org.owasp.dependencycheck") version "9.2.0"
id("com.diffplug.spotless") version "6.25.0"
}
apply(plugin = "opensearch.repositories")
apply(plugin = "org.owasp.dependencycheck")

val runtimeJavaVersion = (System.getProperty("runtime.java")?.toInt())?.let(JavaVersion::toVersion) ?: JavaVersion.current()
logger.quiet("=======================================")
logger.quiet(" Runtime JDK Version : $runtimeJavaVersion")
logger.quiet(" Gradle JDK Version : " + JavaVersion.current())
logger.quiet("=======================================")

java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11

withJavadocJar()
withSourcesJar()

toolchain {
languageVersion = JavaLanguageVersion.of(runtimeJavaVersion.majorVersion)
vendor = JvmVendorSpec.ADOPTIUM
}
}

application {
mainClass.set("org.opensearch.client.codegen.Main")
applicationDefaultJvmArgs = listOf(
"--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
)
}

tasks.named<JavaExec>("run") {
args = listOf(
"--input", "$projectDir/opensearch-openapi.yaml",
"--eclipse-config", "$rootDir/buildSrc/formatterConfig.xml",
"--output", "${project(":java-client").projectDir}/src/generated/java/"
)
}

tasks.withType<ProcessResources> {
expand(
"version" to version,
"git_revision" to (if (rootProject.extra.has("gitHashFull")) rootProject.extra["gitHashFull"] else "unknown")
)
}

tasks.withType<Javadoc>().configureEach{
options {
encoding = "UTF-8"
}
}

tasks.withType<Jar> {
doFirst {
if (rootProject.extra.has("gitHashFull")) {
val jar = this as Jar
jar.manifest.attributes["X-Git-Revision"] = rootProject.extra["gitHashFull"]
jar.manifest.attributes["X-Git-Commit-Time"] = rootProject .extra["gitCommitTime"]
} else {
throw GradleException("No git information available")
}
}

manifest {
attributes["Implementation-Title"] = "OpenSearch Java client code generator"
attributes["Implementation-Vendor"] = "OpenSearch"
attributes["Implementation-URL"] = "https://github.com/opensearch-project/opensearch-java/"
attributes["Build-Date"] = rootProject.extra["buildTime"]
}

metaInf {
from("../LICENSE.txt")
from("../NOTICE.txt")
}
}

tasks.withType<Test> {
useJUnitPlatform()
testLogging {
events("passed", "skipped", "failed")
}
}

tasks.build {
dependsOn("spotlessJavaCheck")
}

dependencies {
// Apache 2.0
implementation("io.swagger.parser.v3", "swagger-parser", "2.1.22")

// (New) BSD
implementation("com.samskivert", "jmustache", "1.16")

// Apache 2.0
implementation("commons-cli", "commons-cli", "1.8.0")
implementation("commons-logging", "commons-logging", "1.3.2")
implementation("org.apache.commons", "commons-lang3", "3.14.0")
implementation("org.apache.commons", "commons-text", "1.12.0")
implementation("org.apache.logging.log4j", "log4j-api", "[2.17.1,3.0)")
implementation("org.apache.logging.log4j", "log4j-core", "[2.17.1,3.0)")
implementation("org.apache.logging.log4j", "log4j-slf4j2-impl", "[2.17.1,3.0)")

// Apache 2.0
implementation("com.fasterxml.jackson.core", "jackson-core", "2.17.1")
implementation("com.fasterxml.jackson.core", "jackson-databind", "2.17.1")

// Apache 2.0
implementation("com.diffplug.spotless", "spotless-lib", "2.45.0")
implementation("com.diffplug.spotless", "spotless-lib-extra", "2.45.0")

// Apache 2.0
// https://search.maven.org/artifact/com.google.code.findbugs/jsr305
implementation("com.google.code.findbugs", "jsr305", "3.0.2")

// Apache 2.0
compileOnly("org.jetbrains", "annotations", "24.1.0")

// Apache 2.0
implementation("org.apache.maven.resolver", "maven-resolver-api", "1.9.20")
implementation("org.apache.maven.resolver", "maven-resolver-supplier", "1.9.20")

// EPL-2.0
testImplementation(platform("org.junit:junit-bom:5.10.2"))
testImplementation("org.junit.jupiter", "junit-jupiter")
testRuntimeOnly("org.junit.platform", "junit-platform-launcher")
}

licenseReport {
renderers = arrayOf(SpdxReporter(rootProject.layout.buildDirectory.file("release/codegen-dependencies.csv").get().getAsFile()))
excludeGroups = arrayOf()
}

class SpdxReporter(val dest: File) : ReportRenderer {
// License names to their SPDX identifier
val spdxIds = mapOf(
"Apache 2" to "Apache-2.0",
"Apache 2.0" to "Apache-2.0",
"Apache-2.0" to "Apache-2.0",
"\"Apache-2.0\";link=\"https://www.apache.org/licenses/LICENSE-2.0.txt\"" to "Apache-2.0",
"\"Apache 2.0\";link=\"http://www.apache.org/licenses/LICENSE-2.0.txt\"" to "Apache-2.0",
"\"Apache License 2.0\";link=\"http://www.apache.org/licenses/LICENSE-2.0.html\"" to "Apache-2.0",
"Apache License 2.0" to "Apache-2.0",
"Apache License, version 2.0" to "Apache-2.0",
"Apache License, Version 2.0" to "Apache-2.0",
"Apache Software License, version 2.0" to "Apache-2.0",
"The Apache License, Version 2.0" to "Apache-2.0",
"The Apache Software License, Version 2.0" to "Apache-2.0",
"BSD Zero Clause License" to "0BSD",
"The (New) BSD License" to "BSD-3-Clause",
"EDL 1.0" to "BSD-3-Clause",
"Eclipse Distribution License - v 1.0" to "BSD-3-Clause",
"Eclipse Distribution License (New BSD License)" to "BSD-3-Clause",
"Eclipse Public License 2.0" to "EPL-2.0",
"Eclipse Public License v. 2.0" to "EPL-2.0",
"Eclipse Public License - v 2.0" to "EPL-2.0",
"GNU General Public License, version 2 with the GNU Classpath Exception" to "GPL-2.0 WITH Classpath-exception-2.0",
"COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0" to "CDDL-1.0",
"Lesser General Public License, version 3 or greater" to "LGPL-3.0+",
"MIT" to "MIT",
"MIT License" to "MIT",
"The MIT License" to "MIT",
"Mozilla Public License, Version 2.0" to "MPL-2.0",
"Public Domain" to "PUBLIC-DOMAIN"
)

private fun quote(str: String) : String {
return if (str.contains(',') || str.contains("\"")) {
"\"" + str.replace("\"", "\"\"") + "\""
} else {
str
}
}

override fun render(data: ProjectData?) {
dest.parentFile.mkdirs()
FileWriter(dest).use { out ->
out.append("name,url,version,revision,license\n")
data?.allDependencies?.forEach { dep ->
val info = com.github.jk1.license.render.LicenseDataCollector.multiModuleLicenseInfo(dep)

val depVersion = dep.version
val depName = dep.group + ":" + dep.name
val depUrl = if (info.moduleUrls.isNotEmpty()) { info.moduleUrls.first() } else { "<NO MODULE URL>" }

val licenseIds = info.licenses.mapNotNull { license ->
license.name?.let {
checkNotNull(spdxIds[it]) { "No SPDX identifier for $license" }
}
}.toSet()

// Combine multiple licenses.
// See https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/#composite-license-expressions
val licenseId = licenseIds.joinToString(" OR ")
out.append("${quote(depName)},${quote(depUrl)},${quote(depVersion)},,${quote(licenseId)}\n")
}
}
}
}

tasks.withType<Jar> {
doLast {
ant.withGroovyBuilder {
"checksum"("algorithm" to "md5", "file" to archiveFile.get())
"checksum"("algorithm" to "sha1", "file" to archiveFile.get())
"checksum"("algorithm" to "sha-256", "file" to archiveFile.get(), "fileext" to ".sha256")
"checksum"("algorithm" to "sha-512", "file" to archiveFile.get(), "fileext" to ".sha512")
}
}
}

spotless {
java {
target("**/*.java")

// Use the default importOrder configuration
importOrder()
removeUnusedImports()

eclipse().configFile("../buildSrc/formatterConfig.xml")

trimTrailingWhitespace()
endWithNewline()
}
}
Loading
Loading