diff --git a/bin/kotlin-client-petstore.sh b/bin/kotlin-client-petstore.sh new file mode 100755 index 00000000000..fa017f1e8ef --- /dev/null +++ b/bin/kotlin-client-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +while [ -h "$SCRIPT" ] ; do + ls=$(ls -ld "$SCRIPT") + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=$(dirname "$SCRIPT")/"$link" + fi +done + +if [ ! -d "${APP_DIR}" ]; then + APP_DIR=$(dirname "$SCRIPT")/.. + APP_DIR=$(cd "${APP_DIR}"; pwd) +fi + +executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn clean package +fi + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="$@ generate -t modules/swagger-codegen/src/main/resources/kotlin-client -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l kotlin -o samples/client/kotlin" + +java ${JAVA_OPTS} -jar ${executable} ${ags} diff --git a/bin/windows/kotlin-client-petstore.bat b/bin/windows/kotlin-client-petstore.bat new file mode 100644 index 00000000000..e9d8efffdaf --- /dev/null +++ b/bin/windows/kotlin-client-petstore.bat @@ -0,0 +1,10 @@ +set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar + +If Not Exist %executable% ( + mvn clean package +) + +REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties +set ags=generate --artifact-id "kotlin-petstore-client" -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l kotlin -o samples\client\petstore\kotlin + +java %JAVA_OPTS% -jar %executable% %ags% diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java new file mode 100644 index 00000000000..aa1a0039ded --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/KotlinClientCodegen.java @@ -0,0 +1,362 @@ +package io.swagger.codegen.languages; + +import io.swagger.codegen.*; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + +public class KotlinClientCodegen extends DefaultCodegen implements CodegenConfig { + static Logger LOGGER = LoggerFactory.getLogger(KotlinClientCodegen.class); + + protected String groupId = "io.swagger"; + protected String artifactId = "kotlin-client"; + protected String artifactVersion = "1.0.0"; + protected String sourceFolder = "src/main/kotlin"; + protected String packageName = "io.swagger.client"; + + /** + * Constructs an instance of `KotlinClientCodegen`. + */ + public KotlinClientCodegen() { + super(); + + outputFolder = "generated-code" + File.separator + "kotlin-client"; + modelTemplateFiles.put("model.mustache", ".kt"); + apiTemplateFiles.put("api.mustache", ".kt"); + // TODO: Documentation generation + embeddedTemplateDir = templateDir = "kotlin-client"; + apiPackage = packageName + ".apis"; + modelPackage = packageName + ".models"; + + languageSpecificPrimitives = new HashSet(Arrays.asList( + "kotlin.Byte", + "kotlin.Short", + "kotlin.Int", + "kotlin.Long", + "kotlin.Float", + "kotlin.Double", + "kotlin.Boolean", + "kotlin.Char", + "kotlin.String", + "kotlin.Array", + "kotlin.collections.List", + "kotlin.collections.Map", + "kotlin.collections.Set" + )); + + // this includes hard reserved words defined by https://github.com/JetBrains/kotlin/blob/master/core/descriptors/src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java + // as well as select soft (contextual) keywords + reservedWords = new HashSet(Arrays.asList( + "abstract", + "as", + "break", + "case", + "catch", + "class", + "continue", + "do", + "else", + "false", + "final", + "finally", + "for", + "fun", + "if", + "in", + "interface", + "is", + "it", + "lazy", + "null", + "object", + "override", + "package", + "private", + "protected", + "public", + "return", + "sealed", + "super", + "this", + "throw", + "true", + "try", + "typealias", + "typeof", + "val", + "var", + "when", + "while" + )); + + defaultIncludes = new HashSet(Arrays.asList( + "kotlin.Byte", + "kotlin.Short", + "kotlin.Int", + "kotlin.Long", + "kotlin.Float", + "kotlin.Double", + "kotlin.Boolean", + "kotlin.Char", + "kotlin.Array", + "kotlin.collections.List", + "kotlin.collections.Set", + "kotlin.collections.Map" + )); + + typeMapping = new HashMap(); + typeMapping.put("string", "kotlin.String"); + typeMapping.put("boolean", "kotlin.Boolean"); + typeMapping.put("integer", "kotlin.Int"); + typeMapping.put("float", "kotlin.Float"); + typeMapping.put("long", "kotlin.Long"); + typeMapping.put("double", "kotlin.Double"); + typeMapping.put("number", "java.math.BigDecimal"); + typeMapping.put("date-time", "java.time.LocalDateTime"); + typeMapping.put("date", "java.time.LocalDateTime"); + typeMapping.put("file", "java.io.File"); + typeMapping.put("array", "kotlin.Array"); + typeMapping.put("list", "kotlin.collections.List"); + typeMapping.put("map", "kotlin.collections.Map"); + typeMapping.put("object", "kotlin.Any"); + typeMapping.put("binary", "kotlin.Array"); + typeMapping.put("Date", "java.time.LocalDateTime"); + typeMapping.put("DateTime", "java.time.LocalDateTime"); + + instantiationTypes.put("array", "arrayOf"); + instantiationTypes.put("list", "listOf"); + instantiationTypes.put("map", "mapOf"); + + importMapping = new HashMap(); + importMapping.put("BigDecimal", "java.math.BigDecimal"); + importMapping.put("UUID", "java.util.UUID"); + importMapping.put("File", "java.io.File"); + importMapping.put("Date", "java.util.Date"); + importMapping.put("Timestamp", "java.sql.Timestamp"); + importMapping.put("DateTime", "java.time.LocalDateTime"); + importMapping.put("LocalDateTime", "java.time.LocalDateTime"); + importMapping.put("LocalDate", "java.time.LocalDate"); + importMapping.put("LocalTime", "java.time.LocalTime"); + + cliOptions.clear(); + cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Client package name (e.g. io.swagger).").defaultValue(this.packageName)); + cliOptions.add(new CliOption(CodegenConstants.GROUP_ID, "Client package's organization (i.e. maven groupId).").defaultValue(groupId)); + cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_ID, "Client artifact id (name of generated jar).").defaultValue(artifactId)); + cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_VERSION, "Client package version.").defaultValue(artifactVersion)); + } + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "kotlin"; + } + + public String getHelp() { + return "Generates a kotlin client."; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setArtifactVersion(String artifactVersion) { + this.artifactVersion = artifactVersion; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public void setSourceFolder(String sourceFolder) { + this.sourceFolder = sourceFolder; + } + + @Override + public void processOpts() { + super.processOpts(); + + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + this.setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME)); + } else { + additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); + } + + if(additionalProperties.containsKey(CodegenConstants.ARTIFACT_ID)) { + this.setArtifactId((String) additionalProperties.get(CodegenConstants.ARTIFACT_ID)); + } else { + additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); + } + + if(additionalProperties.containsKey(CodegenConstants.GROUP_ID)) { + this.setGroupId((String) additionalProperties.get(CodegenConstants.GROUP_ID)); + } else { + additionalProperties.put(CodegenConstants.GROUP_ID, groupId); + } + + if(additionalProperties.containsKey(CodegenConstants.ARTIFACT_VERSION)) { + this.setArtifactVersion((String) additionalProperties.get(CodegenConstants.ARTIFACT_VERSION)); + } else { + additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); + } + + if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { + LOGGER.warn(CodegenConstants.INVOKER_PACKAGE + " with " + this.getName() + " generator is ignored. Use " + CodegenConstants.PACKAGE_NAME + "."); + } + + if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) { + LOGGER.warn(CodegenConstants.MODEL_PACKAGE + " with " + this.getName() + " generator is ignored. Setting this value independently of " + CodegenConstants.PACKAGE_NAME + " is not currently supported."); + } + + if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) { + LOGGER.warn(CodegenConstants.API_PACKAGE + " with " + this.getName() + " generator is ignored. Setting this value independently of " + CodegenConstants.PACKAGE_NAME + " is not currently supported."); + } + + additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); + + supportingFiles.add(new SupportingFile("README.md", "", "README.md")); + + supportingFiles.add(new SupportingFile("build.gradle.mustache", "", "build.gradle")); + supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle")); + + final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator + "infrastructure").replace(".", "/"); + + supportingFiles.add(new SupportingFile("infrastructure/ApiClient.kt.mustache", infrastructureFolder, "ApiClient.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ApiInfrastructureResponse.kt.mustache", infrastructureFolder, "ApiInfrastructureResponse.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ApplicationDelegates.kt.mustache", infrastructureFolder, "ApplicationDelegates.kt")); + supportingFiles.add(new SupportingFile("infrastructure/RequestConfig.kt.mustache", infrastructureFolder, "RequestConfig.kt")); + supportingFiles.add(new SupportingFile("infrastructure/RequestMethod.kt.mustache", infrastructureFolder, "RequestMethod.kt")); + supportingFiles.add(new SupportingFile("infrastructure/ResponseExtensions.kt.mustache", infrastructureFolder, "ResponseExtensions.kt")); + supportingFiles.add(new SupportingFile("infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt")); + supportingFiles.add(new SupportingFile("infrastructure/Errors.kt.mustache", infrastructureFolder, "Errors.kt")); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar); + } + + @Override + public String modelFileFolder() { + return outputFolder + File.separator + sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar); + } + + @Override + public String escapeReservedWord(String name) { + return "_" + name; + } + + /** + * Output the proper model name (capitalized). + * In case the name belongs to the TypeSystem it won't be renamed. + * + * @param name the name of the model + * @return capitalized model name + */ + @Override + public String toModelName(String name) { + if(!name.startsWith("kotlin.") && !name.startsWith("java.")) { + return initialCaps(modelNamePrefix + name + modelNameSuffix); + } else { + return name; + } + } + + /** + * returns the swagger type for the property + * + * @param p Swagger property object + * @return string presentation of the type + **/ + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type; + // This maps, for example, long -> kotlin.Long based on hashes in this type's constructor + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) { + return toModelName(type); + } + } else { + type = swaggerType; + } + return toModelName(type); + } + + /** + * Output the type declaration of the property + * + * @param p Swagger Property object + * @return a string presentation of the property type + */ + @Override + public String getTypeDeclaration(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return getSwaggerType(p) + "<" + getTypeDeclaration(inner) + ">"; + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + + // Maps will be keyed only by primitive Kotlin string + return getSwaggerType(p) + ""; + } + return super.getTypeDeclaration(p); + } + + /** + * Check the type to see if it needs import the library/module/package + * + * @param type name of the type + * @return true if the library/module/package of the corresponding type needs to be imported + */ + @Override + protected boolean needToImport(String type) { + // provides extra protection against improperly trying to import language primitives and java types + boolean imports = !type.startsWith("kotlin.") && !type.startsWith("java.") && !defaultIncludes.contains(type) && !languageSpecificPrimitives.contains(type); + return imports; + } + + /** + * Return the fully-qualified "Model" name for import + * + * @param name the name of the "Model" + * @return the fully-qualified "Model" name for import + */ + @Override + public String toModelImport(String name) { + // toModelImport is called while processing operations, but DefaultCodegen doesn't + // define imports correctly with fully qualified primitives and models as defined in this generator. + if(needToImport(name)) { + return super.toModelImport(name); + } + + return name; + } +} diff --git a/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig b/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig index e8c706ce79b..74ec8da9070 100644 --- a/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig +++ b/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig @@ -65,4 +65,5 @@ io.swagger.codegen.languages.GoServerCodegen io.swagger.codegen.languages.ErlangServerCodegen io.swagger.codegen.languages.UndertowCodegen io.swagger.codegen.languages.JavaMSF4JServerCodegen -io.swagger.codegen.languages.ZendExpressivePathHandlerServerCodegen \ No newline at end of file +io.swagger.codegen.languages.ZendExpressivePathHandlerServerCodegen +io.swagger.codegen.languages.KotlinClientCodegen diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/README.md b/modules/swagger-codegen/src/main/resources/kotlin-client/README.md new file mode 100644 index 00000000000..885688a0fd6 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/README.md @@ -0,0 +1,29 @@ +# Generated Kotlin Client library + +## Requires + +* Kotlin 1.1.2 +* Gradle 3.3 + +## Build + +First, create the gradle wrapper script: + +``` +gradle wrapper +``` + +Then, run: + +``` +./gradlew check assemble +``` + +This runs all tests and packages the library. + +## Notes + +* Supports JSON inputs/outputs, File inputs, and Form inputs. +* Supports collection formats for query parameters: csv, tsv, ssv, pipes. +* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in Swagger definitions. +* Implementation of ApiClient is intended to reduce method counts, specifically to benefit Android targets. \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache new file mode 100644 index 00000000000..545cd29e691 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/api.mustache @@ -0,0 +1,55 @@ +{{>licenseInfo}} +package {{apiPackage}} + +{{#imports}}import {{import}} +{{/imports}} + +import {{packageName}}.infrastructure.* + +{{#operations}} +class {{classname}}(basePath: kotlin.String = "{{{basePath}}}") : ApiClient(basePath) { + + {{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}}* @param {{paramName}} {{description}} {{^required}}(optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}} + {{/allParams}}* @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} + */{{#returnType}} + @Suppress("UNCHECKED_CAST"){{/returnType}} + fun {{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) : {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}} { + val localVariableBody: kotlin.Any? = {{#hasBodyParam}}{{#bodyParams}}{{paramName}}{{/bodyParams}}{{/hasBodyParam}}{{^hasBodyParam}}{{^hasFormParams}}null{{/hasFormParams}}{{#hasFormParams}}mapOf({{#formParams}}"{{{baseName}}}" to "${{paramName}}"{{#hasMore}}, {{/hasMore}}{{/formParams}}){{/hasFormParams}}{{/hasBodyParam}} + val localVariableQuery: kotlin.collections.Map = {{^hasQueryParams}}mapOf(){{/hasQueryParams}}{{#hasQueryParams}}mapOf({{#queryParams}}"{{paramName}}" to {{#isContainer}}{{paramName}}.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}{{paramName}}{{/isContainer}}{{#hasMore}}, {{/hasMore}}{{/queryParams}}){{/hasQueryParams}} + val localVariableHeaders: kotlin.collections.Map = {{^headerParams}}mapOf({{#hasFormParams}}"Content-Type" to "multipart/form-data"{{/hasFormParams}}){{/headerParams}}{{#headerParams}}mapOf("{{paramName}}" to {{#isContainer}}{{paramName}}.joinToString(separator = collectionDelimiter("{{collectionFormat}}")){{/isContainer}}{{^isContainer}}{{paramName}}{{/isContainer}}){{/headerParams}} + val localVariableConfig = RequestConfig( + RequestMethod.{{httpMethod}}, + "{{path}}"{{#pathParams}}.replace("{"+"{{baseName}}"+"}", "${{paramName}}"){{/pathParams}}, + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Any?{{/returnType}}>( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> {{#returnType}}(response as Success<*>).data as {{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}} + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + {{/operation}} + + private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipes" -> "|" + "ssv" -> " " + else -> "" + } +} +{{/operations}} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/build.gradle.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/build.gradle.mustache new file mode 100644 index 00000000000..b0f0a12133d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/build.gradle.mustache @@ -0,0 +1,32 @@ +group '{{groupId}}' +version '{{artifactVersion}}' + +task wrapper(type: Wrapper) { + gradleVersion = '3.3' + distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" +} + +buildscript { + ext.kotlin_version = '1.1.2' + + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' + +repositories { + mavenCentral() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + compile "com.squareup.moshi:moshi-kotlin:1.5.0" + compile "com.squareup.moshi:moshi-adapters:1.5.0" + compile "com.squareup.okhttp3:okhttp:3.8.0" + testCompile "io.kotlintest:kotlintest:2.0.2" +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache new file mode 100644 index 00000000000..7480a80fce9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiClient.kt.mustache @@ -0,0 +1,123 @@ +package {{packageName}}.infrastructure + +import okhttp3.* +import java.io.File + +open class ApiClient(val baseUrl: String) { + companion object { + protected val ContentType = "Content-Type" + protected val Accept = "Accept" + protected val JsonMediaType = "application/json" + protected val FormDataMediaType = "multipart/form-data" + protected val XmlMediaType = "application/xml" + + @JvmStatic + val client : OkHttpClient = OkHttpClient() + + @JvmStatic + var defaultHeaders: Map by ApplicationDelegates.setOnce(mapOf(ContentType to JsonMediaType, Accept to JsonMediaType)) + + @JvmStatic + val jsonHeaders: Map = mapOf(ContentType to JsonMediaType, Accept to JsonMediaType) + } + + inline protected fun requestBody(content: T, mediaType: String = JsonMediaType): RequestBody { + if(content is File) { + return RequestBody.create( + MediaType.parse(mediaType), content + ) + } else if(mediaType == FormDataMediaType) { + var builder = FormBody.Builder() + // content's type *must* be Map + @Suppress("UNCHECKED_CAST") + (content as Map).forEach { key, value -> + builder = builder.add(key, value) + } + return builder.build() + } else if(mediaType == JsonMediaType) { + return RequestBody.create( + MediaType.parse(mediaType), Serializer.moshi.adapter(T::class.java).toJson(content) + ) + } else if (mediaType == XmlMediaType) { + TODO("xml not currently supported.") + } + + // TODO: this should be extended with other serializers + TODO("requestBody currently only supports JSON body and File body.") + } + + inline protected fun responseBody(body: ResponseBody?, mediaType: String = JsonMediaType): T? { + if(body == null) return null + return when(mediaType) { + JsonMediaType -> Serializer.moshi.adapter(T::class.java).fromJson(body.source()) + else -> TODO() + } + } + + inline protected fun request(requestConfig: RequestConfig, body : Any? = null): ApiInfrastructureResponse { + val httpUrl = HttpUrl.parse(baseUrl) ?: throw IllegalStateException("baseUrl is invalid.") + + var urlBuilder = httpUrl.newBuilder() + .addPathSegments(requestConfig.path.trimStart('/')) + + requestConfig.query.forEach { k, v -> urlBuilder = urlBuilder.addQueryParameter(k,v) } + + val url = urlBuilder.build() + val headers = requestConfig.headers + defaultHeaders + + if(headers[ContentType] ?: "" == "") { + throw kotlin.IllegalStateException("Missing Content-Type header. This is required.") + } + + if(headers[Accept] ?: "" == "") { + throw kotlin.IllegalStateException("Missing Accept header. This is required.") + } + + val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase() + val accept = (headers[Accept] as String).substringBefore(";").toLowerCase() + + var request : Request.Builder = when (requestConfig.method) { + RequestMethod.DELETE -> Request.Builder().url(url).delete() + RequestMethod.GET -> Request.Builder().url(url) + RequestMethod.HEAD -> Request.Builder().url(url).head() + RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(body!!, contentType)) + RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(body!!, contentType)) + RequestMethod.POST -> Request.Builder().url(url).post(requestBody(body!!, contentType)) + RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null) + } + + headers.forEach { header -> request = request.addHeader(header.key, header.value) } + + val realRequest = request.build() + val response = client.newCall(realRequest).execute() + + // TODO: handle specific mapping types. e.g. Map> + when { + response.isRedirect -> return Redirection( + response.code(), + response.headers().toMultimap() + ) + response.isInformational -> return Informational( + response.message(), + response.code(), + response.headers().toMultimap() + ) + response.isSuccessful -> return Success( + responseBody(response.body(), accept), + response.code(), + response.headers().toMultimap() + ) + response.isClientError -> return ClientError( + response.body()?.string(), + response.code(), + response.headers().toMultimap() + ) + else -> return ServerError( + null, + response.body()?.string(), + response.code(), + response.headers().toMultimap() + ) + } + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiInfrastructureResponse.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiInfrastructureResponse.kt.mustache new file mode 100644 index 00000000000..a4e0f0f424d --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApiInfrastructureResponse.kt.mustache @@ -0,0 +1,40 @@ +package {{packageName}}.infrastructure + +enum class ResponseType { + Success, Informational, Redirection, ClientError, ServerError +} + +abstract class ApiInfrastructureResponse(val responseType: ResponseType) { + abstract val statusCode: Int + abstract val headers: Map> +} + +class Success( + val data: T, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +): ApiInfrastructureResponse(ResponseType.Success) + +class Informational( + val statusText: String, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiInfrastructureResponse(ResponseType.Informational) + +class Redirection( + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiInfrastructureResponse(ResponseType.Redirection) + +class ClientError( + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiInfrastructureResponse(ResponseType.ClientError) + +class ServerError( + val message: String? = null, + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> +): ApiInfrastructureResponse(ResponseType.ServerError) \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApplicationDelegates.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApplicationDelegates.kt.mustache new file mode 100644 index 00000000000..bc4e1cd6a1b --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ApplicationDelegates.kt.mustache @@ -0,0 +1,29 @@ +package {{packageName}}.infrastructure + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +object ApplicationDelegates { + /** + * Provides a property delegate, allowing the property to be set once and only once. + * + * If unset (no default value), a get on the property will throw [IllegalStateException]. + */ + fun setOnce(defaultValue: T? = null) : ReadWriteProperty = SetOnce(defaultValue) + + private class SetOnce(defaultValue: T? = null) : ReadWriteProperty { + private var isSet = false + private var value: T? = defaultValue + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return value ?: throw IllegalStateException("${property.name} not initialized") + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = synchronized(this) { + if (!isSet) { + this.value = value + isSet = true + } + } + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/Errors.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/Errors.kt.mustache new file mode 100644 index 00000000000..1f6d106d947 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/Errors.kt.mustache @@ -0,0 +1,42 @@ +@file:Suppress("unused") +package {{packageName}}.infrastructure + +import java.lang.RuntimeException + +open class ClientException : RuntimeException { + + /** + * Constructs an [ClientException] with no detail message. + */ + constructor() : super() + + /** + * Constructs an [ClientException] with the specified detail message. + + * @param message the detail message. + */ + constructor(message: kotlin.String) : super(message) + + companion object { + private const val serialVersionUID: Long = 123L + } +} + +open class ServerException : RuntimeException { + + /** + * Constructs an [ServerException] with no detail message. + */ + constructor() : super() + + /** + * Constructs an [ServerException] with the specified detail message. + + * @param message the detail message. + */ + constructor(message: kotlin.String) : super(message) + + companion object { + private const val serialVersionUID: Long = 456L + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestConfig.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestConfig.kt.mustache new file mode 100644 index 00000000000..913b42b648e --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestConfig.kt.mustache @@ -0,0 +1,13 @@ +package {{packageName}}.infrastructure + +/** + * Defines a config object for a given request. + * NOTE: This object doesn't include 'body' because it + * allows for caching of the constructed object + * for many request definitions. + */ +data class RequestConfig( + val method: RequestMethod, + val path: String, + val headers: Map = mapOf(), + val query: Map = mapOf()) \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestMethod.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestMethod.kt.mustache new file mode 100644 index 00000000000..5774235985c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/RequestMethod.kt.mustache @@ -0,0 +1,8 @@ +package {{packageName}}.infrastructure + +/** + * Provides enumerated HTTP verbs + */ +enum class RequestMethod { + GET, DELETE, HEAD, OPTIONS, PATCH, POST, PUT +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ResponseExtensions.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ResponseExtensions.kt.mustache new file mode 100644 index 00000000000..2e2a3478ffa --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/ResponseExtensions.kt.mustache @@ -0,0 +1,23 @@ +package {{packageName}}.infrastructure + +import okhttp3.Response + +/** + * Provides an extension to evaluation whether the response is a 1xx code + */ +val Response.isInformational : Boolean get() = this.code() in 100..199 + +/** + * Provides an extension to evaluation whether the response is a 3xx code + */ +val Response.isRedirect : Boolean get() = this.code() in 300..399 + +/** + * Provides an extension to evaluation whether the response is a 4xx code + */ +val Response.isClientError : Boolean get() = this.code() in 400..499 + +/** + * Provides an extension to evaluation whether the response is a 5xx (Standard) through 999 (non-standard) code + */ +val Response.isServerError : Boolean get() = this.code() in 500..999 \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/Serializer.kt.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/Serializer.kt.mustache new file mode 100644 index 00000000000..71cb991ddba --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/infrastructure/Serializer.kt.mustache @@ -0,0 +1,14 @@ +package {{packageName}}.infrastructure + +import com.squareup.moshi.KotlinJsonAdapterFactory +import com.squareup.moshi.Moshi +import com.squareup.moshi.Rfc3339DateJsonAdapter +import java.util.* + +object Serializer { + @JvmStatic + val moshi: Moshi = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .add(Date::class.java, Rfc3339DateJsonAdapter().nullSafe()) + .build() +} diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/licenseInfo.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/licenseInfo.mustache new file mode 100644 index 00000000000..aee680977df --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/licenseInfo.mustache @@ -0,0 +1,11 @@ +/** +* {{{appName}}} +* {{{appDescription}}} +* +* {{#version}}OpenAPI spec version: {{{version}}}{{/version}} +* {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/model.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/model.mustache new file mode 100644 index 00000000000..442753f7ef2 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/model.mustache @@ -0,0 +1,24 @@ +{{>licenseInfo}} +package {{modelPackage}} + +{{#imports}}import {{import}} +{{/imports}} + +{{#models}} +{{#model}} +/** +* {{{description}}} +{{#vars}} +* @param {{name}} {{{description}}} +{{/vars}} +*/ +data class {{classname}} ( + {{#vars}} + {{#description}} + /* {{{description}}} */ + {{/description}} + val {{{name}}}: {{{datatype}}}{{^required}}?{{/required}}{{#hasMore}},{{/hasMore}} + {{/vars}} +) +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/main/resources/kotlin-client/settings.gradle.mustache b/modules/swagger-codegen/src/main/resources/kotlin-client/settings.gradle.mustache new file mode 100644 index 00000000000..448dc07602e --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/kotlin-client/settings.gradle.mustache @@ -0,0 +1 @@ +rootProject.name = '{{artifactId}}' \ No newline at end of file diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenModelTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenModelTest.java new file mode 100644 index 00000000000..23bf434bf3f --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenModelTest.java @@ -0,0 +1,157 @@ +package io.swagger.codegen.kotlin; + +import io.swagger.codegen.*; +import io.swagger.codegen.languages.KotlinClientCodegen; +import io.swagger.models.*; +import io.swagger.models.properties.*; + +import org.testng.Assert; +import org.testng.annotations.Test; + +@SuppressWarnings("static-method") +public class KotlinClientCodegenModelTest { + + private Model getArrayTestModel() { + return new ModelImpl() + .description("a sample model") + .property("id", new LongProperty()) + .property("examples", new ArrayProperty().items(new StringProperty())) + .required("id"); + } + + private Model getSimpleModel() { + return new ModelImpl() + .description("a sample model") + .property("id", new LongProperty()) + .property("name", new StringProperty()) + .property("createdAt", new DateTimeProperty()) + .required("id") + .required("name"); + } + + private Model getMapModel() { + return new ModelImpl() + .description("a sample model") + .property("mapping", new MapProperty() + .additionalProperties(new StringProperty())) + .required("id"); + } + + private Model getComplexModel() { + return new ModelImpl() + .description("a sample model") + .property("child", new RefProperty("#/definitions/Child")); + } + + @Test(description = "convert a simple model") + public void simpleModelTest() { + final Model model = getSimpleModel(); + final DefaultCodegen codegen = new KotlinClientCodegen(); + + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a sample model"); + Assert.assertEquals(cm.vars.size(), 3); + + final CodegenProperty property1 = cm.vars.get(0); + Assert.assertEquals(property1.baseName, "id"); + Assert.assertEquals(property1.datatype, "kotlin.Long"); + Assert.assertEquals(property1.name, "id"); + Assert.assertEquals(property1.defaultValue, "null"); + Assert.assertEquals(property1.baseType, "kotlin.Long"); + Assert.assertTrue(property1.hasMore); + Assert.assertTrue(property1.required); + Assert.assertTrue(property1.isPrimitiveType); + Assert.assertTrue(property1.isNotContainer); + + final CodegenProperty property2 = cm.vars.get(1); + Assert.assertEquals(property2.baseName, "name"); + Assert.assertEquals(property2.datatype, "kotlin.String"); + Assert.assertEquals(property2.name, "name"); + Assert.assertEquals(property2.defaultValue, "null"); + Assert.assertEquals(property2.baseType, "kotlin.String"); + Assert.assertTrue(property2.hasMore); + Assert.assertTrue(property2.required); + Assert.assertTrue(property2.isPrimitiveType); + Assert.assertTrue(property2.isNotContainer); + + final CodegenProperty property3 = cm.vars.get(2); + Assert.assertEquals(property3.baseName, "createdAt"); + Assert.assertEquals(property3.datatype, "java.time.LocalDateTime"); + Assert.assertEquals(property3.name, "createdAt"); + Assert.assertEquals(property3.defaultValue, "null"); + Assert.assertEquals(property3.baseType, "java.time.LocalDateTime"); + Assert.assertFalse(property3.hasMore); + Assert.assertFalse(property3.required); + Assert.assertTrue(property3.isNotContainer); + } + + @Test(description = "convert a model with array property to default kotlin.Array") + public void arrayPropertyTest() { + final Model model = getArrayTestModel(); + + final DefaultCodegen codegen = new KotlinClientCodegen(); + final CodegenModel generated = codegen.fromModel("sample", model); + + Assert.assertEquals(generated.name, "sample"); + Assert.assertEquals(generated.classname, "Sample"); + Assert.assertEquals(generated.description, "a sample model"); + Assert.assertEquals(generated.vars.size(), 2); + + final CodegenProperty property = generated.vars.get(1); + Assert.assertEquals(property.baseName, "examples"); + Assert.assertEquals(property.getter, "getExamples"); + Assert.assertEquals(property.setter, "setExamples"); + Assert.assertEquals(property.datatype, "kotlin.Array"); + Assert.assertEquals(property.name, "examples"); + Assert.assertEquals(property.defaultValue, "null"); + Assert.assertEquals(property.baseType, "kotlin.Array"); + Assert.assertEquals(property.containerType, "array"); + Assert.assertFalse(property.required); + Assert.assertTrue(property.isContainer); + } + @Test(description = "convert a model with a map property") + public void mapPropertyTest() { + final Model model = getMapModel(); + final DefaultCodegen codegen = new KotlinClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a sample model"); + Assert.assertEquals(cm.vars.size(), 1); + + final CodegenProperty property1 = cm.vars.get(0); + Assert.assertEquals(property1.baseName, "mapping"); + Assert.assertEquals(property1.datatype, "kotlin.collections.Map"); + Assert.assertEquals(property1.name, "mapping"); + Assert.assertEquals(property1.baseType, "kotlin.collections.Map"); + Assert.assertEquals(property1.containerType, "map"); + Assert.assertFalse(property1.required); + Assert.assertTrue(property1.isContainer); + Assert.assertTrue(property1.isPrimitiveType); + } + + @Test(description = "convert a model with complex property") + public void complexPropertyTest() { + final Model model = getComplexModel(); + final DefaultCodegen codegen = new KotlinClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a sample model"); + Assert.assertEquals(cm.vars.size(), 1); + + final CodegenProperty property1 = cm.vars.get(0); + Assert.assertEquals(property1.baseName, "child"); + Assert.assertEquals(property1.datatype, "Child"); + Assert.assertEquals(property1.name, "child"); + Assert.assertEquals(property1.baseType, "Child"); + Assert.assertFalse(property1.required); + Assert.assertTrue(property1.isNotContainer); + } +} + diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenOptionsTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenOptionsTest.java new file mode 100644 index 00000000000..422326651b3 --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/kotlin/KotlinClientCodegenOptionsTest.java @@ -0,0 +1,40 @@ +package io.swagger.codegen.kotlin; + +import io.swagger.codegen.AbstractOptionsTest; +import io.swagger.codegen.CodegenConfig; +import io.swagger.codegen.languages.KotlinClientCodegen; +import io.swagger.codegen.options.KotlinClientCodegenOptionsProvider; + +import mockit.Expectations; +import mockit.Tested; + +public class KotlinClientCodegenOptionsTest extends AbstractOptionsTest { + + @Tested + private KotlinClientCodegen codegen; + + public KotlinClientCodegenOptionsTest() { + super(new KotlinClientCodegenOptionsProvider()); + } + + @Override + protected CodegenConfig getCodegenConfig() { + return codegen; + } + + @SuppressWarnings("unused") + @Override + protected void setExpectations() { + new Expectations(codegen) {{ + codegen.setPackageName(KotlinClientCodegenOptionsProvider.PACKAGE_NAME_VALUE); + times = 1; + codegen.setArtifactVersion(KotlinClientCodegenOptionsProvider.ARTIFACT_VERSION_VALUE); + times = 1; + codegen.setArtifactId(KotlinClientCodegenOptionsProvider.ARTIFACT_ID); + times = 1; + codegen.setGroupId(KotlinClientCodegenOptionsProvider.GROUP_ID); + times = 1; + }}; + } +} + diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/KotlinClientCodegenOptionsProvider.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/KotlinClientCodegenOptionsProvider.java new file mode 100644 index 00000000000..ae79a6d307f --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/KotlinClientCodegenOptionsProvider.java @@ -0,0 +1,36 @@ +package io.swagger.codegen.options; + +import io.swagger.codegen.CodegenConstants; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class KotlinClientCodegenOptionsProvider implements OptionsProvider { + public static final String PACKAGE_NAME_VALUE = "io.swagger.tests.kotlin"; + public static final String ARTIFACT_VERSION_VALUE = "0.0.1-SNAPSHOT"; + public static final String ARTIFACT_ID = "swagger-kotlin-test"; + public static final String GROUP_ID = "io.swagger.tests"; + + @Override + public String getLanguage() { + return "kotlin"; + } + + @Override + public Map createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return builder + .put(CodegenConstants.PACKAGE_NAME, PACKAGE_NAME_VALUE) + .put(CodegenConstants.ARTIFACT_VERSION, ARTIFACT_VERSION_VALUE) + .put(CodegenConstants.ARTIFACT_ID, ARTIFACT_ID) + .put(CodegenConstants.GROUP_ID, GROUP_ID) + .build(); + } + + @Override + public boolean isServer() { + return false; + } +} + diff --git a/new.sh b/new.sh new file mode 100755 index 00000000000..8c71230d50a --- /dev/null +++ b/new.sh @@ -0,0 +1,305 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage() { + echo "Stubs out files for new generators" && \ + echo "usage:" && \ + echo "$0 [options]" && \ + echo " Options:" + grep "[[:space:]].)\ #" $0 | tr -d "#" | sed -r 's/( \| \*)//' | sed -r 's/([a-z])\)/-\1/'; + exit 0; +} + +root=$(cd $(dirname "${BASH_SOURCE}") && pwd) +gen_type=client +tests=0 +gen_name= + +[ $# -eq 0 ] && usage +while getopts ":hcsdtn:" arg; do + case ${arg} in + n) # Required. Specify generator name. + gen_name=${OPTARG} + ;; + c) # Create a client generator + gen_type=client + ;; + s) # Create a server generator + gen_type=server + ;; + d) # Create a documentation generator + gen_type=documentation + ;; + t) # When specified, creates test file(s) for the generator. + tests=1 + ;; + h | *) # Display help. + usage + exit 0 + ;; + esac +done + +[ -z "${gen_name}" ] && usage + +lang_classname=$(echo "${gen_name}-${gen_type}Codegen" | sed -r 's/(^|_|-)([a-z])/\U\2/g') +gen_name_camel=$(echo "${gen_name}" | sed -r 's/(^|_|-)([a-z])/\U\2/g' | sed 's/^./\L&/') +codegen_type_enum=$(echo "${gen_type}" | sed -r 's/(.)/\U\1/g') + +# Step 1: Add Language Generator +[ -f "${root}/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/${lang_classname}.java" ] && \ + echo "${lang_classname} already exists" && exit 1; + +echo "Creating modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/${lang_classname}.java" +cat > "${root}/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/${lang_classname}.java" <> "${root}/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig" + +# Step 3: Create resource files +mkdir -p "${root}/modules/swagger-codegen/src/main/resources/${gen_name}-${gen_type}" +echo "Creating modules/swagger-codegen/src/main/resources/${gen_name}-${gen_type}/README.md" && \ + touch "${root}/modules/swagger-codegen/src/main/resources/${gen_name}-${gen_type}/README.md" +echo "Creating modules/swagger-codegen/src/main/resources/${gen_name}-${gen_type}/model.mustache" && \ + touch "${root}/modules/swagger-codegen/src/main/resources/${gen_name}-${gen_type}/model.mustache" +echo "Creating /modules/swagger-codegen/src/main/resources/${gen_name}-${gen_type}/api.mustache" && \ + touch "${root}/modules/swagger-codegen/src/main/resources/${gen_name}-${gen_type}/api.mustache" + +# Step 4: Create bash/batch scripts + +## Windows batch file +echo "Creating bin/windows/${gen_name}-${gen_type}-petstore.bat" +cat > "${root}/bin/windows/${gen_name}-${gen_type}-petstore.bat"< "${root}/bin/${gen_name}-${gen_type}-petstore.sh"< \(.*\)$') + if expr "\$link" : '/.*' > /dev/null; then + SCRIPT="\$link" + else + SCRIPT=\$(dirname "\$SCRIPT")/"\$link" + fi +done + +if [ ! -d "\${APP_DIR}" ]; then + APP_DIR=\$(dirname "\$SCRIPT")/.. + APP_DIR=\$(cd "\${APP_DIR}"; pwd) +fi + +executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "\$executable" ] +then + mvn clean package +fi + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="\${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="\$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l ${gen_name} -o samples/${gen_type}/${gen_name}" + +java \${JAVA_OPTS} -jar \${executable} \${ags} +EOF + +# Step 5: (optional) Create Swagger Codegen test files +if [ "1" -eq "${tests}" ]; then + mkdir -p "${root}/modules/swagger-codegen/src/test/java/io/swagger/codegen/${gen_name_camel}" + # Codegen + echo "Creating modules/swagger-codegen/src/test/java/io/swagger/codegen/${gen_name_camel}/${lang_classname}Test.java" + cat > "${root}/modules/swagger-codegen/src/test/java/io/swagger/codegen/${gen_name_camel}/${lang_classname}Test.java"< "${root}/modules/swagger-codegen/src/test/java/io/swagger/codegen/${gen_name_camel}/${lang_classname}ModelTest.java"< "${root}/modules/swagger-codegen/src/test/java/io/swagger/codegen/${gen_name_camel}/${lang_classname}OptionsTest.java"< "${root}/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/${lang_classname}OptionsProvider.java"< createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return builder + .put(${lang_classname}.PROJECT_NAME, PROJECT_NAME_VALUE) + .build(); + } + + @Override + public boolean isServer() { + return false; + } +} + +EOF +fi + +echo "Finished." diff --git a/samples/client/kotlin/.gitignore b/samples/client/kotlin/.gitignore new file mode 100644 index 00000000000..fe1b206d040 --- /dev/null +++ b/samples/client/kotlin/.gitignore @@ -0,0 +1,106 @@ + +# Created by https://www.gitignore.io/api/java,intellij,gradle + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Gradle ### +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# End of https://www.gitignore.io/api/java,intellij,gradle diff --git a/samples/client/kotlin/.swagger-codegen-ignore b/samples/client/kotlin/.swagger-codegen-ignore new file mode 100644 index 00000000000..c5fa491b4c5 --- /dev/null +++ b/samples/client/kotlin/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/kotlin/.swagger-codegen/VERSION b/samples/client/kotlin/.swagger-codegen/VERSION new file mode 100644 index 00000000000..7fea99011a6 --- /dev/null +++ b/samples/client/kotlin/.swagger-codegen/VERSION @@ -0,0 +1 @@ +2.2.3-SNAPSHOT \ No newline at end of file diff --git a/samples/client/kotlin/README.md b/samples/client/kotlin/README.md new file mode 100644 index 00000000000..885688a0fd6 --- /dev/null +++ b/samples/client/kotlin/README.md @@ -0,0 +1,29 @@ +# Generated Kotlin Client library + +## Requires + +* Kotlin 1.1.2 +* Gradle 3.3 + +## Build + +First, create the gradle wrapper script: + +``` +gradle wrapper +``` + +Then, run: + +``` +./gradlew check assemble +``` + +This runs all tests and packages the library. + +## Notes + +* Supports JSON inputs/outputs, File inputs, and Form inputs. +* Supports collection formats for query parameters: csv, tsv, ssv, pipes. +* Some Kotlin and Java types are fully qualified to avoid conflicts with types defined in Swagger definitions. +* Implementation of ApiClient is intended to reduce method counts, specifically to benefit Android targets. \ No newline at end of file diff --git a/samples/client/kotlin/bin/build.gradle b/samples/client/kotlin/bin/build.gradle new file mode 100644 index 00000000000..1ae1ae99322 --- /dev/null +++ b/samples/client/kotlin/bin/build.gradle @@ -0,0 +1,32 @@ +group 'io.swagger' +version '1.0.0' + +task wrapper(type: Wrapper) { + gradleVersion = '3.3' + distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" +} + +buildscript { + ext.kotlin_version = '1.1.2' + + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' + +repositories { + mavenCentral() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + compile "com.squareup.moshi:moshi-kotlin:1.5.0" + compile "com.squareup.moshi:moshi-adapters:1.5.0" + compile "com.squareup.okhttp3:okhttp:3.8.0" + testCompile "io.kotlintest:kotlintest:2.0.2" +} \ No newline at end of file diff --git a/samples/client/kotlin/bin/settings.gradle b/samples/client/kotlin/bin/settings.gradle new file mode 100644 index 00000000000..50b05bef95e --- /dev/null +++ b/samples/client/kotlin/bin/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'kotlin-client' \ No newline at end of file diff --git a/samples/client/kotlin/build.gradle b/samples/client/kotlin/build.gradle new file mode 100644 index 00000000000..1ae1ae99322 --- /dev/null +++ b/samples/client/kotlin/build.gradle @@ -0,0 +1,32 @@ +group 'io.swagger' +version '1.0.0' + +task wrapper(type: Wrapper) { + gradleVersion = '3.3' + distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" +} + +buildscript { + ext.kotlin_version = '1.1.2' + + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' + +repositories { + mavenCentral() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + compile "com.squareup.moshi:moshi-kotlin:1.5.0" + compile "com.squareup.moshi:moshi-adapters:1.5.0" + compile "com.squareup.okhttp3:okhttp:3.8.0" + testCompile "io.kotlintest:kotlintest:2.0.2" +} \ No newline at end of file diff --git a/samples/client/kotlin/gradle/wrapper/gradle-wrapper.jar b/samples/client/kotlin/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..45ac8213b78 Binary files /dev/null and b/samples/client/kotlin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/samples/client/kotlin/gradle/wrapper/gradle-wrapper.properties b/samples/client/kotlin/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..1d03d7af2f3 --- /dev/null +++ b/samples/client/kotlin/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon May 29 10:58:54 EDT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/samples/client/kotlin/gradlew b/samples/client/kotlin/gradlew new file mode 100755 index 00000000000..4453ccea33d --- /dev/null +++ b/samples/client/kotlin/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/samples/client/kotlin/gradlew.bat b/samples/client/kotlin/gradlew.bat new file mode 100644 index 00000000000..e95643d6a2c --- /dev/null +++ b/samples/client/kotlin/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/client/kotlin/settings.gradle b/samples/client/kotlin/settings.gradle new file mode 100644 index 00000000000..50b05bef95e --- /dev/null +++ b/samples/client/kotlin/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'kotlin-client' \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/PetApi.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/PetApi.kt new file mode 100644 index 00000000000..336843fceec --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/PetApi.kt @@ -0,0 +1,286 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.apis + +import io.swagger.client.models.ApiResponse +import io.swagger.client.models.Pet + +import io.swagger.client.infrastructure.* + +class PetApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiClient(basePath) { + + /** + * Add a new pet to the store + * + * @param body Pet object that needs to be added to the store + * @return void + */ + fun addPet(body: Pet) : Unit { + val localVariableBody: kotlin.Any? = body + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.POST, + "/pet", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Deletes a pet + * + * @param petId Pet id to delete + * @param apiKey (optional) + * @return void + */ + fun deletePet(petId: kotlin.Long, apiKey: kotlin.String) : Unit { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf("apiKey" to apiKey) + val localVariableConfig = RequestConfig( + RequestMethod.DELETE, + "/pet/{petId}".replace("{"+"petId"+"}", "$petId"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Finds Pets by status + * Multiple status values can be provided with comma separated strings + * @param status Status values that need to be considered for filter + * @return kotlin.Array + */ + @Suppress("UNCHECKED_CAST") + fun findPetsByStatus(status: kotlin.Array) : kotlin.Array { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf("status" to status.joinToString(separator = collectionDelimiter("csv"))) + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/pet/findByStatus", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request>( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as kotlin.Array + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Finds Pets by tags + * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + * @param tags Tags to filter by + * @return kotlin.Array + */ + @Suppress("UNCHECKED_CAST") + fun findPetsByTags(tags: kotlin.Array) : kotlin.Array { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf("tags" to tags.joinToString(separator = collectionDelimiter("csv"))) + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/pet/findByTags", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request>( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as kotlin.Array + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Find pet by ID + * Returns a single pet + * @param petId ID of pet to return + * @return Pet + */ + @Suppress("UNCHECKED_CAST") + fun getPetById(petId: kotlin.Long) : Pet { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/pet/{petId}".replace("{"+"petId"+"}", "$petId"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as Pet + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Update an existing pet + * + * @param body Pet object that needs to be added to the store + * @return void + */ + fun updatePet(body: Pet) : Unit { + val localVariableBody: kotlin.Any? = body + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.PUT, + "/pet", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Updates a pet in the store with form data + * + * @param petId ID of pet that needs to be updated + * @param name Updated name of the pet (optional) + * @param status Updated status of the pet (optional) + * @return void + */ + fun updatePetWithForm(petId: kotlin.Long, name: kotlin.String, status: kotlin.String) : Unit { + val localVariableBody: kotlin.Any? = mapOf("name" to "$name", "status" to "$status") + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf("Content-Type" to "multipart/form-data") + val localVariableConfig = RequestConfig( + RequestMethod.POST, + "/pet/{petId}".replace("{"+"petId"+"}", "$petId"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * uploads an image + * + * @param petId ID of pet to update + * @param additionalMetadata Additional data to pass to server (optional) + * @param file file to upload (optional) + * @return ApiResponse + */ + @Suppress("UNCHECKED_CAST") + fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String, file: java.io.File) : ApiResponse { + val localVariableBody: kotlin.Any? = mapOf("additionalMetadata" to "$additionalMetadata", "file" to "$file") + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf("Content-Type" to "multipart/form-data") + val localVariableConfig = RequestConfig( + RequestMethod.POST, + "/pet/{petId}/uploadImage".replace("{"+"petId"+"}", "$petId"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as ApiResponse + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + + private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipes" -> "|" + "ssv" -> " " + else -> "" + } +} diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/StoreApi.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/StoreApi.kt new file mode 100644 index 00000000000..9dabfdb3b66 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/StoreApi.kt @@ -0,0 +1,154 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.apis + +import io.swagger.client.models.Order + +import io.swagger.client.infrastructure.* + +class StoreApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiClient(basePath) { + + /** + * Delete purchase order by ID + * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + * @param orderId ID of the order that needs to be deleted + * @return void + */ + fun deleteOrder(orderId: kotlin.String) : Unit { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.DELETE, + "/store/order/{orderId}".replace("{"+"orderId"+"}", "$orderId"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Returns pet inventories by status + * Returns a map of status codes to quantities + * @return kotlin.collections.Map + */ + @Suppress("UNCHECKED_CAST") + fun getInventory() : kotlin.collections.Map { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/store/inventory", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request>( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as kotlin.collections.Map + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Find purchase order by ID + * For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions + * @param orderId ID of pet that needs to be fetched + * @return Order + */ + @Suppress("UNCHECKED_CAST") + fun getOrderById(orderId: kotlin.Long) : Order { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/store/order/{orderId}".replace("{"+"orderId"+"}", "$orderId"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as Order + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Place an order for a pet + * + * @param body order placed for purchasing the pet + * @return Order + */ + @Suppress("UNCHECKED_CAST") + fun placeOrder(body: Order) : Order { + val localVariableBody: kotlin.Any? = body + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.POST, + "/store/order", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as Order + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + + private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipes" -> "|" + "ssv" -> " " + else -> "" + } +} diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/UserApi.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/UserApi.kt new file mode 100644 index 00000000000..b61953eaa61 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/apis/UserApi.kt @@ -0,0 +1,279 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.apis + +import io.swagger.client.models.User + +import io.swagger.client.infrastructure.* + +class UserApi(basePath: kotlin.String = "http://petstore.swagger.io/v2") : ApiClient(basePath) { + + /** + * Create user + * This can only be done by the logged in user. + * @param body Created user object + * @return void + */ + fun createUser(body: User) : Unit { + val localVariableBody: kotlin.Any? = body + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.POST, + "/user", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Creates list of users with given input array + * + * @param body List of user object + * @return void + */ + fun createUsersWithArrayInput(body: kotlin.Array) : Unit { + val localVariableBody: kotlin.Any? = body + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.POST, + "/user/createWithArray", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Creates list of users with given input array + * + * @param body List of user object + * @return void + */ + fun createUsersWithListInput(body: kotlin.Array) : Unit { + val localVariableBody: kotlin.Any? = body + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.POST, + "/user/createWithList", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Delete user + * This can only be done by the logged in user. + * @param username The name that needs to be deleted + * @return void + */ + fun deleteUser(username: kotlin.String) : Unit { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.DELETE, + "/user/{username}".replace("{"+"username"+"}", "$username"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Get user by user name + * + * @param username The name that needs to be fetched. Use user1 for testing. + * @return User + */ + @Suppress("UNCHECKED_CAST") + fun getUserByName(username: kotlin.String) : User { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/user/{username}".replace("{"+"username"+"}", "$username"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as User + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Logs user into the system + * + * @param username The user name for login + * @param password The password for login in clear text + * @return kotlin.String + */ + @Suppress("UNCHECKED_CAST") + fun loginUser(username: kotlin.String, password: kotlin.String) : kotlin.String { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf("username" to username, "password" to password) + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/user/login", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> (response as Success<*>).data as kotlin.String + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Logs out current logged in user session + * + * @return void + */ + fun logoutUser() : Unit { + val localVariableBody: kotlin.Any? = null + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.GET, + "/user/logout", + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + /** + * Updated user + * This can only be done by the logged in user. + * @param username name that need to be deleted + * @param body Updated user object + * @return void + */ + fun updateUser(username: kotlin.String, body: User) : Unit { + val localVariableBody: kotlin.Any? = body + val localVariableQuery: kotlin.collections.Map = mapOf() + val localVariableHeaders: kotlin.collections.Map = mapOf() + val localVariableConfig = RequestConfig( + RequestMethod.PUT, + "/user/{username}".replace("{"+"username"+"}", "$username"), + query = localVariableQuery, + headers = localVariableHeaders + ) + val response = request( + localVariableConfig, + localVariableBody + ) + + return when (response.responseType) { + ResponseType.Success -> Unit + ResponseType.Informational -> TODO() + ResponseType.Redirection -> TODO() + ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error") + ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error") + else -> throw kotlin.IllegalStateException("Undefined ResponseType.") + } + } + + + private fun collectionDelimiter(collectionFormat: kotlin.String) = when(collectionFormat) { + "csv" -> "," + "tsv" -> "\t" + "pipes" -> "|" + "ssv" -> " " + else -> "" + } +} diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt new file mode 100644 index 00000000000..05ff12c0f94 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiClient.kt @@ -0,0 +1,123 @@ +package io.swagger.client.infrastructure + +import okhttp3.* +import java.io.File + +open class ApiClient(val baseUrl: String) { + companion object { + protected val ContentType = "Content-Type" + protected val Accept = "Accept" + protected val JsonMediaType = "application/json" + protected val FormDataMediaType = "multipart/form-data" + protected val XmlMediaType = "application/xml" + + @JvmStatic + val client : OkHttpClient = OkHttpClient() + + @JvmStatic + var defaultHeaders: Map by ApplicationDelegates.setOnce(mapOf(ContentType to JsonMediaType, Accept to JsonMediaType)) + + @JvmStatic + val jsonHeaders: Map = mapOf(ContentType to JsonMediaType, Accept to JsonMediaType) + } + + inline protected fun requestBody(content: T, mediaType: String = JsonMediaType): RequestBody { + if(content is File) { + return RequestBody.create( + MediaType.parse(mediaType), content + ) + } else if(mediaType == FormDataMediaType) { + var builder = FormBody.Builder() + // content's type *must* be Map + @Suppress("UNCHECKED_CAST") + (content as Map).forEach { key, value -> + builder = builder.add(key, value) + } + return builder.build() + } else if(mediaType == JsonMediaType) { + return RequestBody.create( + MediaType.parse(mediaType), Serializer.moshi.adapter(T::class.java).toJson(content) + ) + } else if (mediaType == XmlMediaType) { + TODO("xml not currently supported.") + } + + // TODO: this should be extended with other serializers + TODO("requestBody currently only supports JSON body and File body.") + } + + inline protected fun responseBody(body: ResponseBody?, mediaType: String = JsonMediaType): T? { + if(body == null) return null + return when(mediaType) { + JsonMediaType -> Serializer.moshi.adapter(T::class.java).fromJson(body.source()) + else -> TODO() + } + } + + inline protected fun request(requestConfig: RequestConfig, body : Any? = null): ApiInfrastructureResponse { + val httpUrl = HttpUrl.parse(baseUrl) ?: throw IllegalStateException("baseUrl is invalid.") + + var urlBuilder = httpUrl.newBuilder() + .addPathSegments(requestConfig.path.trimStart('/')) + + requestConfig.query.forEach { k, v -> urlBuilder = urlBuilder.addQueryParameter(k,v) } + + val url = urlBuilder.build() + val headers = requestConfig.headers + defaultHeaders + + if(headers[ContentType] ?: "" == "") { + throw kotlin.IllegalStateException("Missing Content-Type header. This is required.") + } + + if(headers[Accept] ?: "" == "") { + throw kotlin.IllegalStateException("Missing Accept header. This is required.") + } + + val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase() + val accept = (headers[Accept] as String).substringBefore(";").toLowerCase() + + var request : Request.Builder = when (requestConfig.method) { + RequestMethod.DELETE -> Request.Builder().url(url).delete() + RequestMethod.GET -> Request.Builder().url(url) + RequestMethod.HEAD -> Request.Builder().url(url).head() + RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(body!!, contentType)) + RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(body!!, contentType)) + RequestMethod.POST -> Request.Builder().url(url).post(requestBody(body!!, contentType)) + RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null) + } + + headers.forEach { header -> request = request.addHeader(header.key, header.value) } + + val realRequest = request.build() + val response = client.newCall(realRequest).execute() + + // TODO: handle specific mapping types. e.g. Map> + when { + response.isRedirect -> return Redirection( + response.code(), + response.headers().toMultimap() + ) + response.isInformational -> return Informational( + response.message(), + response.code(), + response.headers().toMultimap() + ) + response.isSuccessful -> return Success( + responseBody(response.body(), accept), + response.code(), + response.headers().toMultimap() + ) + response.isClientError -> return ClientError( + response.body()?.string(), + response.code(), + response.headers().toMultimap() + ) + else -> return ServerError( + null, + response.body()?.string(), + response.code(), + response.headers().toMultimap() + ) + } + } +} \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiInfrastructureResponse.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiInfrastructureResponse.kt new file mode 100644 index 00000000000..b2b8a9cbc6b --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApiInfrastructureResponse.kt @@ -0,0 +1,40 @@ +package io.swagger.client.infrastructure + +enum class ResponseType { + Success, Informational, Redirection, ClientError, ServerError +} + +abstract class ApiInfrastructureResponse(val responseType: ResponseType) { + abstract val statusCode: Int + abstract val headers: Map> +} + +class Success( + val data: T, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +): ApiInfrastructureResponse(ResponseType.Success) + +class Informational( + val statusText: String, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiInfrastructureResponse(ResponseType.Informational) + +class Redirection( + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiInfrastructureResponse(ResponseType.Redirection) + +class ClientError( + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> = mapOf() +) : ApiInfrastructureResponse(ResponseType.ClientError) + +class ServerError( + val message: String? = null, + val body: Any? = null, + override val statusCode: Int = -1, + override val headers: Map> +): ApiInfrastructureResponse(ResponseType.ServerError) \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApplicationDelegates.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApplicationDelegates.kt new file mode 100644 index 00000000000..340d32e3211 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ApplicationDelegates.kt @@ -0,0 +1,29 @@ +package io.swagger.client.infrastructure + +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +object ApplicationDelegates { + /** + * Provides a property delegate, allowing the property to be set once and only once. + * + * If unset (no default value), a get on the property will throw [IllegalStateException]. + */ + fun setOnce(defaultValue: T? = null) : ReadWriteProperty = SetOnce(defaultValue) + + private class SetOnce(defaultValue: T? = null) : ReadWriteProperty { + private var isSet = false + private var value: T? = defaultValue + + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return value ?: throw IllegalStateException("${property.name} not initialized") + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = synchronized(this) { + if (!isSet) { + this.value = value + isSet = true + } + } + } +} \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/Errors.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/Errors.kt new file mode 100644 index 00000000000..ff908d53a92 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/Errors.kt @@ -0,0 +1,42 @@ +@file:Suppress("unused") +package io.swagger.client.infrastructure + +import java.lang.RuntimeException + +open class ClientException : RuntimeException { + + /** + * Constructs an [ClientException] with no detail message. + */ + constructor() : super() + + /** + * Constructs an [ClientException] with the specified detail message. + + * @param message the detail message. + */ + constructor(message: kotlin.String) : super(message) + + companion object { + private const val serialVersionUID: Long = 123L + } +} + +open class ServerException : RuntimeException { + + /** + * Constructs an [ServerException] with no detail message. + */ + constructor() : super() + + /** + * Constructs an [ServerException] with the specified detail message. + + * @param message the detail message. + */ + constructor(message: kotlin.String) : super(message) + + companion object { + private const val serialVersionUID: Long = 456L + } +} \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestConfig.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestConfig.kt new file mode 100644 index 00000000000..1df8b769db5 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestConfig.kt @@ -0,0 +1,13 @@ +package io.swagger.client.infrastructure + +/** + * Defines a config object for a given request. + * NOTE: This object doesn't include 'body' because it + * allows for caching of the constructed object + * for many request definitions. + */ +data class RequestConfig( + val method: RequestMethod, + val path: String, + val headers: Map = mapOf(), + val query: Map = mapOf()) \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestMethod.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestMethod.kt new file mode 100644 index 00000000000..d9d04f02701 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/RequestMethod.kt @@ -0,0 +1,8 @@ +package io.swagger.client.infrastructure + +/** + * Provides enumerated HTTP verbs + */ +enum class RequestMethod { + GET, DELETE, HEAD, OPTIONS, PATCH, POST, PUT +} \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ResponseExtensions.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ResponseExtensions.kt new file mode 100644 index 00000000000..04e91720138 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/ResponseExtensions.kt @@ -0,0 +1,23 @@ +package io.swagger.client.infrastructure + +import okhttp3.Response + +/** + * Provides an extension to evaluation whether the response is a 1xx code + */ +val Response.isInformational : Boolean get() = this.code() in 100..199 + +/** + * Provides an extension to evaluation whether the response is a 3xx code + */ +val Response.isRedirect : Boolean get() = this.code() in 300..399 + +/** + * Provides an extension to evaluation whether the response is a 4xx code + */ +val Response.isClientError : Boolean get() = this.code() in 400..499 + +/** + * Provides an extension to evaluation whether the response is a 5xx (Standard) through 999 (non-standard) code + */ +val Response.isServerError : Boolean get() = this.code() in 500..999 \ No newline at end of file diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/Serializer.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/Serializer.kt new file mode 100644 index 00000000000..a858407ff34 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/infrastructure/Serializer.kt @@ -0,0 +1,14 @@ +package io.swagger.client.infrastructure + +import com.squareup.moshi.KotlinJsonAdapterFactory +import com.squareup.moshi.Moshi +import com.squareup.moshi.Rfc3339DateJsonAdapter +import java.util.* + +object Serializer { + @JvmStatic + val moshi: Moshi = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .add(Date::class.java, Rfc3339DateJsonAdapter().nullSafe()) + .build() +} diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/ApiResponse.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/ApiResponse.kt new file mode 100644 index 00000000000..82521f53eb8 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/ApiResponse.kt @@ -0,0 +1,25 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.models + + +/** +* Describes the result of uploading an image resource +* @param code +* @param type +* @param message +*/ +data class ApiResponse ( + val code: kotlin.Int?, + val type: kotlin.String?, + val message: kotlin.String? +) diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Category.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Category.kt new file mode 100644 index 00000000000..e5cbf21efda --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Category.kt @@ -0,0 +1,23 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.models + + +/** +* A category for a pet +* @param id +* @param name +*/ +data class Category ( + val id: kotlin.Long?, + val name: kotlin.String? +) diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Order.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Order.kt new file mode 100644 index 00000000000..324908a7b1c --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Order.kt @@ -0,0 +1,32 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.models + + +/** +* An order for a pets from the pet store +* @param id +* @param petId +* @param quantity +* @param shipDate +* @param status Order Status +* @param complete +*/ +data class Order ( + val id: kotlin.Long?, + val petId: kotlin.Long?, + val quantity: kotlin.Int?, + val shipDate: java.time.LocalDateTime?, + /* Order Status */ + val status: kotlin.String?, + val complete: kotlin.Boolean? +) diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Pet.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Pet.kt new file mode 100644 index 00000000000..210d6258545 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Pet.kt @@ -0,0 +1,34 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.models + +import io.swagger.client.models.Category +import io.swagger.client.models.Tag + +/** +* A pet for sale in the pet store +* @param id +* @param category +* @param name +* @param photoUrls +* @param tags +* @param status pet status in the store +*/ +data class Pet ( + val id: kotlin.Long?, + val category: Category?, + val name: kotlin.String, + val photoUrls: kotlin.Array, + val tags: kotlin.Array?, + /* pet status in the store */ + val status: kotlin.String? +) diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Tag.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Tag.kt new file mode 100644 index 00000000000..ec9fb55e576 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/Tag.kt @@ -0,0 +1,23 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.models + + +/** +* A tag for a pet +* @param id +* @param name +*/ +data class Tag ( + val id: kotlin.Long?, + val name: kotlin.String? +) diff --git a/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/User.kt b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/User.kt new file mode 100644 index 00000000000..4bb9a160458 --- /dev/null +++ b/samples/client/kotlin/src/main/kotlin/io/swagger/client/models/User.kt @@ -0,0 +1,36 @@ +/** +* Swagger Petstore +* This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters. +* +* OpenAPI spec version: 1.0.0 +* Contact: apiteam@swagger.io +* +* NOTE: This class is auto generated by the swagger code generator program. +* https://github.com/swagger-api/swagger-codegen.git +* Do not edit the class manually. +*/ +package io.swagger.client.models + + +/** +* A User who is purchasing from the pet store +* @param id +* @param username +* @param firstName +* @param lastName +* @param email +* @param password +* @param phone +* @param userStatus User Status +*/ +data class User ( + val id: kotlin.Long?, + val username: kotlin.String?, + val firstName: kotlin.String?, + val lastName: kotlin.String?, + val email: kotlin.String?, + val password: kotlin.String?, + val phone: kotlin.String?, + /* User Status */ + val userStatus: kotlin.Int? +)