Skip to content

Commit

Permalink
Generate client code from OpenAPI specification (#366)
Browse files Browse the repository at this point in the history
* OpenAPI generator from scratch

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Write to files and generate object shapes

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Output response shapes and enum shapes

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Fix builder setters for enums

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Better handling of required fields

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Unify templates for ObjectShape/OperationRequest

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Simple http path expression

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Generate client class

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Cleanup

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Generate namespaces remote store client

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Add license header, improve http path builder, fix remote store spec

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Suppress checkstyle unused imports lint for generated code

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Centralize some naming

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Allow renaming operations

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Add generated warning header

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Refactor

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Less code more templates

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Simplify http path part

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Fixes

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Swap to swagger-parser

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Handle x-data-type & array response bodies

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Handle multiple http paths for same operation

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Improve type handling

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Tweak package name determination

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Handle additionalProperties on ErrorCause

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Simplify enum shape

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Change default number type

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Generate parameter-less operation method when no required fields

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Refactor to parse multi-file spec

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* spotlessApply

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Use spotless to format code

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Maintain spec location information

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Refactor model transformation and type mapper

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Match existing naming scheme

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Change OperationGroup matching logic to determine which to generate

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Better handle errors thrown by formatter

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Add typedef comment

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Support tagged union

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Add additional reserved keywords

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Improve parsing logic

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Add basic tests and implement proper argument parsing

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Improve tagged union generation and filter global query params

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Change enum naming style

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Handle Time type

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Handle deprecation

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Clear operations to generate

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Change usages of `<stream>.toList()` to `<stream>.collect(Collectors.toList())`

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Remove publishing from build.gradle

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* Cleaning up

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

* spotless

Signed-off-by: Thomas Farr <tsfarr@amazon.com>

---------

Signed-off-by: Thomas Farr <tsfarr@amazon.com>
  • Loading branch information
Xtansia committed Jun 10, 2024
1 parent 4a459f7 commit bb995ec
Show file tree
Hide file tree
Showing 98 changed files with 6,049 additions and 1 deletion.
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

0 comments on commit bb995ec

Please sign in to comment.