diff --git a/bin/configs/kotlin-jvm-volley.yaml b/bin/configs/kotlin-jvm-volley.yaml
new file mode 100644
index 000000000000..cdb8ac51a9b4
--- /dev/null
+++ b/bin/configs/kotlin-jvm-volley.yaml
@@ -0,0 +1,9 @@
+generatorName: kotlin
+outputDir: samples/client/petstore/kotlin-jvm-volley
+library: jvm-volley
+inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore.yaml
+templateDir: modules/openapi-generator/src/main/resources/kotlin-client
+additionalProperties:
+ artifactId: kotlin-petstore-jvm-volley
+ generateRoomModels: "true"
+ serializationLibrary: "gson"
diff --git a/docs/generators/kotlin.md b/docs/generators/kotlin.md
index cbf43a3a1e9e..786ff7013ebb 100644
--- a/docs/generators/kotlin.md
+++ b/docs/generators/kotlin.md
@@ -13,8 +13,9 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|collectionType|Option. Collection type to use|
- **array**
- kotlin.Array
- **list**
- kotlin.collections.List
|list|
|dateLibrary|Option. Date library to use|- **threetenbp-localdatetime**
- Threetenbp - Backport of JSR310 (jvm only, for legacy app only)
- **string**
- String
- **java8-localdatetime**
- Java 8 native JSR310 (jvm only, for legacy app only)
- **java8**
- Java 8 native JSR310 (jvm only, preferred for jdk 1.8+)
- **threetenbp**
- Threetenbp - Backport of JSR310 (jvm only, preferred for jdk < 1.8)
|java8|
|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |camelCase|
+|generateRoomModels|Generate Android Room database models in addition to API models (JVM Volley library only)| |false|
|groupId|Generated artifact package's organization (i.e. maven groupId).| |org.openapitools|
-|library|Library template (sub-template) to use|- **jvm-okhttp4**
- [DEFAULT] Platform: Java Virtual Machine. HTTP client: OkHttp 4.2.0 (Android 5.0+ and Java 8+). JSON processing: Moshi 1.8.0.
- **jvm-okhttp3**
- Platform: Java Virtual Machine. HTTP client: OkHttp 3.12.4 (Android 2.3+ and Java 7+). JSON processing: Moshi 1.8.0.
- **jvm-retrofit2**
- Platform: Java Virtual Machine. HTTP client: Retrofit 2.6.2.
- **multiplatform**
- Platform: Kotlin multiplatform. HTTP client: Ktor 1.6.0. JSON processing: Kotlinx Serialization: 1.2.1.
|jvm-okhttp4|
+|library|Library template (sub-template) to use|- **jvm-okhttp4**
- [DEFAULT] Platform: Java Virtual Machine. HTTP client: OkHttp 4.2.0 (Android 5.0+ and Java 8+). JSON processing: Moshi 1.8.0.
- **jvm-okhttp3**
- Platform: Java Virtual Machine. HTTP client: OkHttp 3.12.4 (Android 2.3+ and Java 7+). JSON processing: Moshi 1.8.0.
- **jvm-retrofit2**
- Platform: Java Virtual Machine. HTTP client: Retrofit 2.6.2.
- **multiplatform**
- Platform: Kotlin multiplatform. HTTP client: Ktor 1.6.0. JSON processing: Kotlinx Serialization: 1.2.1.
- **jvm-volley**
- Platform: JVM for Android. HTTP client: Volley 1.2.1. JSON processing: gson 2.8.9
|jvm-okhttp4|
|modelMutable|Create mutable models| |false|
|moshiCodeGen|Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info.| |false|
|omitGradlePluginVersions|Whether to declare Gradle plugin versions in build files.| |false|
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java
index c8d7a87716d0..b0d272d075b4 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinClientCodegen.java
@@ -57,12 +57,15 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
protected static final String JVM_OKHTTP3 = "jvm-okhttp3";
protected static final String JVM_RETROFIT2 = "jvm-retrofit2";
protected static final String MULTIPLATFORM = "multiplatform";
+ protected static final String JVM_VOLLEY = "jvm-volley";
public static final String USE_RX_JAVA = "useRxJava";
public static final String USE_RX_JAVA2 = "useRxJava2";
public static final String USE_RX_JAVA3 = "useRxJava3";
public static final String USE_COROUTINES = "useCoroutines";
public static final String DO_NOT_USE_RX_AND_COROUTINES = "doNotUseRxAndCoroutines";
+ public static final String GENERATE_ROOM_MODELS = "generateRoomModels";
+ public static final String ROOM_MODEL_PACKAGE = "roomModelPackage";
public static final String OMIT_GRADLE_PLUGIN_VERSIONS = "omitGradlePluginVersions";
public static final String DATE_LIBRARY = "dateLibrary";
@@ -85,6 +88,9 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
// backwards compatibility for openapi configs that specify neither rx1 nor rx2
// (mustache does not allow for boolean operators so we need this extra field)
protected boolean doNotUseRxAndCoroutines = true;
+ protected boolean generateRoomModels = false;
+ protected String roomModelPackage = "";
+
protected String authFolder;
@@ -167,6 +173,9 @@ public KotlinClientCodegen() {
outputFolder = "generated-code" + File.separator + "kotlin-client";
modelTemplateFiles.put("model.mustache", ".kt");
+ if (generateRoomModels) {
+ modelTemplateFiles.put("model_room.mustache", ".kt");
+ }
apiTemplateFiles.put("api.mustache", ".kt");
modelDocTemplateFiles.put("model_doc.mustache", ".md");
apiDocTemplateFiles.put("api_doc.mustache", ".md");
@@ -197,6 +206,7 @@ public KotlinClientCodegen() {
supportedLibraries.put(JVM_OKHTTP3, "Platform: Java Virtual Machine. HTTP client: OkHttp 3.12.4 (Android 2.3+ and Java 7+). JSON processing: Moshi 1.8.0.");
supportedLibraries.put(JVM_RETROFIT2, "Platform: Java Virtual Machine. HTTP client: Retrofit 2.6.2.");
supportedLibraries.put(MULTIPLATFORM, "Platform: Kotlin multiplatform. HTTP client: Ktor 1.6.0. JSON processing: Kotlinx Serialization: 1.2.1.");
+ supportedLibraries.put(JVM_VOLLEY, "Platform: JVM for Android. HTTP client: Volley 1.2.1. JSON processing: gson 2.8.9");
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "Library template (sub-template) to use");
libraryOption.setEnum(supportedLibraries);
@@ -220,6 +230,8 @@ public KotlinClientCodegen() {
cliOptions.add(CliOption.newBoolean(MOSHI_CODE_GEN, "Whether to enable codegen with the Moshi library. Refer to the [official Moshi doc](https://github.com/square/moshi#codegen) for more info."));
+ cliOptions.add(CliOption.newBoolean(GENERATE_ROOM_MODELS, "Generate Android Room database models in addition to API models (JVM Volley library only)", false));
+
cliOptions.add(CliOption.newBoolean(SUPPORT_ANDROID_API_LEVEL_25_AND_BELLOW, "[WARNING] This flag will generate code that has a known security vulnerability. It uses `kotlin.io.createTempFile` instead of `java.nio.file.Files.createTempFile` in oder to support Android API level 25 and bellow. For more info, please check the following links https://github.com/OpenAPITools/openapi-generator/security/advisories/GHSA-23x4-m842-fmwf, https://github.com/OpenAPITools/openapi-generator/pull/9284"));
}
@@ -235,6 +247,12 @@ public String getHelp() {
return "Generates a Kotlin client.";
}
+ public boolean getGenerateRoomModels() { return generateRoomModels; }
+
+ public void setGenerateRoomModels(Boolean generateRoomModels) {
+ this.generateRoomModels = generateRoomModels;
+ }
+
public void setUseRxJava(boolean useRxJava) {
if (useRxJava) {
this.useRxJava2 = false;
@@ -298,14 +316,45 @@ public void setCollectionType(String collectionType) {
this.collectionType = collectionType;
}
+ public void setRoomModelPackage(String roomModelPackage) {
+ this.roomModelPackage = roomModelPackage;
+ }
+
@Override
- public void processOpts() {
- super.processOpts();
+ public String modelFilename(String templateName, String modelName) {
+ String suffix = modelTemplateFiles().get(templateName);
+ // If this was a proper template method, i wouldn't have to make myself throw up by doing this....
+ if (getGenerateRoomModels() && suffix.startsWith("RoomModel")) {
+ return roomModelFileFolder() + File.separator + toModelFilename(modelName) + suffix;
+ } else {
+ return modelFileFolder() + File.separator + toModelFilename(modelName) + suffix;
+ }
+ }
+
+ public String roomModelFileFolder() {
+ return outputFolder + File.separator + sourceFolder + File.separator + roomModelPackage.replace('.', File.separatorChar);
+ }
- if (MULTIPLATFORM.equals(getLibrary())) {
- sourceFolder = "src/commonMain/kotlin";
+ @Override
+ public void processOpts() {
+ if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
+ setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
+ } else {
+ // Set the value to defaults if we haven't overridden
+ if (MULTIPLATFORM.equals(getLibrary())) {
+ setSourceFolder("src/commonMain/kotlin");
+ }
+ else if (JVM_VOLLEY.equals(getLibrary())){
+ // Android plugin wants it's source in java
+ setSourceFolder("src/main/java");
+ }
+ else {
+ setSourceFolder(super.sourceFolder);
+ }
+ additionalProperties.put(CodegenConstants.SOURCE_FOLDER, this.sourceFolder);
}
+ super.processOpts();
boolean hasRx = additionalProperties.containsKey(USE_RX_JAVA);
boolean hasRx2 = additionalProperties.containsKey(USE_RX_JAVA2);
@@ -348,6 +397,12 @@ public void processOpts() {
final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator + "infrastructure").replace(".", "/");
authFolder = (sourceFolder + File.separator + packageName + File.separator + "auth").replace(".", "/");
+ // request destination folder
+ final String requestFolder = (sourceFolder + File.separator + packageName + File.separator + "request").replace(".", "/");
+
+ // auth destination folder
+ final String authFolder = (sourceFolder + File.separator + packageName + File.separator + "auth").replace(".", "/");
+
// additional properties
if (additionalProperties.containsKey(DATE_LIBRARY)) {
setDateLibrary(additionalProperties.get(DATE_LIBRARY).toString());
@@ -364,6 +419,9 @@ public void processOpts() {
case JVM_OKHTTP4:
processJVMOkHttpLibrary(infrastructureFolder);
break;
+ case JVM_VOLLEY:
+ processJVMVolleyLibrary(infrastructureFolder, requestFolder, authFolder);
+ break;
case JVM_RETROFIT2:
processJVMRetrofit2Library(infrastructureFolder);
break;
@@ -477,6 +535,47 @@ private void processJVMRetrofit2Library(String infrastructureFolder) {
addSupportingSerializerAdapters(infrastructureFolder);
}
+ private void processJVMVolleyLibrary(String infrastructureFolder, String requestFolder, String authFolder) {
+
+ additionalProperties.put(JVM, true);
+ additionalProperties.put(JVM_VOLLEY, true);
+
+ if (additionalProperties.containsKey(GENERATE_ROOM_MODELS)) {
+ this.setGenerateRoomModels(convertPropertyToBooleanAndWriteBack(GENERATE_ROOM_MODELS));
+ // Hide this option behind a property getter and setter in case we need to check it elsewhere
+ if (getGenerateRoomModels()) {
+ modelTemplateFiles.put("model_room.mustache", "RoomModel.kt");
+ supportingFiles.add(new SupportingFile("infrastructure/ITransformForStorage.mustache", infrastructureFolder, "ITransformForStorage.kt"));
+
+ }
+ } else {
+ additionalProperties.put(GENERATE_ROOM_MODELS, generateRoomModels);
+ }
+
+ if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
+ if (!additionalProperties.containsKey(ROOM_MODEL_PACKAGE))
+ this.setRoomModelPackage(packageName + ".models.room");
+ else
+ this.setRoomModelPackage(additionalProperties.get(ROOM_MODEL_PACKAGE).toString());
+ }
+ additionalProperties.put(ROOM_MODEL_PACKAGE, roomModelPackage);
+
+ supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt"));
+
+ // We have auth related partial files, so they can be overridden, but don't generate them explicitly
+ supportingFiles.add(new SupportingFile("request/GsonRequest.mustache", requestFolder, "GsonRequest.kt"));
+ supportingFiles.add(new SupportingFile("request/IRequestFactory.mustache", requestFolder, "IRequestFactory.kt"));
+ supportingFiles.add(new SupportingFile("request/RequestFactory.mustache", requestFolder, "RequestFactory.kt"));
+ supportingFiles.add(new SupportingFile("infrastructure/CollectionFormats.kt.mustache", infrastructureFolder, "CollectionFormats.kt"));
+
+ if (getSerializationLibrary() != SERIALIZATION_LIBRARY_TYPE.gson) {
+ throw new RuntimeException("This library currently only supports gson serialization. Try adding '--additional-properties serializationLibrary=gson' to your command.");
+ }
+ addSupportingSerializerAdapters(infrastructureFolder);
+ supportingFiles.remove(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt"));
+
+ }
+
private void addSupportingSerializerAdapters(final String infrastructureFolder) {
supportingFiles.add(new SupportingFile("jvm-common/infrastructure/Serializer.kt.mustache", infrastructureFolder, "Serializer.kt"));
supportingFiles.add(new SupportingFile("jvm-common/infrastructure/ByteArrayAdapter.kt.mustache", infrastructureFolder, "ByteArrayAdapter.kt"));
@@ -605,6 +704,11 @@ private void commonSupportingFiles() {
if (getLibrary().equals(MULTIPLATFORM)) {
supportingFiles.add(new SupportingFile("build.gradle.kts.mustache", "", "build.gradle.kts"));
supportingFiles.add(new SupportingFile("settings.gradle.kts.mustache", "", "settings.gradle.kts"));
+ } else if (getLibrary().equals(JVM_VOLLEY)) {
+ supportingFiles.add(new SupportingFile("build.mustache", "", "build.gradle"));
+ supportingFiles.add(new SupportingFile("gradle.properties.mustache", "", "gradle.properties"));
+ supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle"));
+ supportingFiles.add(new SupportingFile("manifest.mustache", "", "src/main/AndroidManifest.xml"));
} else {
supportingFiles.add(new SupportingFile("build.gradle.mustache", "", "build.gradle"));
supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle"));
@@ -625,6 +729,9 @@ public Map postProcessModels(Map objs) {
for (Object model : models) {
@SuppressWarnings("unchecked") Map mo = (Map) model;
CodegenModel cm = (CodegenModel) mo.get("model");
+ if (getGenerateRoomModels()) {
+ cm.vendorExtensions.put("x-has-data-class-body", true);
+ }
// escape the variable base name for use as a string literal
List vars = Stream.of(
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache
index c5289a46fadc..5f5b4da03ca8 100644
--- a/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/data_class.mustache
@@ -35,6 +35,10 @@ import kotlinx.serialization.encoding.*
{{#serializableModel}}
import java.io.Serializable
{{/serializableModel}}
+{{#generateRoomModels}}
+import {{roomModelPackage}}.{{classname}}RoomModel
+import {{packageName}}.infrastructure.ITransformForStorage
+{{/generateRoomModels}}
/**
* {{{description}}}
@@ -56,8 +60,16 @@ import java.io.Serializable
{{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}}
{{/allVars}}
-){{/discriminator}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#vendorExtensions.x-has-data-class-body}} {
+){{/discriminator}}{{#parent}}{{^serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{^parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{^serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#parent}}{{#serializableModel}}{{#parcelizeModels}} : {{{parent}}}{{#isMap}}(){{/isMap}}{{#isArray}}(){{/isArray}}, Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{^parcelizeModels}} : Serializable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{^serializableModel}}{{#parcelizeModels}} : Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{^parent}}{{#serializableModel}}{{#parcelizeModels}} : Serializable, Parcelable{{/parcelizeModels}}{{/serializableModel}}{{/parent}}{{#generateRoomModels}}{{#parent}}, {{/parent}}{{^discriminator}}{{^parent}}:{{/parent}} ITransformForStorage<{{classname}}RoomModel>{{/discriminator}}{{/generateRoomModels}}{{#vendorExtensions.x-has-data-class-body}} {
{{/vendorExtensions.x-has-data-class-body}}
+{{#generateRoomModels}}
+ companion object { }
+ {{^discriminator}}override fun toRoomModel(): {{classname}}RoomModel =
+ {{classname}}RoomModel(roomTableId = 0,
+ {{#allVars}}{{#items.isPrimitiveType}}{{#isArray}}{{#isList}}{{name}} = this.{{name}},{{/isList}}{{/isArray}}{{/items.isPrimitiveType}}{{^isEnum}}{{^isArray}}{{name}} = this.{{name}},{{/isArray}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{name}} = this.{{name}},{{/isArray}}{{/isEnum}}
+ {{/allVars}}
+ ){{/discriminator}}
+{{/generateRoomModels}}
{{#serializableModel}}
{{#nonPublicApi}}internal {{/nonPublicApi}}companion object {
private const val serialVersionUID: Long = 123
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/README.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/README.mustache
new file mode 100644
index 000000000000..ea684b998481
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/README.mustache
@@ -0,0 +1,232 @@
+# {{packageName}} - Kotlin client library for {{appName}}
+
+
+A kotlin client for Android using the currently recommended http client, Volley. See https://developer.android.com/training/volley
+
+- Currently sends GsonRequests
+- Currently only supports Gson as a serializer - will throw an exception if a different serializer is chosen
+- Defaults the source location to src/main/java as per standard Android builds
+
+
+## Design
+
+Volley is a queue/request based layer on top of http url stack specific to Android. Android favours dependency injection and
+a layered architecture, and IO performed off the main thread to maintain UI responsiveness, with a preferred technique of
+kotlin co-routines. The code gen library reflects these factors.
+
+- Api calls use co-routines, and execute them using volley callbacks to avoid tying up a thread.
+- Facilitate dependency injection, with default implementations available.
+- Generate a requestFactory that can be overridden
+- Allow the passing of the RequestFactory per tag (api client) or per operation (an extra parameter is created on operations with non-global security), with per operation auth overriding global security.
+- DI scoping of the Request Factory and pre-generated auth header factories allow for thread safe and secure setting of credentials.
+- Lazy header factories allow for refreshing tokens etc
+- Factoring of header factories to the Request Factory allow ambient provision of credentials. Code gen library is credential storage agnostic.
+- Header factories allow the merging of generated headers from open api spec with dynamically added headers
+
+- Injection of http url stack to allow custom http stacks. Default implementation is best practice singleton
+- Data classes used for serialisation to reflect volley's preference - an immutable request that once queued can't be tampered with.
+
+- Reuse model class and other jvm common infrastructure
+
+- Optional generation of room database models, and transform methods to these from open api models
+- Room and api models can be extended with additional extension properties.
+
+## Future improvements
+- Option to generate image requests on certain conditionals e.g content-type gif etc
+- Support for kotlin serialization.
+- Multi part form parameters and support for file inputs
+
+## Usage
+Hilt Dependency injection example - with default values for parameters overridden.
+```
+ @Provides
+ internal fun provideSomeApi(
+ context: Context,
+ restService: IRestService,
+ configurationService: IConfigurationService,
+ sessionService: ISessionService
+ ): SomeApi {
+ return SomeApi(
+ context = context,
+ requestQueue = restService.getRequestQueue(),
+ requestFactory = RequestFactory(listOf(createSessionHeaderFactory(sessionService), createTraceHeaderFactory()),
+ postProcessors = listOf(retryPolicySetter)),
+ basePath = configurationService.getBaseUrl()
+ )
+ }
+```
+Here is the constructor so you can see the defaults
+```class SomeApi (
+val context: Context,
+val requestQueue: Lazy = lazy(initializer = {
+ Volley.newRequestQueue(context.applicationContext)
+ }),
+ val requestFactory: IRequestFactory = RequestFactory(),
+ val basePath: String = "https://yourbasepath.from_input_parameter.com/api",
+ private val postProcessors :List <(Request<*>) -> Unit> = listOf()) {
+```
+
+### Overriding defaults
+The above constructor for each api allows the following to be customized
+- A custom context, so either a singleton request queue or different scope can be created - see
+https://developer.android.com/training/volley/requestqueue#singleton
+- An overrideable request queue - which in turn can have a custom http url stack passed to it
+- An overrideable request factory constructor call, or a request factory that can be overridden by a custom template, with
+custom header factory, request post processors and custom gson adapters injected.
+
+#### Overriding request generation
+Request generation can be overridden by
+- Overriding the entire request factory template
+- Supplying custom header factories - methods that take any possible parameters but return a map of headers
+- Supplying custom request post processors - methods that take and return the request object
+
+Header factory examples can be found in the auth section, as these are implemented as header factories. eg
+```
+val basicAuthHeaderFactoryBuilder = { username: String?, password: String? ->
+{ mapOf("Authorization" to "Basic " + Base64.encodeToString("${username ?: ""}:${password ?: ""}".toByteArray(), Base64.DEFAULT))}
+}
+```
+In this case it's a lambda function (a factory method) that takes an username and password, and returns a map of headers. Other
+generated code will supply the username and password. In this case it results in a map of just one key/value pair, but
+it could be multiple. The important part is it's returning a map - and that the surrounding code
+will can bind the inputs to it at some point.
+
+Here is a different example that supplies tracing header values
+```
+/**
+ * Create a lambda of tracing headers to be injected into an API's [RequestFactory].
+ */
+private fun createTraceHeaderFactory(): () -> Map = {
+ mapOf(
+ HttpHeaderType.b3_traceId.rawValue to UUIDExtensions.asTraceId(UUID.randomUUID()),
+ HttpHeaderType.b3_spanId.rawValue to UUIDExtensions.asSpanId(UUID.randomUUID()),
+ HttpHeaderType.b3_sampled.rawValue to "1"
+ )
+}
+```
+Finally a post processor example
+```
+ /**
+ * Configure a [DefaultRetryPolicy] to be injected into the [RequestFactory] with a maximum number of retries of zero.
+ */
+ private val retryPolicySetter = { request: Request<*> ->
+ Unit.apply {
+ request.setRetryPolicy(
+ DefaultRetryPolicy(
+ RestService.DEFAULT_TIMEOUT_MS,
+ 0,
+ DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
+ )
+ )
+ }
+ }
+```
+
+### Serialization
+#### Gson and Polymorphic types
+The GsonRequest object can be passed custom type adapters
+```
+class GsonRequest(
+ method: Int,
+ url: String,
+ private val body: Any?,
+ private val headers: Map?,
+ private val params: MutableMap?,
+ private val contentTypeForBody: String?,
+ private val encodingForParams: String?,
+ private val gsonAdapters: Map?,
+ private val type: Type,
+ private val listener: Response.Listener,
+ errorListener: Response.ErrorListener
+) : Request(method, url, errorListener) {
+
+ val gsonBuilder: GsonBuilder = GsonBuilder()
+ .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter())
+ .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
+ .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
+ .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter())
+
+```
+## Requires
+
+{{#jvm}}
+* Kotlin 1.4.30
+* Gradle 6.8.3
+{{/jvm}}
+{{#multiplatform}}
+* Kotlin 1.5.10
+{{/multiplatform}}
+
+## Build
+
+{{#jvm}}
+First, create the gradle wrapper script:
+
+```
+gradle wrapper
+```
+
+Then, run:
+
+{{/jvm}}
+```
+./gradlew check assemble
+```
+
+This runs all tests and packages the library.
+
+{{#generateApiDocs}}
+
+## Documentation for API Endpoints
+
+All URIs are relative to *{{{basePath}}}*
+
+Class | Method | HTTP request | Description
+------------ | ------------- | ------------- | -------------
+{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{{summary}}}{{/summary}}
+{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
+{{/generateApiDocs}}
+
+{{#generateModelDocs}}
+
+## Documentation for Models
+
+{{#modelPackage}}
+{{#models}}{{#model}} - [{{{modelPackage}}}.{{{classname}}}]({{modelDocPath}}{{{classname}}}.md)
+{{/model}}{{/models}}
+{{/modelPackage}}
+{{^modelPackage}}
+No model defined in this package
+{{/modelPackage}}
+{{/generateModelDocs}}
+
+{{! TODO: optional documentation for authorization? }}
+## Documentation for Authorization
+
+{{^authMethods}}
+All endpoints do not require authorization.
+{{/authMethods}}
+{{#authMethods}}
+{{#last}}
+Authentication schemes defined for the API:
+{{/last}}
+{{/authMethods}}
+{{#authMethods}}
+
+### {{name}}
+
+{{#isApiKey}}- **Type**: API key
+- **API key parameter name**: {{keyParamName}}
+- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
+{{/isApiKey}}
+{{#isBasic}}- **Type**: HTTP basic authentication
+{{/isBasic}}
+{{#isOAuth}}- **Type**: OAuth
+- **Flow**: {{flow}}
+- **Authorization URL**: {{authorizationUrl}}
+- **Scopes**: {{^scopes}}N/A{{/scopes}}
+{{#scopes}} - {{scope}}: {{description}}
+{{/scopes}}
+{{/isOAuth}}
+
+{{/authMethods}}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/api.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/api.mustache
new file mode 100644
index 000000000000..6262fb5b53e0
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/api.mustache
@@ -0,0 +1,129 @@
+package {{apiPackage}}
+
+import android.content.Context
+import com.android.volley.DefaultRetryPolicy
+import com.android.volley.Request
+import com.android.volley.RequestQueue
+import com.android.volley.Response
+import com.android.volley.toolbox.BaseHttpStack
+import com.android.volley.toolbox.Volley
+import java.util.*;
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+import com.google.gson.reflect.TypeToken
+
+import {{packageName}}.request.IRequestFactory
+import {{packageName}}.request.RequestFactory
+import {{packageName}}.infrastructure.CollectionFormats.*
+
+{{#imports}}import {{import}}
+{{/imports}}
+
+{{#operations}}
+/*
+* If you wish to use a custom http stack with your client you
+* can pass that to the request queue like:
+* Volley.newRequestQueue(context.applicationContext, myCustomHttpStack)
+*/
+class {{classname}} (
+ private val context: Context,
+ private val requestQueue: Lazy = lazy(initializer = {
+ Volley.newRequestQueue(context.applicationContext)
+ }),
+ private val requestFactory: IRequestFactory = RequestFactory(),
+ private val basePath: String = "{{{basePath}}}",
+ private val postProcessors :List <(Request<*>) -> Unit> = listOf()) {
+
+ {{#operation}}
+ /**
+ * {{summary}}
+ * {{notes}}
+ {{#allParams}} * @param {{paramName}} {{description}}
+ {{/allParams}} * @return {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}
+ */
+ {{#isDeprecated}}
+ @Deprecated("This api was deprecated")
+ {{/isDeprecated}}
+ suspend fun {{operationId}}({{^allParams}}){{/allParams}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{#-last}}{{#operationAuthMethod}}, opAuthHeaderFactory = () -> map{{/operationAuthMethod}}){{/-last}}{{/allParams}}: {{#returnType}}{{{returnType}}}?{{/returnType}}{{^returnType}}Unit{{/returnType}} {
+ {{#bodyParam}}
+ val body: Any? = {{paramName}}
+ {{/bodyParam}}
+ {{^bodyParam}}
+ val body: Any? = null
+ {{/bodyParam}}
+ {{#allParams}}
+ {{#required}}
+ // verify the required parameter '{{paramName}}' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull({{paramName}})
+ {{/required}}
+ {{/allParams}}
+
+ val contentTypes : Array = arrayOf({{#consumes}}"{{{mediaType}}}"{{^-last}},{{/-last}}{{/consumes}})
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "{{{path}}}"{{#pathParams}}.replace("{" + "{{baseName}}" + "}", IRequestFactory.escapeString({{{paramName}}}.toString())){{/pathParams}};
+
+ // form params
+ val formParams = mapOf(
+ {{#formParams}}
+ "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}),
+ {{/formParams}}
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ {{#queryParams}}
+ "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}),
+ {{/queryParams}}
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ {{#headerParams}}
+ "{{baseName}}" to IRequestFactory.parameterToString({{paramName}}),
+ {{/headerParams}}
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}>() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}> = requestFactory.build(
+ Request.Method.{{httpMethod}},
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ {{/operation}}
+}
+{{/operations}}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/api_doc.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/api_doc.mustache
new file mode 100644
index 000000000000..f7bf73ae31d0
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/api_doc.mustache
@@ -0,0 +1,83 @@
+# {{classname}}{{#description}}
+{{description}}{{/description}}
+
+All URIs are relative to *{{basePath}}*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
+{{/operation}}{{/operations}}
+
+{{#operations}}
+{{#operation}}
+
+{{summary}}{{#notes}}
+
+{{notes}}{{/notes}}
+
+### Example
+```kotlin
+// Import classes:
+//import {{{packageName}}}.*
+//import {{{packageName}}}.infrastructure.*
+//import {{{modelPackage}}}.*
+
+val apiClient = ApiClient()
+{{#authMethods}}
+{{#isBasic}}
+{{#isBasicBasic}}
+apiClient.setCredentials("USERNAME", "PASSWORD")
+{{/isBasicBasic}}
+{{#isBasicBearer}}
+apiClient.setBearerToken("TOKEN")
+{{/isBasicBearer}}
+{{/isBasic}}
+{{/authMethods}}
+val webService = apiClient.createWebservice({{{classname}}}::class.java)
+{{#allParams}}
+val {{{paramName}}} : {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}}
+{{/allParams}}
+
+{{#useCoroutines}}
+launch(Dispatchers.IO) {
+{{/useCoroutines}}
+{{#useCoroutines}} {{/useCoroutines}}{{#returnType}}val result : {{{returnType}}}{{#nullableReturnType}}?{{/nullableReturnType}} = {{/returnType}}webService.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}})
+{{#useCoroutines}}
+}
+{{/useCoroutines}}
+```
+
+### Parameters
+{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}}
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}}
+{{#allParams}} **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}{{#generateModelDocs}}[**{{dataType}}**]({{baseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{dataType}}**{{/generateModelDocs}}{{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{#defaultValue}} [default to {{defaultValue}}]{{/defaultValue}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}}
+{{/allParams}}
+
+### Return type
+
+{{#returnType}}{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{#generateModelDocs}}[**{{returnType}}**]({{returnBaseType}}.md){{/generateModelDocs}}{{^generateModelDocs}}**{{returnType}}**{{/generateModelDocs}}{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}null (empty response body){{/returnType}}
+
+### Authorization
+
+{{^authMethods}}No authorization required{{/authMethods}}
+{{#authMethods}}
+{{#isBasic}}
+{{#isBasicBasic}}
+Configure {{name}}:
+ ApiClient().setCredentials("USERNAME", "PASSWORD")
+{{/isBasicBasic}}
+{{#isBasicBearer}}
+Configure {{name}}:
+ ApiClient().setBearerToken("TOKEN")
+{{/isBasicBearer}}
+{{/isBasic}}
+{{/authMethods}}
+
+### HTTP request headers
+
+ - **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}}
+ - **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}}
+
+{{/operation}}
+{{/operations}}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/apikeyauth.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/apikeyauth.mustache
new file mode 100644
index 000000000000..919da64ce3de
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/apikeyauth.mustache
@@ -0,0 +1,16 @@
+ // Api Key auth supports query header and cookie.
+ // Query is supported in the path generation only with a hardcoded value.
+ // TODO: Not sure about cookie auth form
+ // If implementing api key in query parameter use the ^isKeyInHeader property
+
+ val apiKeyAuthHeaderFactoryBuilder = {
+ paramName: String, apiKeyPrefix: String?, apiKey: String? -> {
+ mapOf(paramName to
+ if (apiKeyPrefix != null) {
+ "$apiKeyPrefix $apiKey"
+ } else {
+ apiKey!!
+ }
+ )
+ }
+ }
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/authentication.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/authentication.mustache
new file mode 100644
index 000000000000..707a8c25fe34
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/authentication.mustache
@@ -0,0 +1,15 @@
+companion object Authentication {
+ // Where a header factory requires parameters a client will need to bind these
+ // TODO Generate appropriate header factories based on settings
+ {{#authMethods}}
+{{#isApiKey}}
+{{>auth/apikeyauth}}
+{{/isApiKey}}
+{{#isBasic}}
+{{>auth/httpbasicauth}}
+{{/isBasic}}
+{{#isOAuth}}
+ // TODO: Oauth not implemented yet - comment out below as OAuth does not exist
+{{/isOAuth}}
+ {{/authMethods}}
+ }
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/httpbasicauth.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/httpbasicauth.mustache
new file mode 100644
index 000000000000..f4cf1e11fe6b
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/httpbasicauth.mustache
@@ -0,0 +1,3 @@
+val basicAuthHeaderFactoryBuilder = { username: String?, password: String? ->
+ { mapOf("Authorization" to "Basic " + Base64.encodeToString("${username ?: ""}:${password ?: ""}".toByteArray(), Base64.DEFAULT))}
+}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/oauth.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/oauth.mustache
new file mode 100644
index 000000000000..78ffc6a2d7c5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/auth/oauth.mustache
@@ -0,0 +1,5 @@
+val basicAuthHeaderFactoryBuilder = { username: String?, password: String? ->
+{
+ throw NotImplementedError("OAuth security scheme header factory not impemented yet - see open api generator auth/oauth.mustache")
+ mapOf("" to "")}
+}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/bodyParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/bodyParams.mustache
new file mode 100644
index 000000000000..886aecf65e30
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/bodyParams.mustache
@@ -0,0 +1 @@
+{{#isBodyParam}}{{{paramName}}}: {{{dataType}}}{{^required}}? = null{{/required}}{{/isBodyParam}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/build.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/build.mustache
new file mode 100644
index 000000000000..341884176a93
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/build.mustache
@@ -0,0 +1,129 @@
+{{#useAndroidMavenGradlePlugin}}
+group = '{{groupId}}'
+project.version = '{{artifactVersion}}'
+{{/useAndroidMavenGradlePlugin}}
+
+buildscript {
+
+ ext.kotlin_version = '1.5.10'
+
+ ext.swagger_annotations_version = "1.6.2"
+
+ ext.gson_version = "2.8.6"
+
+ ext.volley_version = "1.2.0"
+
+ ext.junit_version = "4.13.2"
+
+ ext.robolectric_version = "4.5.1"
+
+ ext.concurrent_unit_version = "0.4.6"
+
+ repositories {
+ mavenLocal()
+ google()
+ maven {
+ url 'https://dl.google.com/dl/android/maven2'
+ }
+ mavenCentral()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.android.tools.build:gradle:4.0.2'
+ {{#useAndroidMavenGradlePlugin}}
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
+ {{/useAndroidMavenGradlePlugin}}
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+{{#useAndroidMavenGradlePlugin}}
+apply plugin: 'com.github.dcendents.android-maven'
+{{/useAndroidMavenGradlePlugin}}
+
+android {
+ compileSdkVersion 30
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 30
+ }
+ compileOptions {
+ coreLibraryDesugaringEnabled true
+ {{#supportJava6}}
+ sourceCompatibility JavaVersion.VERSION_1_6
+ targetCompatibility JavaVersion.VERSION_1_6
+ {{/supportJava6}}
+ {{^supportJava6}}
+ {{#java8}}
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ {{/java8}}
+ {{^java8}}
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ {{/java8}}
+ {{/supportJava6}}
+ }
+ lintOptions {
+ abortOnError false
+ }
+
+ // Rename the aar correctly
+ libraryVariants.all { variant ->
+ variant.outputs.all { output ->
+ if (outputFile != null && outputFileName.endsWith('.aar')) {
+ outputFileName = "${archivesBaseName}-${version}.aar"
+ }
+ }
+ }
+
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
+}
+
+dependencies {
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation "io.swagger:swagger-annotations:$swagger_annotations_version"
+ implementation "com.google.code.gson:gson:$gson_version"
+ implementation "com.android.volley:volley:${volley_version}"
+ testImplementation "junit:junit:$junit_version"
+ testImplementation "org.robolectric:robolectric:${robolectric_version}"
+ testImplementation "net.jodah:concurrentunit:${concurrent_unit_version}"
+ {{#generateRoomModels}}
+ annotationProcessor "androidx.room:room-runtime:2.3.0"
+ implementation "androidx.room:room-runtime:2.3.0"
+ {{/generateRoomModels}}
+}
+
+afterEvaluate {
+ android.libraryVariants.all { variant ->
+ def task = project.tasks.create "jar${variant.name.capitalize()}", Jar
+ task.description = "Create jar artifact for ${variant.name}"
+ task.dependsOn variant.javaCompile
+ task.from variant.javaCompile.destinationDir
+ task.destinationDirectory = project.file("${project.buildDir}/outputs/jar")
+ task.archiveFileName = "${project.name}-${variant.baseName}-${version}.jar"
+ artifacts.add('archives', task);
+ }
+}
+
+{{#useAndroidMavenGradlePlugin}}
+task sourcesJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ classifier = 'sources'
+}
+
+artifacts {
+ archives sourcesJar
+}
+{{/useAndroidMavenGradlePlugin}}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/formParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/formParams.mustache
new file mode 100644
index 000000000000..62de9c638183
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/formParams.mustache
@@ -0,0 +1 @@
+{{#isFormParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isFormParam}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/gradle.properties.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/gradle.properties.mustache
new file mode 100644
index 000000000000..8cbe6270192c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/gradle.properties.mustache
@@ -0,0 +1,4 @@
+{{#generateRoomModels}}
+android.useAndroidX=true
+android.enableJetifier=true
+{{/generateRoomModels}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/headerParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/headerParams.mustache
new file mode 100644
index 000000000000..4226cf9b4b9a
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/headerParams.mustache
@@ -0,0 +1 @@
+{{#isHeaderParam}}{{{paramName}}}: {{{dataType}}}{{#required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}}{{^required}}?{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{^defaultValue}} = null{{/defaultValue}}{{/required}}{{/isHeaderParam}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache
new file mode 100644
index 000000000000..659f2df4851a
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/infrastructure/CollectionFormats.kt.mustache
@@ -0,0 +1,56 @@
+package {{packageName}}.infrastructure
+
+class CollectionFormats {
+
+ open class CSVParams {
+
+ var params: List
+
+ constructor(params: List) {
+ this.params = params
+ }
+
+ constructor(vararg params: String) {
+ this.params = listOf(*params)
+ }
+
+ override fun toString(): String {
+ return params.joinToString(",")
+ }
+ }
+
+ open class SSVParams : CSVParams {
+
+ constructor(params: List) : super(params)
+
+ constructor(vararg params: String) : super(*params)
+
+ override fun toString(): String {
+ return params.joinToString(" ")
+ }
+ }
+
+ class TSVParams : CSVParams {
+
+ constructor(params: List) : super(params)
+
+ constructor(vararg params: String) : super(*params)
+
+ override fun toString(): String {
+ return params.joinToString("\t")
+ }
+ }
+
+ class PIPESParams : CSVParams {
+
+ constructor(params: List) : super(params)
+
+ constructor(vararg params: String) : super(*params)
+
+ override fun toString(): String {
+ return params.joinToString("|")
+ }
+ }
+
+ class SPACEParams : SSVParams()
+}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache
new file mode 100644
index 000000000000..83b5fa369ab8
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/infrastructure/ITransformForStorage.mustache
@@ -0,0 +1,9 @@
+{{>licenseInfo}}
+package {{packageName}}.infrastructure
+
+import {{roomModelPackage}}.*
+
+// TODO ITransformForStorage
+interface ITransformForStorage {
+ fun toRoomModel(): T
+}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/manifest.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/manifest.mustache
new file mode 100644
index 000000000000..1ea918bad732
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/manifest.mustache
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/pathParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/pathParams.mustache
new file mode 100644
index 000000000000..12deb9aac19c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/pathParams.mustache
@@ -0,0 +1 @@
+{{#isPathParam}}{{{paramName}}}: {{{dataType}}}{{/isPathParam}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/queryParams.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/queryParams.mustache
new file mode 100644
index 000000000000..9ab185315111
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/queryParams.mustache
@@ -0,0 +1 @@
+{{#isQueryParam}}{{{paramName}}}: {{#collectionFormat}}{{#isCollectionFormatMulti}}{{{dataType}}}{{/isCollectionFormatMulti}}{{^isCollectionFormatMulti}}{{{collectionFormat.toUpperCase}}}Params{{/isCollectionFormatMulti}}{{/collectionFormat}}{{^collectionFormat}}{{{dataType}}}{{/collectionFormat}}{{^required}}? = null{{/required}}{{/isQueryParam}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/GsonRequest.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/GsonRequest.mustache
new file mode 100644
index 000000000000..e87f7413aab3
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/GsonRequest.mustache
@@ -0,0 +1,119 @@
+package {{packageName}}.request
+
+import com.android.volley.NetworkResponse
+import com.android.volley.ParseError
+import com.android.volley.Request
+import com.android.volley.Response
+import com.android.volley.toolbox.HttpHeaderParser
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonSyntaxException
+import java.io.UnsupportedEncodingException
+import java.nio.charset.Charset
+import java.net.HttpURLConnection
+import java.lang.reflect.Type
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.OffsetDateTime
+
+import {{packageName}}.infrastructure.OffsetDateTimeAdapter
+import {{packageName}}.infrastructure.LocalDateTimeAdapter
+import {{packageName}}.infrastructure.LocalDateAdapter
+import {{packageName}}.infrastructure.ByteArrayAdapter
+
+class GsonRequest(
+ method: Int,
+ url: String,
+ private val body: Any?,
+ private val headers: Map?,
+ private val params: MutableMap?,
+ private val contentTypeForBody: String?,
+ private val encodingForParams: String?,
+ private val gsonAdapters: Map?,
+ private val type: Type,
+ private val listener: Response.Listener,
+ errorListener: Response.ErrorListener
+) : Request(method, url, errorListener) {
+
+ val gsonBuilder: GsonBuilder = GsonBuilder()
+ .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter())
+ .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
+ .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
+ .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter())
+ .apply {
+ gsonAdapters?.forEach {
+ this.registerTypeAdapter(it.key, it.value)
+ }
+ }
+
+ val gson: Gson by lazy {
+ gsonBuilder.create()
+ }
+
+ private var response: NetworkResponse? = null
+
+ override fun deliverResponse(response: T?) {
+ listener.onResponse(response)
+ }
+
+ override fun getParams(): MutableMap? = params ?: super.getParams()
+
+ override fun getBodyContentType(): String = contentTypeForBody ?: super.getBodyContentType()
+
+ override fun getParamsEncoding(): String = encodingForParams ?: super.getParamsEncoding()
+
+ override fun getHeaders(): MutableMap {
+ val combined = HashMap()
+ combined.putAll(super.getHeaders())
+ if (headers != null) {
+ combined.putAll(headers)
+ }
+ return combined
+ }
+
+ override fun getBody(): ByteArray? {
+ if (body != null) {
+ return gson.toJson(body).toByteArray(Charsets.UTF_8)
+ }
+ return super.getBody()
+ }
+
+ override fun parseNetworkResponse(response: NetworkResponse?): Response {
+ return try {
+ this.response = copyTo(response)
+ val json = String(
+ response?.data ?: ByteArray(0),
+ Charset.forName(HttpHeaderParser.parseCharset(response?.headers))
+ )
+ Response.success(
+ gson.fromJson(json, type),
+ HttpHeaderParser.parseCacheHeaders(response)
+ )
+ } catch (e: UnsupportedEncodingException) {
+ Response.error(ParseError(e))
+ } catch (e: JsonSyntaxException) {
+ Response.error(ParseError(e))
+ }
+ }
+
+ private fun copyTo(response: NetworkResponse?): NetworkResponse {
+ return if (response != null) {
+ NetworkResponse(
+ response.statusCode,
+ response.data,
+ response.notModified,
+ response.networkTimeMs,
+ response.allHeaders
+ )
+ } else {
+ // Return an empty response.
+ NetworkResponse(
+ HttpURLConnection.HTTP_BAD_METHOD,
+ ByteArray(0),
+ false,
+ 0,
+ emptyList()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/IRequestFactory.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/IRequestFactory.mustache
new file mode 100644
index 000000000000..d46fe90c6202
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/IRequestFactory.mustache
@@ -0,0 +1,64 @@
+package {{packageName}}.request
+
+import com.android.volley.Request
+import com.android.volley.Response
+import java.io.UnsupportedEncodingException
+import java.lang.reflect.Type
+import java.net.URLEncoder
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+import java.time.format.DateTimeFormatter
+import java.time.OffsetDateTime
+import java.time.LocalDate
+
+
+interface IRequestFactory {
+
+ companion object {
+ /**
+ * ISO 8601 date time format.
+ * @see https://en.wikipedia.org/wiki/ISO_8601
+ */
+ fun formatDateTime(datetime: OffsetDateTime) = DateTimeFormatter.ISO_INSTANT.format(datetime)
+ fun formatDate(date: LocalDate) = DateTimeFormatter.ISO_LOCAL_DATE.format(date)
+
+ fun escapeString(str: String): String {
+ return try {
+ URLEncoder.encode(str, "UTF-8")
+ } catch (e: UnsupportedEncodingException) {
+ str
+ }
+ }
+
+ fun parameterToString(param: Any?) =
+ when (param) {
+ null -> ""
+ is OffsetDateTime -> formatDateTime(param)
+ is Collection<*> -> {
+ val b = StringBuilder()
+ for (o in param) {
+ if (b.isNotEmpty()) {
+ b.append(",")
+ }
+ b.append(o.toString())
+ }
+ b.toString()
+ }
+ else -> param.toString()
+ }
+ }
+
+
+ fun build(
+ method: Int,
+ url : String,
+ body: Any?,
+ headers: Map?,
+ queryParams: Map?,
+ formParams: Map?,
+ contentTypeForBody: String?,
+ type: Type,
+ responseListener: Response.Listener,
+ errorListener: Response.ErrorListener): Request
+}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/RequestFactory.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/RequestFactory.mustache
new file mode 100644
index 000000000000..e9bc5d21283a
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-volley/request/RequestFactory.mustache
@@ -0,0 +1,69 @@
+// Knowing the details of an operation it will produce a call to a Volley Request constructor
+package {{packageName}}.request
+
+
+import com.android.volley.Request
+import com.android.volley.Response
+{{#hasAuthMethods}}
+import android.util.Base64
+{{/hasAuthMethods}}
+import {{packageName}}.request.IRequestFactory.Companion.escapeString
+import java.lang.reflect.Type
+import java.util.Locale
+import java.util.UUID
+
+class RequestFactory(private val headerFactories : List<() -> Map> = listOf(), private val postProcessors :List <(Request<*>) -> Unit> = listOf(), private val gsonAdapters: Map = mapOf()): IRequestFactory {
+{{#hasAuthMethods}}
+
+ {{>auth/authentication}}
+{{/hasAuthMethods}}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Suppress("UNCHECKED_CAST")
+ override fun build(
+ method: Int,
+ url: String,
+ body: Any?,
+ headers: Map?,
+ queryParams: Map?,
+ formParams: Map?,
+ contentTypeForBody: String?,
+ type: Type,
+ responseListener: Response.Listener,
+ errorListener: Response.ErrorListener
+ ): Request {
+ val afterMarketHeaders = (headers?.toMutableMap() ?: mutableMapOf())
+ // Factory built and aftermarket
+ // Merge the after market headers on top of the base ones in case we are overriding per call auth
+ val allHeaders = headerFactories.fold(afterMarketHeaders) { acc, factory -> (acc + factory.invoke()).toMutableMap() }
+
+ // If we decide to support auth parameters in the url, then you will reference them by supplying a url string
+ // with known variable name refernces in the string. We will then apply
+ val updatedUrl = if (!queryParams.isNullOrEmpty()) {
+ queryParams.asSequence().fold("$url?") {acc, param ->
+ "$acc${escapeString(param.key)}=${escapeString(param.value)}&"
+ }.trimEnd('&')
+ } else {
+ url
+ }
+
+ val request = GsonRequest(
+ method,
+ updatedUrl,
+ body,
+ allHeaders,
+ formParams?.toMutableMap(),
+ contentTypeForBody,
+ null,
+ gsonAdapters,
+ type,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ return request
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/model_room.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/model_room.mustache
new file mode 100644
index 000000000000..bb43e6bf87ec
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/model_room.mustache
@@ -0,0 +1,38 @@
+{{>licenseInfo}}
+package {{roomModelPackage}}
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import {{modelPackage}}.*
+
+{{#models}}
+{{#model}}
+
+@Entity(tableName = "{{classname}}")
+/**
+* Room model for {{{description}}}
+{{#allVars}}
+* @param {{{name}}} {{{description}}}
+{{/allVars}}
+*/
+data class {{classname}}RoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+ {{#allVars}}{{#items.isPrimitiveType}}var {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInCamelCase}}}{{/items.isEnum}}>{{>model_room_init_var}}{{/isArray}},
+ {{/items.isPrimitiveType}}{{/allVars}}
+ {{#allVars}}{{^isEnum}}{{^isArray}}var {{{name}}}: {{{dataType}}}{{>model_room_init_var}},
+ {{/isArray}}{{/isEnum}}{{/allVars}}
+ {{#allVars}}{{#isEnum}}{{^isArray}}var {{{name}}}: {{classname}}.{{{nameInCamelCase}}}{{>model_room_init_var}},
+ {{/isArray}}{{/isEnum}}{{/allVars}}) {
+{{#allVars}}{{#isArray}}{{#isList}}{{^items.isPrimitiveType}}
+ @Ignore
+ {{^isNullable}}{{#required}}lateinit {{/required}}{{/isNullable}}var {{{name}}}: {{#isArray}}{{#isList}}kotlin.collections.List{{/isList}}{{^isList}}kotlin.Array{{/isList}}<{{^items.isEnum}}{{^items.isPrimitiveType}}{{^items.isModel}}{{/items.isModel}}{{/items.isPrimitiveType}}{{{items.dataType}}}{{/items.isEnum}}{{#items.isEnum}}{{classname}}.{{{nameInCamelCase}}}{{/items.isEnum}}>{{>model_room_init_var}}{{/isArray}}
+{{/items.isPrimitiveType}}{{/isList}}{{/isArray}}{{/allVars}}
+ {{^discriminator}}companion object { }
+
+ fun toApiModel(): {{classname}} = {{classname}}(
+ {{#allVars}}{{name}} = this.{{name}},
+ {{/allVars}}){{/discriminator}}
+}
+{{/model}}
+{{/models}}
diff --git a/modules/openapi-generator/src/main/resources/kotlin-client/model_room_init_var.mustache b/modules/openapi-generator/src/main/resources/kotlin-client/model_room_init_var.mustache
new file mode 100644
index 000000000000..01896d2439f6
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/kotlin-client/model_room_init_var.mustache
@@ -0,0 +1 @@
+{{#isNullable}}?{{/isNullable}}{{^required}}?{{/required}}{{#defaultvalue}} = {{defaultvalue}}{{/defaultvalue}}{{^defaultvalue}}{{#isNullable}} = null{{/isNullable}}{{^required}} = null{{/required}}{{/defaultvalue}}
\ No newline at end of file
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/TestUtils.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/TestUtils.java
index e82c584a4c59..7c7ee64361d4 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/TestUtils.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/TestUtils.java
@@ -187,4 +187,14 @@ public static void assertFileNotContains(Path path, String... lines) {
for (String line : lines)
assertFalse(file.contains(linearize(line)));
}
+
+ public static void assertFileNotExists(Path path) {
+ try {
+ new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+ fail("File exists when it should not: " + path);
+ } catch (IOException e) {
+ // File exists, pass.
+ assertTrue(true);
+ }
+ }
}
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/jvm_volley/KotlinJvmVolleyModelCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/jvm_volley/KotlinJvmVolleyModelCodegenTest.java
new file mode 100644
index 000000000000..6473e036d4d7
--- /dev/null
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/jvm_volley/KotlinJvmVolleyModelCodegenTest.java
@@ -0,0 +1,83 @@
+package org.openapitools.codegen.kotlin.jvm_volley;
+
+import io.swagger.parser.OpenAPIParser;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.parser.core.models.ParseOptions;
+import org.openapitools.codegen.ClientOptInput;
+import org.openapitools.codegen.CodegenConstants;
+import org.openapitools.codegen.DefaultGenerator;
+import org.openapitools.codegen.languages.AbstractKotlinCodegen;
+import org.openapitools.codegen.languages.KotlinClientCodegen;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import static org.openapitools.codegen.TestUtils.*;
+
+public class KotlinJvmVolleyModelCodegenTest {
+
+ @Test
+ public void modelsWithRoomModels() throws IOException {
+ KotlinClientCodegen codegen = new KotlinClientCodegen();
+ codegen.additionalProperties().put(KotlinClientCodegen.GENERATE_ROOM_MODELS, true);
+ codegen.additionalProperties().put(KotlinClientCodegen.ROOM_MODEL_PACKAGE, "models.room");
+ codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, AbstractKotlinCodegen.SERIALIZATION_LIBRARY_TYPE.gson.name());
+
+ String outputPath = checkModel(codegen, false);
+
+ assertFileContains(Paths.get(outputPath + "/src/main/java/models/room/BigDogRoomModel.kt"), "toApiModel()");
+ assertFileContains(Paths.get(outputPath + "/src/main/java/models/BigDog.kt"), "toRoomModel()");
+ }
+
+ @Test
+ public void modelsWithoutRoomModels() throws IOException {
+ KotlinClientCodegen codegen = new KotlinClientCodegen();
+ codegen.additionalProperties().put(KotlinClientCodegen.GENERATE_ROOM_MODELS, false);
+ codegen.additionalProperties().put(CodegenConstants.SERIALIZATION_LIBRARY, AbstractKotlinCodegen.SERIALIZATION_LIBRARY_TYPE.gson.name());
+
+ String outputPath = checkModel(codegen, false);
+
+ assertFileNotExists(Paths.get(outputPath + "/src/main/java/models/room/BigDogRoomModel.kt"));
+ assertFileContains(Paths.get(outputPath + "/src/main/java/models/BigDog.kt"));
+ assertFileNotContains(Paths.get(outputPath + "/src/main/java/models/BigDog.kt"), "toRoomModel()");
+ }
+
+ private String checkModel(AbstractKotlinCodegen codegen, boolean mutable, String... props) throws IOException {
+ String outputPath = generateModels(codegen, "src/test/resources/3_0/generic.yaml", mutable);
+ assertFileContains(Paths.get(outputPath + "/src/main/java/models/Animal.kt"), props);
+ return outputPath;
+ }
+
+ private String generateModels(AbstractKotlinCodegen codegen, String fileName, boolean mutable) throws IOException {
+ File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+ String outputPath = output.getAbsolutePath().replace('\\', '/');
+
+ OpenAPI openAPI = new OpenAPIParser()
+ .readLocation(fileName, null, new ParseOptions()).getOpenAPI();
+ codegen.setOutputDir(output.getAbsolutePath());
+ codegen.setLibrary("jvm-volley");
+
+ codegen.additionalProperties().put(CodegenConstants.MODEL_PACKAGE, "models");
+ codegen.additionalProperties().put(AbstractKotlinCodegen.MODEL_MUTABLE, mutable);
+
+ ClientOptInput input = new ClientOptInput()
+ .openAPI(openAPI)
+ .config(codegen);
+
+ DefaultGenerator generator = new DefaultGenerator();
+
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true");
+
+ generator.opts(input).generate();
+
+ return outputPath;
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/.openapi-generator-ignore b/samples/client/petstore/kotlin-jvm-volley/.openapi-generator-ignore
new file mode 100644
index 000000000000..7484ee590a38
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/.openapi-generator-ignore
@@ -0,0 +1,23 @@
+# OpenAPI Generator Ignore
+# Generated by openapi-generator https://github.com/openapitools/openapi-generator
+
+# 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 OpenAPI Generator 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/petstore/kotlin-jvm-volley/.openapi-generator/FILES b/samples/client/petstore/kotlin-jvm-volley/.openapi-generator/FILES
new file mode 100644
index 000000000000..1de427bbcb2f
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/.openapi-generator/FILES
@@ -0,0 +1,43 @@
+README.md
+build.gradle
+docs/ApiResponse.md
+docs/Category.md
+docs/Order.md
+docs/Pet.md
+docs/PetApi.md
+docs/StoreApi.md
+docs/Tag.md
+docs/User.md
+docs/UserApi.md
+gradle.properties
+gradle/wrapper/gradle-wrapper.jar
+gradle/wrapper/gradle-wrapper.properties
+gradlew
+gradlew.bat
+settings.gradle
+src/main/AndroidManifest.xml
+src/main/java/org/openapitools/client/apis/PetApi.kt
+src/main/java/org/openapitools/client/apis/StoreApi.kt
+src/main/java/org/openapitools/client/apis/UserApi.kt
+src/main/java/org/openapitools/client/infrastructure/ByteArrayAdapter.kt
+src/main/java/org/openapitools/client/infrastructure/CollectionFormats.kt
+src/main/java/org/openapitools/client/infrastructure/CollectionFormats.kt
+src/main/java/org/openapitools/client/infrastructure/ITransformForStorage.kt
+src/main/java/org/openapitools/client/infrastructure/LocalDateAdapter.kt
+src/main/java/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt
+src/main/java/org/openapitools/client/infrastructure/OffsetDateTimeAdapter.kt
+src/main/java/org/openapitools/client/models/Category.kt
+src/main/java/org/openapitools/client/models/ModelApiResponse.kt
+src/main/java/org/openapitools/client/models/Order.kt
+src/main/java/org/openapitools/client/models/Pet.kt
+src/main/java/org/openapitools/client/models/Tag.kt
+src/main/java/org/openapitools/client/models/User.kt
+src/main/java/org/openapitools/client/models/room/CategoryRoomModel.kt
+src/main/java/org/openapitools/client/models/room/ModelApiResponseRoomModel.kt
+src/main/java/org/openapitools/client/models/room/OrderRoomModel.kt
+src/main/java/org/openapitools/client/models/room/PetRoomModel.kt
+src/main/java/org/openapitools/client/models/room/TagRoomModel.kt
+src/main/java/org/openapitools/client/models/room/UserRoomModel.kt
+src/main/java/org/openapitools/client/request/GsonRequest.kt
+src/main/java/org/openapitools/client/request/IRequestFactory.kt
+src/main/java/org/openapitools/client/request/RequestFactory.kt
diff --git a/samples/client/petstore/kotlin-jvm-volley/.openapi-generator/VERSION b/samples/client/petstore/kotlin-jvm-volley/.openapi-generator/VERSION
new file mode 100644
index 000000000000..4077803655c0
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/.openapi-generator/VERSION
@@ -0,0 +1 @@
+5.3.1-SNAPSHOT
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-jvm-volley/README.md b/samples/client/petstore/kotlin-jvm-volley/README.md
new file mode 100644
index 000000000000..9350405a0f57
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/README.md
@@ -0,0 +1,230 @@
+# org.openapitools.client - Kotlin client library for OpenAPI Petstore
+
+
+A kotlin client for Android using the currently recommended http client, Volley. See https://developer.android.com/training/volley
+
+- Currently sends GsonRequests
+- Currently only supports Gson as a serializer - will throw an exception if a different serializer is chosen
+- Defaults the source location to src/main/java as per standard Android builds
+
+
+## Design
+
+Volley is a queue/request based layer on top of http url stack specific to Android. Android favours dependency injection and
+a layered architecture, and IO performed off the main thread to maintain UI responsiveness, with a preferred technique of
+kotlin co-routines. The code gen library reflects these factors.
+
+- Api calls use co-routines, and execute them using volley callbacks to avoid tying up a thread.
+- Facilitate dependency injection, with default implementations available.
+- Generate a requestFactory that can be overridden
+- Allow the passing of the RequestFactory per tag (api client) or per operation (an extra parameter is created on operations with non-global security), with per operation auth overriding global security.
+- DI scoping of the Request Factory and pre-generated auth header factories allow for thread safe and secure setting of credentials.
+- Lazy header factories allow for refreshing tokens etc
+- Factoring of header factories to the Request Factory allow ambient provision of credentials. Code gen library is credential storage agnostic.
+- Header factories allow the merging of generated headers from open api spec with dynamically added headers
+
+- Injection of http url stack to allow custom http stacks. Default implementation is best practice singleton
+- Data classes used for serialisation to reflect volley's preference - an immutable request that once queued can't be tampered with.
+
+- Reuse model class and other jvm common infrastructure
+
+- Optional generation of room database models, and transform methods to these from open api models
+- Room and api models can be extended with additional extension properties.
+
+## Future improvements
+- Option to generate image requests on certain conditionals e.g content-type gif etc
+- Support for kotlin serialization.
+- Multi part form parameters and support for file inputs
+
+## Usage
+Hilt Dependency injection example - with default values for parameters overridden.
+```
+ @Provides
+ internal fun provideSomeApi(
+ context: Context,
+ restService: IRestService,
+ configurationService: IConfigurationService,
+ sessionService: ISessionService
+ ): SomeApi {
+ return SomeApi(
+ context = context,
+ requestQueue = restService.getRequestQueue(),
+ requestFactory = RequestFactory(listOf(createSessionHeaderFactory(sessionService), createTraceHeaderFactory()),
+ postProcessors = listOf(retryPolicySetter)),
+ basePath = configurationService.getBaseUrl()
+ )
+ }
+```
+Here is the constructor so you can see the defaults
+```class SomeApi (
+val context: Context,
+val requestQueue: Lazy = lazy(initializer = {
+ Volley.newRequestQueue(context.applicationContext)
+ }),
+ val requestFactory: IRequestFactory = RequestFactory(),
+ val basePath: String = "https://yourbasepath.from_input_parameter.com/api",
+ private val postProcessors :List <(Request<*>) -> Unit> = listOf()) {
+```
+
+### Overriding defaults
+The above constructor for each api allows the following to be customized
+- A custom context, so either a singleton request queue or different scope can be created - see
+https://developer.android.com/training/volley/requestqueue#singleton
+- An overrideable request queue - which in turn can have a custom http url stack passed to it
+- An overrideable request factory constructor call, or a request factory that can be overridden by a custom template, with
+custom header factory, request post processors and custom gson adapters injected.
+
+#### Overriding request generation
+Request generation can be overridden by
+- Overriding the entire request factory template
+- Supplying custom header factories - methods that take any possible parameters but return a map of headers
+- Supplying custom request post processors - methods that take and return the request object
+
+Header factory examples can be found in the auth section, as these are implemented as header factories. eg
+```
+val basicAuthHeaderFactoryBuilder = { username: String?, password: String? ->
+{ mapOf("Authorization" to "Basic " + Base64.encodeToString("${username ?: ""}:${password ?: ""}".toByteArray(), Base64.DEFAULT))}
+}
+```
+In this case it's a lambda function (a factory method) that takes an username and password, and returns a map of headers. Other
+generated code will supply the username and password. In this case it results in a map of just one key/value pair, but
+it could be multiple. The important part is it's returning a map - and that the surrounding code
+will can bind the inputs to it at some point.
+
+Here is a different example that supplies tracing header values
+```
+/**
+ * Create a lambda of tracing headers to be injected into an API's [RequestFactory].
+ */
+private fun createTraceHeaderFactory(): () -> Map = {
+ mapOf(
+ HttpHeaderType.b3_traceId.rawValue to UUIDExtensions.asTraceId(UUID.randomUUID()),
+ HttpHeaderType.b3_spanId.rawValue to UUIDExtensions.asSpanId(UUID.randomUUID()),
+ HttpHeaderType.b3_sampled.rawValue to "1"
+ )
+}
+```
+Finally a post processor example
+```
+ /**
+ * Configure a [DefaultRetryPolicy] to be injected into the [RequestFactory] with a maximum number of retries of zero.
+ */
+ private val retryPolicySetter = { request: Request<*> ->
+ Unit.apply {
+ request.setRetryPolicy(
+ DefaultRetryPolicy(
+ RestService.DEFAULT_TIMEOUT_MS,
+ 0,
+ DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
+ )
+ )
+ }
+ }
+```
+
+### Serialization
+#### Gson and Polymorphic types
+The GsonRequest object can be passed custom type adapters
+```
+class GsonRequest(
+ method: Int,
+ url: String,
+ private val body: Any?,
+ private val headers: Map?,
+ private val params: MutableMap?,
+ private val contentTypeForBody: String?,
+ private val encodingForParams: String?,
+ private val gsonAdapters: Map?,
+ private val type: Type,
+ private val listener: Response.Listener,
+ errorListener: Response.ErrorListener
+) : Request(method, url, errorListener) {
+
+ val gsonBuilder: GsonBuilder = GsonBuilder()
+ .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter())
+ .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
+ .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
+ .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter())
+
+```
+## Requires
+
+* Kotlin 1.4.30
+* Gradle 6.8.3
+
+## Build
+
+First, create the gradle wrapper script:
+
+```
+gradle wrapper
+```
+
+Then, run:
+
+```
+./gradlew check assemble
+```
+
+This runs all tests and packages the library.
+
+
+## Documentation for API Endpoints
+
+All URIs are relative to *http://petstore.swagger.io/v2*
+
+Class | Method | HTTP request | Description
+------------ | ------------- | ------------- | -------------
+*PetApi* | [**addPet**](docs/PetApi.md#addpet) | **POST** /pet | Add a new pet to the store
+*PetApi* | [**deletePet**](docs/PetApi.md#deletepet) | **DELETE** /pet/{petId} | Deletes a pet
+*PetApi* | [**findPetsByStatus**](docs/PetApi.md#findpetsbystatus) | **GET** /pet/findByStatus | Finds Pets by status
+*PetApi* | [**findPetsByTags**](docs/PetApi.md#findpetsbytags) | **GET** /pet/findByTags | Finds Pets by tags
+*PetApi* | [**getPetById**](docs/PetApi.md#getpetbyid) | **GET** /pet/{petId} | Find pet by ID
+*PetApi* | [**updatePet**](docs/PetApi.md#updatepet) | **PUT** /pet | Update an existing pet
+*PetApi* | [**updatePetWithForm**](docs/PetApi.md#updatepetwithform) | **POST** /pet/{petId} | Updates a pet in the store with form data
+*PetApi* | [**uploadFile**](docs/PetApi.md#uploadfile) | **POST** /pet/{petId}/uploadImage | uploads an image
+*StoreApi* | [**deleteOrder**](docs/StoreApi.md#deleteorder) | **DELETE** /store/order/{orderId} | Delete purchase order by ID
+*StoreApi* | [**getInventory**](docs/StoreApi.md#getinventory) | **GET** /store/inventory | Returns pet inventories by status
+*StoreApi* | [**getOrderById**](docs/StoreApi.md#getorderbyid) | **GET** /store/order/{orderId} | Find purchase order by ID
+*StoreApi* | [**placeOrder**](docs/StoreApi.md#placeorder) | **POST** /store/order | Place an order for a pet
+*UserApi* | [**createUser**](docs/UserApi.md#createuser) | **POST** /user | Create user
+*UserApi* | [**createUsersWithArrayInput**](docs/UserApi.md#createuserswitharrayinput) | **POST** /user/createWithArray | Creates list of users with given input array
+*UserApi* | [**createUsersWithListInput**](docs/UserApi.md#createuserswithlistinput) | **POST** /user/createWithList | Creates list of users with given input array
+*UserApi* | [**deleteUser**](docs/UserApi.md#deleteuser) | **DELETE** /user/{username} | Delete user
+*UserApi* | [**getUserByName**](docs/UserApi.md#getuserbyname) | **GET** /user/{username} | Get user by user name
+*UserApi* | [**loginUser**](docs/UserApi.md#loginuser) | **GET** /user/login | Logs user into the system
+*UserApi* | [**logoutUser**](docs/UserApi.md#logoutuser) | **GET** /user/logout | Logs out current logged in user session
+*UserApi* | [**updateUser**](docs/UserApi.md#updateuser) | **PUT** /user/{username} | Updated user
+
+
+
+## Documentation for Models
+
+ - [org.openapitools.client.models.Category](docs/Category.md)
+ - [org.openapitools.client.models.ModelApiResponse](docs/ModelApiResponse.md)
+ - [org.openapitools.client.models.Order](docs/Order.md)
+ - [org.openapitools.client.models.Pet](docs/Pet.md)
+ - [org.openapitools.client.models.Tag](docs/Tag.md)
+ - [org.openapitools.client.models.User](docs/User.md)
+
+
+
+## Documentation for Authorization
+
+
+### api_key
+
+- **Type**: API key
+- **API key parameter name**: api_key
+- **Location**: HTTP header
+
+
+### petstore_auth
+
+- **Type**: OAuth
+- **Flow**: implicit
+- **Authorization URL**: http://petstore.swagger.io/api/oauth/dialog
+- **Scopes**:
+ - write:pets: modify pets in your account
+ - read:pets: read your pets
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/build.gradle b/samples/client/petstore/kotlin-jvm-volley/build.gradle
new file mode 100644
index 000000000000..6a369e599487
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/build.gradle
@@ -0,0 +1,95 @@
+
+buildscript {
+
+ ext.kotlin_version = '1.5.10'
+
+ ext.swagger_annotations_version = "1.6.2"
+
+ ext.gson_version = "2.8.6"
+
+ ext.volley_version = "1.2.0"
+
+ ext.junit_version = "4.13.2"
+
+ ext.robolectric_version = "4.5.1"
+
+ ext.concurrent_unit_version = "0.4.6"
+
+ repositories {
+ mavenLocal()
+ google()
+ maven {
+ url 'https://dl.google.com/dl/android/maven2'
+ }
+ mavenCentral()
+ }
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.android.tools.build:gradle:4.0.2'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 30
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 30
+ }
+ compileOptions {
+ coreLibraryDesugaringEnabled true
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ lintOptions {
+ abortOnError false
+ }
+
+ // Rename the aar correctly
+ libraryVariants.all { variant ->
+ variant.outputs.all { output ->
+ if (outputFile != null && outputFileName.endsWith('.aar')) {
+ outputFileName = "${archivesBaseName}-${version}.aar"
+ }
+ }
+ }
+
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
+}
+
+dependencies {
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation "io.swagger:swagger-annotations:$swagger_annotations_version"
+ implementation "com.google.code.gson:gson:$gson_version"
+ implementation "com.android.volley:volley:${volley_version}"
+ testImplementation "junit:junit:$junit_version"
+ testImplementation "org.robolectric:robolectric:${robolectric_version}"
+ testImplementation "net.jodah:concurrentunit:${concurrent_unit_version}"
+ annotationProcessor "androidx.room:room-runtime:2.3.0"
+ implementation "androidx.room:room-runtime:2.3.0"
+}
+
+afterEvaluate {
+ android.libraryVariants.all { variant ->
+ def task = project.tasks.create "jar${variant.name.capitalize()}", Jar
+ task.description = "Create jar artifact for ${variant.name}"
+ task.dependsOn variant.javaCompile
+ task.from variant.javaCompile.destinationDir
+ task.destinationDirectory = project.file("${project.buildDir}/outputs/jar")
+ task.archiveFileName = "${project.name}-${variant.baseName}-${version}.jar"
+ artifacts.add('archives', task);
+ }
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/ApiResponse.md b/samples/client/petstore/kotlin-jvm-volley/docs/ApiResponse.md
new file mode 100644
index 000000000000..12f08d5cdef0
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/ApiResponse.md
@@ -0,0 +1,12 @@
+
+# ModelApiResponse
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**code** | **kotlin.Int** | | [optional]
+**type** | **kotlin.String** | | [optional]
+**message** | **kotlin.String** | | [optional]
+
+
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/Category.md b/samples/client/petstore/kotlin-jvm-volley/docs/Category.md
new file mode 100644
index 000000000000..2c28a670fc79
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/Category.md
@@ -0,0 +1,11 @@
+
+# Category
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**id** | **kotlin.Long** | | [optional]
+**name** | **kotlin.String** | | [optional]
+
+
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/Order.md b/samples/client/petstore/kotlin-jvm-volley/docs/Order.md
new file mode 100644
index 000000000000..94ab0d537e54
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/Order.md
@@ -0,0 +1,22 @@
+
+# Order
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**id** | **kotlin.Long** | | [optional]
+**petId** | **kotlin.Long** | | [optional]
+**quantity** | **kotlin.Int** | | [optional]
+**shipDate** | [**java.time.OffsetDateTime**](java.time.OffsetDateTime.md) | | [optional]
+**status** | [**inline**](#Status) | Order Status | [optional]
+**complete** | **kotlin.Boolean** | | [optional]
+
+
+
+## Enum: status
+Name | Value
+---- | -----
+status | placed, approved, delivered
+
+
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/Pet.md b/samples/client/petstore/kotlin-jvm-volley/docs/Pet.md
new file mode 100644
index 000000000000..bc3dd89718f0
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/Pet.md
@@ -0,0 +1,22 @@
+
+# Pet
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**name** | **kotlin.String** | |
+**photoUrls** | **kotlin.collections.List<kotlin.String>** | |
+**id** | **kotlin.Long** | | [optional]
+**category** | [**Category**](Category.md) | | [optional]
+**tags** | [**kotlin.collections.List<Tag>**](Tag.md) | | [optional]
+**status** | [**inline**](#Status) | pet status in the store | [optional]
+
+
+
+## Enum: status
+Name | Value
+---- | -----
+status | available, pending, sold
+
+
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/PetApi.md b/samples/client/petstore/kotlin-jvm-volley/docs/PetApi.md
new file mode 100644
index 000000000000..529441d7dfd9
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/PetApi.md
@@ -0,0 +1,320 @@
+# PetApi
+
+All URIs are relative to *http://petstore.swagger.io/v2*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**addPet**](PetApi.md#addPet) | **POST** /pet | Add a new pet to the store
+[**deletePet**](PetApi.md#deletePet) | **DELETE** /pet/{petId} | Deletes a pet
+[**findPetsByStatus**](PetApi.md#findPetsByStatus) | **GET** /pet/findByStatus | Finds Pets by status
+[**findPetsByTags**](PetApi.md#findPetsByTags) | **GET** /pet/findByTags | Finds Pets by tags
+[**getPetById**](PetApi.md#getPetById) | **GET** /pet/{petId} | Find pet by ID
+[**updatePet**](PetApi.md#updatePet) | **PUT** /pet | Update an existing pet
+[**updatePetWithForm**](PetApi.md#updatePetWithForm) | **POST** /pet/{petId} | Updates a pet in the store with form data
+[**uploadFile**](PetApi.md#uploadFile) | **POST** /pet/{petId}/uploadImage | uploads an image
+
+
+
+Add a new pet to the store
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val body : Pet = // Pet | Pet object that needs to be added to the store
+
+webService.addPet(body)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**Pet**](Pet.md)| Pet object that needs to be added to the store |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: application/json, application/xml
+ - **Accept**: Not defined
+
+
+Deletes a pet
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val petId : kotlin.Long = 789 // kotlin.Long | Pet id to delete
+val apiKey : kotlin.String = apiKey_example // kotlin.String |
+
+webService.deletePet(petId, apiKey)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **petId** | **kotlin.Long**| Pet id to delete |
+ **apiKey** | **kotlin.String**| | [optional]
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
+
+Finds Pets by status
+
+Multiple status values can be provided with comma separated strings
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val status : kotlin.collections.List = // kotlin.collections.List | Status values that need to be considered for filter
+
+val result : kotlin.collections.List = webService.findPetsByStatus(status)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **status** | [**kotlin.collections.List<kotlin.String>**](kotlin.String.md)| Status values that need to be considered for filter | [enum: available, pending, sold]
+
+### Return type
+
+[**kotlin.collections.List<Pet>**](Pet.md)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/xml, application/json
+
+
+Finds Pets by tags
+
+Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val tags : kotlin.collections.List = // kotlin.collections.List | Tags to filter by
+
+val result : kotlin.collections.List = webService.findPetsByTags(tags)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **tags** | [**kotlin.collections.List<kotlin.String>**](kotlin.String.md)| Tags to filter by |
+
+### Return type
+
+[**kotlin.collections.List<Pet>**](Pet.md)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/xml, application/json
+
+
+Find pet by ID
+
+Returns a single pet
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val petId : kotlin.Long = 789 // kotlin.Long | ID of pet to return
+
+val result : Pet = webService.getPetById(petId)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **petId** | **kotlin.Long**| ID of pet to return |
+
+### Return type
+
+[**Pet**](Pet.md)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/xml, application/json
+
+
+Update an existing pet
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val body : Pet = // Pet | Pet object that needs to be added to the store
+
+webService.updatePet(body)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**Pet**](Pet.md)| Pet object that needs to be added to the store |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: application/json, application/xml
+ - **Accept**: Not defined
+
+
+Updates a pet in the store with form data
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val petId : kotlin.Long = 789 // kotlin.Long | ID of pet that needs to be updated
+val name : kotlin.String = name_example // kotlin.String | Updated name of the pet
+val status : kotlin.String = status_example // kotlin.String | Updated status of the pet
+
+webService.updatePetWithForm(petId, name, status)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **petId** | **kotlin.Long**| ID of pet that needs to be updated |
+ **name** | **kotlin.String**| Updated name of the pet | [optional]
+ **status** | **kotlin.String**| Updated status of the pet | [optional]
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: application/x-www-form-urlencoded
+ - **Accept**: Not defined
+
+
+uploads an image
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(PetApi::class.java)
+val petId : kotlin.Long = 789 // kotlin.Long | ID of pet to update
+val additionalMetadata : kotlin.String = additionalMetadata_example // kotlin.String | Additional data to pass to server
+val file : java.io.File = BINARY_DATA_HERE // java.io.File | file to upload
+
+val result : ModelApiResponse = webService.uploadFile(petId, additionalMetadata, file)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **petId** | **kotlin.Long**| ID of pet to update |
+ **additionalMetadata** | **kotlin.String**| Additional data to pass to server | [optional]
+ **file** | **java.io.File**| file to upload | [optional]
+
+### Return type
+
+[**ModelApiResponse**](ModelApiResponse.md)
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: multipart/form-data
+ - **Accept**: application/json
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/StoreApi.md b/samples/client/petstore/kotlin-jvm-volley/docs/StoreApi.md
new file mode 100644
index 000000000000..168afd9b9c07
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/StoreApi.md
@@ -0,0 +1,158 @@
+# StoreApi
+
+All URIs are relative to *http://petstore.swagger.io/v2*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**deleteOrder**](StoreApi.md#deleteOrder) | **DELETE** /store/order/{orderId} | Delete purchase order by ID
+[**getInventory**](StoreApi.md#getInventory) | **GET** /store/inventory | Returns pet inventories by status
+[**getOrderById**](StoreApi.md#getOrderById) | **GET** /store/order/{orderId} | Find purchase order by ID
+[**placeOrder**](StoreApi.md#placeOrder) | **POST** /store/order | Place an order for a pet
+
+
+
+Delete purchase order by ID
+
+For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(StoreApi::class.java)
+val orderId : kotlin.String = orderId_example // kotlin.String | ID of the order that needs to be deleted
+
+webService.deleteOrder(orderId)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **orderId** | **kotlin.String**| ID of the order that needs to be deleted |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
+
+Returns pet inventories by status
+
+Returns a map of status codes to quantities
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(StoreApi::class.java)
+
+val result : kotlin.collections.Map = webService.getInventory()
+```
+
+### Parameters
+This endpoint does not need any parameter.
+
+### Return type
+
+**kotlin.collections.Map<kotlin.String, kotlin.Int>**
+
+### Authorization
+
+
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/json
+
+
+Find purchase order by ID
+
+For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(StoreApi::class.java)
+val orderId : kotlin.Long = 789 // kotlin.Long | ID of pet that needs to be fetched
+
+val result : Order = webService.getOrderById(orderId)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **orderId** | **kotlin.Long**| ID of pet that needs to be fetched |
+
+### Return type
+
+[**Order**](Order.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/xml, application/json
+
+
+Place an order for a pet
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(StoreApi::class.java)
+val body : Order = // Order | order placed for purchasing the pet
+
+val result : Order = webService.placeOrder(body)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**Order**](Order.md)| order placed for purchasing the pet |
+
+### Return type
+
+[**Order**](Order.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/xml, application/json
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/Tag.md b/samples/client/petstore/kotlin-jvm-volley/docs/Tag.md
new file mode 100644
index 000000000000..60ce1bcdbad3
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/Tag.md
@@ -0,0 +1,11 @@
+
+# Tag
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**id** | **kotlin.Long** | | [optional]
+**name** | **kotlin.String** | | [optional]
+
+
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/User.md b/samples/client/petstore/kotlin-jvm-volley/docs/User.md
new file mode 100644
index 000000000000..e801729b5ed1
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/User.md
@@ -0,0 +1,17 @@
+
+# User
+
+## Properties
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+**id** | **kotlin.Long** | | [optional]
+**username** | **kotlin.String** | | [optional]
+**firstName** | **kotlin.String** | | [optional]
+**lastName** | **kotlin.String** | | [optional]
+**email** | **kotlin.String** | | [optional]
+**password** | **kotlin.String** | | [optional]
+**phone** | **kotlin.String** | | [optional]
+**userStatus** | **kotlin.Int** | User Status | [optional]
+
+
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/docs/UserApi.md b/samples/client/petstore/kotlin-jvm-volley/docs/UserApi.md
new file mode 100644
index 000000000000..b26ede410044
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/docs/UserApi.md
@@ -0,0 +1,310 @@
+# UserApi
+
+All URIs are relative to *http://petstore.swagger.io/v2*
+
+Method | HTTP request | Description
+------------- | ------------- | -------------
+[**createUser**](UserApi.md#createUser) | **POST** /user | Create user
+[**createUsersWithArrayInput**](UserApi.md#createUsersWithArrayInput) | **POST** /user/createWithArray | Creates list of users with given input array
+[**createUsersWithListInput**](UserApi.md#createUsersWithListInput) | **POST** /user/createWithList | Creates list of users with given input array
+[**deleteUser**](UserApi.md#deleteUser) | **DELETE** /user/{username} | Delete user
+[**getUserByName**](UserApi.md#getUserByName) | **GET** /user/{username} | Get user by user name
+[**loginUser**](UserApi.md#loginUser) | **GET** /user/login | Logs user into the system
+[**logoutUser**](UserApi.md#logoutUser) | **GET** /user/logout | Logs out current logged in user session
+[**updateUser**](UserApi.md#updateUser) | **PUT** /user/{username} | Updated user
+
+
+
+Create user
+
+This can only be done by the logged in user.
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+val body : User = // User | Created user object
+
+webService.createUser(body)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**User**](User.md)| Created user object |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
+
+Creates list of users with given input array
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+val body : kotlin.collections.List = // kotlin.collections.List | List of user object
+
+webService.createUsersWithArrayInput(body)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**kotlin.collections.List<User>**](User.md)| List of user object |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
+
+Creates list of users with given input array
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+val body : kotlin.collections.List = // kotlin.collections.List | List of user object
+
+webService.createUsersWithListInput(body)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **body** | [**kotlin.collections.List<User>**](User.md)| List of user object |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
+
+Delete user
+
+This can only be done by the logged in user.
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+val username : kotlin.String = username_example // kotlin.String | The name that needs to be deleted
+
+webService.deleteUser(username)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **username** | **kotlin.String**| The name that needs to be deleted |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
+
+Get user by user name
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+val username : kotlin.String = username_example // kotlin.String | The name that needs to be fetched. Use user1 for testing.
+
+val result : User = webService.getUserByName(username)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **username** | **kotlin.String**| The name that needs to be fetched. Use user1 for testing. |
+
+### Return type
+
+[**User**](User.md)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/xml, application/json
+
+
+Logs user into the system
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+val username : kotlin.String = username_example // kotlin.String | The user name for login
+val password : kotlin.String = password_example // kotlin.String | The password for login in clear text
+
+val result : kotlin.String = webService.loginUser(username, password)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **username** | **kotlin.String**| The user name for login |
+ **password** | **kotlin.String**| The password for login in clear text |
+
+### Return type
+
+**kotlin.String**
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: application/xml, application/json
+
+
+Logs out current logged in user session
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+
+webService.logoutUser()
+```
+
+### Parameters
+This endpoint does not need any parameter.
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
+
+Updated user
+
+This can only be done by the logged in user.
+
+### Example
+```kotlin
+// Import classes:
+//import org.openapitools.client.*
+//import org.openapitools.client.infrastructure.*
+//import org.openapitools.client.models.*
+
+val apiClient = ApiClient()
+val webService = apiClient.createWebservice(UserApi::class.java)
+val username : kotlin.String = username_example // kotlin.String | name that need to be deleted
+val body : User = // User | Updated user object
+
+webService.updateUser(username, body)
+```
+
+### Parameters
+
+Name | Type | Description | Notes
+------------- | ------------- | ------------- | -------------
+ **username** | **kotlin.String**| name that need to be deleted |
+ **body** | [**User**](User.md)| Updated user object |
+
+### Return type
+
+null (empty response body)
+
+### Authorization
+
+No authorization required
+
+### HTTP request headers
+
+ - **Content-Type**: Not defined
+ - **Accept**: Not defined
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/gradle.properties b/samples/client/petstore/kotlin-jvm-volley/gradle.properties
new file mode 100644
index 000000000000..646c51b977f3
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/samples/client/petstore/kotlin-jvm-volley/gradle/wrapper/gradle-wrapper.jar b/samples/client/petstore/kotlin-jvm-volley/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000000..e708b1c023ec
Binary files /dev/null and b/samples/client/petstore/kotlin-jvm-volley/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/samples/client/petstore/kotlin-jvm-volley/gradle/wrapper/gradle-wrapper.properties b/samples/client/petstore/kotlin-jvm-volley/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000000..8cf6eb5ad222
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/samples/client/petstore/kotlin-jvm-volley/gradlew b/samples/client/petstore/kotlin-jvm-volley/gradlew
new file mode 100644
index 000000000000..4f906e0c811f
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## 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='"-Xmx64m" "-Xms64m"'
+
+# 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 or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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=`expr $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"
+
+exec "$JAVACMD" "$@"
diff --git a/samples/client/petstore/kotlin-jvm-volley/gradlew.bat b/samples/client/petstore/kotlin-jvm-volley/gradlew.bat
new file mode 100644
index 000000000000..107acd32c4e6
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@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 Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@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="-Xmx64m" "-Xms64m"
+
+@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 execute
+
+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 execute
+
+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
+
+: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 %*
+
+: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/petstore/kotlin-jvm-volley/settings.gradle b/samples/client/petstore/kotlin-jvm-volley/settings.gradle
new file mode 100644
index 000000000000..4e9ddfce002b
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/settings.gradle
@@ -0,0 +1,2 @@
+
+rootProject.name = 'kotlin-petstore-jvm-volley'
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/AndroidManifest.xml b/samples/client/petstore/kotlin-jvm-volley/src/main/AndroidManifest.xml
new file mode 100644
index 000000000000..90fc37cd8912
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/PetApi.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/PetApi.kt
new file mode 100644
index 000000000000..8f17db8b84de
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/PetApi.kt
@@ -0,0 +1,586 @@
+package org.openapitools.client.apis
+
+import android.content.Context
+import com.android.volley.DefaultRetryPolicy
+import com.android.volley.Request
+import com.android.volley.RequestQueue
+import com.android.volley.Response
+import com.android.volley.toolbox.BaseHttpStack
+import com.android.volley.toolbox.Volley
+import java.util.*;
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+import com.google.gson.reflect.TypeToken
+
+import org.openapitools.client.request.IRequestFactory
+import org.openapitools.client.request.RequestFactory
+import org.openapitools.client.infrastructure.CollectionFormats.*
+
+import org.openapitools.client.models.ModelApiResponse
+import org.openapitools.client.models.Pet
+
+/*
+* If you wish to use a custom http stack with your client you
+* can pass that to the request queue like:
+* Volley.newRequestQueue(context.applicationContext, myCustomHttpStack)
+*/
+class PetApi (
+ private val context: Context,
+ private val requestQueue: Lazy = lazy(initializer = {
+ Volley.newRequestQueue(context.applicationContext)
+ }),
+ private val requestFactory: IRequestFactory = RequestFactory(),
+ private val basePath: String = "http://petstore.swagger.io/v2",
+ private val postProcessors :List <(Request<*>) -> Unit> = listOf()) {
+
+ /**
+ * Add a new pet to the store
+ *
+ * @param body Pet object that needs to be added to the store
+ * @return void
+ */
+ suspend fun addPet(body: Pet): Unit {
+ val body: Any? = body
+ // verify the required parameter 'body' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(body)
+
+ val contentTypes : Array = arrayOf("application/json","application/xml")
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.POST,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Deletes a pet
+ *
+ * @param petId Pet id to delete
+ * @param apiKey
+ * @return void
+ */
+ suspend fun deletePet(petId: kotlin.Long, apiKey: kotlin.String? = null): Unit {
+ val body: Any? = null
+ // verify the required parameter 'petId' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(petId)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet/{petId}".replace("{" + "petId" + "}", IRequestFactory.escapeString(petId.toString()));
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ "api_key" to IRequestFactory.parameterToString(apiKey),
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.DELETE,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * 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.collections.List
+ */
+ suspend fun findPetsByStatus(status: CSVParams): kotlin.collections.List? {
+ val body: Any? = null
+ // verify the required parameter 'status' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(status)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet/findByStatus";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ "status" to IRequestFactory.parameterToString(status),
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener> { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken>() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request> = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * 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.collections.List
+ */
+ @Deprecated("This api was deprecated")
+ suspend fun findPetsByTags(tags: CSVParams): kotlin.collections.List? {
+ val body: Any? = null
+ // verify the required parameter 'tags' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(tags)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet/findByTags";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ "tags" to IRequestFactory.parameterToString(tags),
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener> { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken>() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request> = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Find pet by ID
+ * Returns a single pet
+ * @param petId ID of pet to return
+ * @return Pet
+ */
+ suspend fun getPetById(petId: kotlin.Long): Pet? {
+ val body: Any? = null
+ // verify the required parameter 'petId' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(petId)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet/{petId}".replace("{" + "petId" + "}", IRequestFactory.escapeString(petId.toString()));
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Update an existing pet
+ *
+ * @param body Pet object that needs to be added to the store
+ * @return void
+ */
+ suspend fun updatePet(body: Pet): Unit {
+ val body: Any? = body
+ // verify the required parameter 'body' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(body)
+
+ val contentTypes : Array = arrayOf("application/json","application/xml")
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.PUT,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * 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
+ * @param status Updated status of the pet
+ * @return void
+ */
+ suspend fun updatePetWithForm(petId: kotlin.Long, name: kotlin.String? = null, status: kotlin.String? = null): Unit {
+ val body: Any? = null
+ // verify the required parameter 'petId' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(petId)
+
+ val contentTypes : Array = arrayOf("application/x-www-form-urlencoded")
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet/{petId}".replace("{" + "petId" + "}", IRequestFactory.escapeString(petId.toString()));
+
+ // form params
+ val formParams = mapOf(
+ "name" to IRequestFactory.parameterToString(name),
+ "status" to IRequestFactory.parameterToString(status),
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.POST,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * uploads an image
+ *
+ * @param petId ID of pet to update
+ * @param additionalMetadata Additional data to pass to server
+ * @param file file to upload
+ * @return ModelApiResponse
+ */
+ suspend fun uploadFile(petId: kotlin.Long, additionalMetadata: kotlin.String? = null, file: java.io.File? = null): ModelApiResponse? {
+ val body: Any? = null
+ // verify the required parameter 'petId' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(petId)
+
+ val contentTypes : Array = arrayOf("multipart/form-data")
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/pet/{petId}/uploadImage".replace("{" + "petId" + "}", IRequestFactory.escapeString(petId.toString()));
+
+ // form params
+ val formParams = mapOf(
+ "additionalMetadata" to IRequestFactory.parameterToString(additionalMetadata),
+ "file" to IRequestFactory.parameterToString(file),
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.POST,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/StoreApi.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/StoreApi.kt
new file mode 100644
index 000000000000..0d2d7af60d66
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/StoreApi.kt
@@ -0,0 +1,300 @@
+package org.openapitools.client.apis
+
+import android.content.Context
+import com.android.volley.DefaultRetryPolicy
+import com.android.volley.Request
+import com.android.volley.RequestQueue
+import com.android.volley.Response
+import com.android.volley.toolbox.BaseHttpStack
+import com.android.volley.toolbox.Volley
+import java.util.*;
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+import com.google.gson.reflect.TypeToken
+
+import org.openapitools.client.request.IRequestFactory
+import org.openapitools.client.request.RequestFactory
+import org.openapitools.client.infrastructure.CollectionFormats.*
+
+import org.openapitools.client.models.Order
+
+/*
+* If you wish to use a custom http stack with your client you
+* can pass that to the request queue like:
+* Volley.newRequestQueue(context.applicationContext, myCustomHttpStack)
+*/
+class StoreApi (
+ private val context: Context,
+ private val requestQueue: Lazy = lazy(initializer = {
+ Volley.newRequestQueue(context.applicationContext)
+ }),
+ private val requestFactory: IRequestFactory = RequestFactory(),
+ private val basePath: String = "http://petstore.swagger.io/v2",
+ private val postProcessors :List <(Request<*>) -> Unit> = listOf()) {
+
+ /**
+ * 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
+ */
+ suspend fun deleteOrder(orderId: kotlin.String): Unit {
+ val body: Any? = null
+ // verify the required parameter 'orderId' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(orderId)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/store/order/{orderId}".replace("{" + "orderId" + "}", IRequestFactory.escapeString(orderId.toString()));
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.DELETE,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Returns pet inventories by status
+ * Returns a map of status codes to quantities
+ * @return kotlin.collections.Map
+ */
+ suspend fun getInventory(): kotlin.collections.Map? {
+ val body: Any? = null
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/store/inventory";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener> { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken>() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request> = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * 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
+ */
+ suspend fun getOrderById(orderId: kotlin.Long): Order? {
+ val body: Any? = null
+ // verify the required parameter 'orderId' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(orderId)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/store/order/{orderId}".replace("{" + "orderId" + "}", IRequestFactory.escapeString(orderId.toString()));
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Place an order for a pet
+ *
+ * @param body order placed for purchasing the pet
+ * @return Order
+ */
+ suspend fun placeOrder(body: Order): Order? {
+ val body: Any? = body
+ // verify the required parameter 'body' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(body)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/store/order";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.POST,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/UserApi.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/UserApi.kt
new file mode 100644
index 000000000000..bb7b4e24b153
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/apis/UserApi.kt
@@ -0,0 +1,578 @@
+package org.openapitools.client.apis
+
+import android.content.Context
+import com.android.volley.DefaultRetryPolicy
+import com.android.volley.Request
+import com.android.volley.RequestQueue
+import com.android.volley.Response
+import com.android.volley.toolbox.BaseHttpStack
+import com.android.volley.toolbox.Volley
+import java.util.*;
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+import com.google.gson.reflect.TypeToken
+
+import org.openapitools.client.request.IRequestFactory
+import org.openapitools.client.request.RequestFactory
+import org.openapitools.client.infrastructure.CollectionFormats.*
+
+import org.openapitools.client.models.User
+
+/*
+* If you wish to use a custom http stack with your client you
+* can pass that to the request queue like:
+* Volley.newRequestQueue(context.applicationContext, myCustomHttpStack)
+*/
+class UserApi (
+ private val context: Context,
+ private val requestQueue: Lazy = lazy(initializer = {
+ Volley.newRequestQueue(context.applicationContext)
+ }),
+ private val requestFactory: IRequestFactory = RequestFactory(),
+ private val basePath: String = "http://petstore.swagger.io/v2",
+ private val postProcessors :List <(Request<*>) -> Unit> = listOf()) {
+
+ /**
+ * Create user
+ * This can only be done by the logged in user.
+ * @param body Created user object
+ * @return void
+ */
+ suspend fun createUser(body: User): Unit {
+ val body: Any? = body
+ // verify the required parameter 'body' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(body)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.POST,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Creates list of users with given input array
+ *
+ * @param body List of user object
+ * @return void
+ */
+ suspend fun createUsersWithArrayInput(body: kotlin.collections.List): Unit {
+ val body: Any? = body
+ // verify the required parameter 'body' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(body)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user/createWithArray";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.POST,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Creates list of users with given input array
+ *
+ * @param body List of user object
+ * @return void
+ */
+ suspend fun createUsersWithListInput(body: kotlin.collections.List): Unit {
+ val body: Any? = body
+ // verify the required parameter 'body' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(body)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user/createWithList";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.POST,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Delete user
+ * This can only be done by the logged in user.
+ * @param username The name that needs to be deleted
+ * @return void
+ */
+ suspend fun deleteUser(username: kotlin.String): Unit {
+ val body: Any? = null
+ // verify the required parameter 'username' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(username)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user/{username}".replace("{" + "username" + "}", IRequestFactory.escapeString(username.toString()));
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.DELETE,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Get user by user name
+ *
+ * @param username The name that needs to be fetched. Use user1 for testing.
+ * @return User
+ */
+ suspend fun getUserByName(username: kotlin.String): User? {
+ val body: Any? = null
+ // verify the required parameter 'username' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(username)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user/{username}".replace("{" + "username" + "}", IRequestFactory.escapeString(username.toString()));
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Logs user into the system
+ *
+ * @param username The user name for login
+ * @param password The password for login in clear text
+ * @return kotlin.String
+ */
+ suspend fun loginUser(username: kotlin.String, password: kotlin.String): kotlin.String? {
+ val body: Any? = null
+ // verify the required parameter 'username' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(username)
+ // verify the required parameter 'password' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(password)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user/login";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ "username" to IRequestFactory.parameterToString(username),
+ "password" to IRequestFactory.parameterToString(password),
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * Logs out current logged in user session
+ *
+ * @return void
+ */
+ suspend fun logoutUser(): Unit {
+ val body: Any? = null
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user/logout";
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.GET,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+ /**
+ * 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
+ */
+ suspend fun updateUser(username: kotlin.String, body: User): Unit {
+ val body: Any? = body
+ // verify the required parameter 'username' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(username)
+ // verify the required parameter 'body' is set
+ // This is probably taken care of by non-null types anyway
+ requireNotNull(body)
+
+ val contentTypes : Array = arrayOf()
+ val contentType: String = if (contentTypes.isNotEmpty()) { contentTypes.first() } else { "application/json" }
+
+ // Do some work or avoid some work based on what we know about the model,
+ // before we delegate to a pluggable request factory template
+ // The request factory template contains only pure code and no templates
+ // to make it easy to override with your own.
+
+ // create path and map variables
+ val path = "/user/{username}".replace("{" + "username" + "}", IRequestFactory.escapeString(username.toString()));
+
+ // form params
+ val formParams = mapOf(
+ )
+
+
+ // TODO: Cater for allowing empty values
+ // TODO, if its apikey auth, then add the header names here and the hardcoded auth key
+ // Only support hard coded apikey in query param auth for when we do this first path
+ val queryParams = mapOf(
+ ).filter { it.value.isNotEmpty() }
+
+ val headerParams: Map = mapOf(
+ )
+
+ return suspendCoroutine { continuation ->
+ val responseListener = Response.Listener { response ->
+ continuation.resume(response)
+ }
+
+ val errorListener = Response.ErrorListener { error ->
+ continuation.resumeWithException(error)
+ }
+
+ val responseType = object : TypeToken() {}.type
+
+ // Call the correct request builder based on whether we have a return type or a body.
+ // All other switching on types must be done in code inside the builder
+ val request: Request = requestFactory.build(
+ Request.Method.PUT,
+ "$basePath$path",
+ body,
+ headerParams,
+ queryParams,
+ formParams,
+ contentType,
+ responseType,
+ responseListener,
+ errorListener)
+
+ postProcessors.forEach{ it.invoke(request)}
+
+ requestQueue.value.add(request)
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/ByteArrayAdapter.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/ByteArrayAdapter.kt
new file mode 100644
index 000000000000..6120b081929d
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/ByteArrayAdapter.kt
@@ -0,0 +1,33 @@
+package org.openapitools.client.infrastructure
+
+import com.google.gson.TypeAdapter
+import com.google.gson.stream.JsonReader
+import com.google.gson.stream.JsonWriter
+import com.google.gson.stream.JsonToken.NULL
+import java.io.IOException
+
+class ByteArrayAdapter : TypeAdapter() {
+ @Throws(IOException::class)
+ override fun write(out: JsonWriter?, value: ByteArray?) {
+ if (value == null) {
+ out?.nullValue()
+ } else {
+ out?.value(String(value))
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun read(out: JsonReader?): ByteArray? {
+ out ?: return null
+
+ when (out.peek()) {
+ NULL -> {
+ out.nextNull()
+ return null
+ }
+ else -> {
+ return out.nextString().toByteArray()
+ }
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/CollectionFormats.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/CollectionFormats.kt
new file mode 100644
index 000000000000..001e99325d2e
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/CollectionFormats.kt
@@ -0,0 +1,56 @@
+package org.openapitools.client.infrastructure
+
+class CollectionFormats {
+
+ open class CSVParams {
+
+ var params: List
+
+ constructor(params: List) {
+ this.params = params
+ }
+
+ constructor(vararg params: String) {
+ this.params = listOf(*params)
+ }
+
+ override fun toString(): String {
+ return params.joinToString(",")
+ }
+ }
+
+ open class SSVParams : CSVParams {
+
+ constructor(params: List) : super(params)
+
+ constructor(vararg params: String) : super(*params)
+
+ override fun toString(): String {
+ return params.joinToString(" ")
+ }
+ }
+
+ class TSVParams : CSVParams {
+
+ constructor(params: List) : super(params)
+
+ constructor(vararg params: String) : super(*params)
+
+ override fun toString(): String {
+ return params.joinToString("\t")
+ }
+ }
+
+ class PIPESParams : CSVParams {
+
+ constructor(params: List) : super(params)
+
+ constructor(vararg params: String) : super(*params)
+
+ override fun toString(): String {
+ return params.joinToString("|")
+ }
+ }
+
+ class SPACEParams : SSVParams()
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/ITransformForStorage.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/ITransformForStorage.kt
new file mode 100644
index 000000000000..21e96af43d9f
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/ITransformForStorage.kt
@@ -0,0 +1,28 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.infrastructure
+
+import org.openapitools.client.models.room.*
+
+// TODO ITransformForStorage
+interface ITransformForStorage {
+ fun toRoomModel(): T
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/LocalDateAdapter.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/LocalDateAdapter.kt
new file mode 100644
index 000000000000..30ef6697183a
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/LocalDateAdapter.kt
@@ -0,0 +1,35 @@
+package org.openapitools.client.infrastructure
+
+import com.google.gson.TypeAdapter
+import com.google.gson.stream.JsonReader
+import com.google.gson.stream.JsonWriter
+import com.google.gson.stream.JsonToken.NULL
+import java.io.IOException
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+
+class LocalDateAdapter(private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE) : TypeAdapter() {
+ @Throws(IOException::class)
+ override fun write(out: JsonWriter?, value: LocalDate?) {
+ if (value == null) {
+ out?.nullValue()
+ } else {
+ out?.value(formatter.format(value))
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun read(out: JsonReader?): LocalDate? {
+ out ?: return null
+
+ when (out.peek()) {
+ NULL -> {
+ out.nextNull()
+ return null
+ }
+ else -> {
+ return LocalDate.parse(out.nextString(), formatter)
+ }
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt
new file mode 100644
index 000000000000..3ad781c66ca1
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/LocalDateTimeAdapter.kt
@@ -0,0 +1,35 @@
+package org.openapitools.client.infrastructure
+
+import com.google.gson.TypeAdapter
+import com.google.gson.stream.JsonReader
+import com.google.gson.stream.JsonWriter
+import com.google.gson.stream.JsonToken.NULL
+import java.io.IOException
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+
+class LocalDateTimeAdapter(private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME) : TypeAdapter() {
+ @Throws(IOException::class)
+ override fun write(out: JsonWriter?, value: LocalDateTime?) {
+ if (value == null) {
+ out?.nullValue()
+ } else {
+ out?.value(formatter.format(value))
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun read(out: JsonReader?): LocalDateTime? {
+ out ?: return null
+
+ when (out.peek()) {
+ NULL -> {
+ out.nextNull()
+ return null
+ }
+ else -> {
+ return LocalDateTime.parse(out.nextString(), formatter)
+ }
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/OffsetDateTimeAdapter.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/OffsetDateTimeAdapter.kt
new file mode 100644
index 000000000000..e615135c9cc0
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/infrastructure/OffsetDateTimeAdapter.kt
@@ -0,0 +1,35 @@
+package org.openapitools.client.infrastructure
+
+import com.google.gson.TypeAdapter
+import com.google.gson.stream.JsonReader
+import com.google.gson.stream.JsonWriter
+import com.google.gson.stream.JsonToken.NULL
+import java.io.IOException
+import java.time.OffsetDateTime
+import java.time.format.DateTimeFormatter
+
+class OffsetDateTimeAdapter(private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME) : TypeAdapter() {
+ @Throws(IOException::class)
+ override fun write(out: JsonWriter?, value: OffsetDateTime?) {
+ if (value == null) {
+ out?.nullValue()
+ } else {
+ out?.value(formatter.format(value))
+ }
+ }
+
+ @Throws(IOException::class)
+ override fun read(out: JsonReader?): OffsetDateTime? {
+ out ?: return null
+
+ when (out.peek()) {
+ NULL -> {
+ out.nextNull()
+ return null
+ }
+ else -> {
+ return OffsetDateTime.parse(out.nextString(), formatter)
+ }
+ }
+ }
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/ApiResponse.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/ApiResponse.kt
new file mode 100644
index 000000000000..d2569559c1aa
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/ApiResponse.kt
@@ -0,0 +1,57 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models
+
+
+import com.google.gson.annotations.SerializedName
+import org.openapitools.client.models.room.ApiResponseRoomModel
+import org.openapitools.client.infrastructure.ITransformForStorage
+
+/**
+ * Describes the result of uploading an image resource
+ *
+ * @param code
+ * @param type
+ * @param message
+ */
+
+data class ApiResponse (
+
+ @SerializedName("code")
+ val code: kotlin.Int? = null,
+
+ @SerializedName("type")
+ val type: kotlin.String? = null,
+
+ @SerializedName("message")
+ val message: kotlin.String? = null
+
+): ITransformForStorage {
+ companion object { }
+ override fun toRoomModel(): ApiResponseRoomModel =
+ ApiResponseRoomModel(roomTableId = 0,
+ code = this.code,
+type = this.type,
+message = this.message,
+ )
+
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Category.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Category.kt
new file mode 100644
index 000000000000..8bd167529e18
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Category.kt
@@ -0,0 +1,52 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models
+
+
+import com.google.gson.annotations.SerializedName
+import org.openapitools.client.models.room.CategoryRoomModel
+import org.openapitools.client.infrastructure.ITransformForStorage
+
+/**
+ * A category for a pet
+ *
+ * @param id
+ * @param name
+ */
+
+data class Category (
+
+ @SerializedName("id")
+ val id: kotlin.Long? = null,
+
+ @SerializedName("name")
+ val name: kotlin.String? = null
+
+): ITransformForStorage {
+ companion object { }
+ override fun toRoomModel(): CategoryRoomModel =
+ CategoryRoomModel(roomTableId = 0,
+ id = this.id,
+name = this.name,
+ )
+
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/ModelApiResponse.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/ModelApiResponse.kt
new file mode 100644
index 000000000000..9f81f502ed3c
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/ModelApiResponse.kt
@@ -0,0 +1,57 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models
+
+
+import com.google.gson.annotations.SerializedName
+import org.openapitools.client.models.room.ModelApiResponseRoomModel
+import org.openapitools.client.infrastructure.ITransformForStorage
+
+/**
+ * Describes the result of uploading an image resource
+ *
+ * @param code
+ * @param type
+ * @param message
+ */
+
+data class ModelApiResponse (
+
+ @SerializedName("code")
+ val code: kotlin.Int? = null,
+
+ @SerializedName("type")
+ val type: kotlin.String? = null,
+
+ @SerializedName("message")
+ val message: kotlin.String? = null
+
+): ITransformForStorage {
+ companion object { }
+ override fun toRoomModel(): ModelApiResponseRoomModel =
+ ModelApiResponseRoomModel(roomTableId = 0,
+ code = this.code,
+type = this.type,
+message = this.message,
+ )
+
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Order.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Order.kt
new file mode 100644
index 000000000000..cb48557cd279
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Order.kt
@@ -0,0 +1,83 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models
+
+
+import com.google.gson.annotations.SerializedName
+import org.openapitools.client.models.room.OrderRoomModel
+import org.openapitools.client.infrastructure.ITransformForStorage
+
+/**
+ * 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 (
+
+ @SerializedName("id")
+ val id: kotlin.Long? = null,
+
+ @SerializedName("petId")
+ val petId: kotlin.Long? = null,
+
+ @SerializedName("quantity")
+ val quantity: kotlin.Int? = null,
+
+ @SerializedName("shipDate")
+ val shipDate: java.time.OffsetDateTime? = null,
+
+ /* Order Status */
+ @SerializedName("status")
+ val status: Order.Status? = null,
+
+ @SerializedName("complete")
+ val complete: kotlin.Boolean? = false
+
+): ITransformForStorage {
+ companion object { }
+ override fun toRoomModel(): OrderRoomModel =
+ OrderRoomModel(roomTableId = 0,
+ id = this.id,
+petId = this.petId,
+quantity = this.quantity,
+shipDate = this.shipDate,
+status = this.status,
+complete = this.complete,
+ )
+
+ /**
+ * Order Status
+ *
+ * Values: placed,approved,delivered
+ */
+ enum class Status(val value: kotlin.String) {
+ @SerializedName(value = "placed") placed("placed"),
+ @SerializedName(value = "approved") approved("approved"),
+ @SerializedName(value = "delivered") delivered("delivered");
+ }
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Pet.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Pet.kt
new file mode 100644
index 000000000000..83edfb9c4f4f
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Pet.kt
@@ -0,0 +1,85 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models
+
+import org.openapitools.client.models.Category
+import org.openapitools.client.models.Tag
+
+import com.google.gson.annotations.SerializedName
+import org.openapitools.client.models.room.PetRoomModel
+import org.openapitools.client.infrastructure.ITransformForStorage
+
+/**
+ * A pet for sale in the pet store
+ *
+ * @param name
+ * @param photoUrls
+ * @param id
+ * @param category
+ * @param tags
+ * @param status pet status in the store
+ */
+
+data class Pet (
+
+ @SerializedName("name")
+ val name: kotlin.String,
+
+ @SerializedName("photoUrls")
+ val photoUrls: kotlin.collections.List,
+
+ @SerializedName("id")
+ val id: kotlin.Long? = null,
+
+ @SerializedName("category")
+ val category: Category? = null,
+
+ @SerializedName("tags")
+ val tags: kotlin.collections.List? = null,
+
+ /* pet status in the store */
+ @SerializedName("status")
+ val status: Pet.Status? = null
+
+): ITransformForStorage {
+ companion object { }
+ override fun toRoomModel(): PetRoomModel =
+ PetRoomModel(roomTableId = 0,
+ name = this.name,
+photoUrls = this.photoUrls,
+id = this.id,
+category = this.category,
+
+status = this.status,
+ )
+
+ /**
+ * pet status in the store
+ *
+ * Values: available,pending,sold
+ */
+ enum class Status(val value: kotlin.String) {
+ @SerializedName(value = "available") available("available"),
+ @SerializedName(value = "pending") pending("pending"),
+ @SerializedName(value = "sold") sold("sold");
+ }
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Tag.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Tag.kt
new file mode 100644
index 000000000000..03dcdbbaf04f
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/Tag.kt
@@ -0,0 +1,52 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models
+
+
+import com.google.gson.annotations.SerializedName
+import org.openapitools.client.models.room.TagRoomModel
+import org.openapitools.client.infrastructure.ITransformForStorage
+
+/**
+ * A tag for a pet
+ *
+ * @param id
+ * @param name
+ */
+
+data class Tag (
+
+ @SerializedName("id")
+ val id: kotlin.Long? = null,
+
+ @SerializedName("name")
+ val name: kotlin.String? = null
+
+): ITransformForStorage {
+ companion object { }
+ override fun toRoomModel(): TagRoomModel =
+ TagRoomModel(roomTableId = 0,
+ id = this.id,
+name = this.name,
+ )
+
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/User.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/User.kt
new file mode 100644
index 000000000000..c35a2df394fb
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/User.kt
@@ -0,0 +1,83 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models
+
+
+import com.google.gson.annotations.SerializedName
+import org.openapitools.client.models.room.UserRoomModel
+import org.openapitools.client.infrastructure.ITransformForStorage
+
+/**
+ * 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 (
+
+ @SerializedName("id")
+ val id: kotlin.Long? = null,
+
+ @SerializedName("username")
+ val username: kotlin.String? = null,
+
+ @SerializedName("firstName")
+ val firstName: kotlin.String? = null,
+
+ @SerializedName("lastName")
+ val lastName: kotlin.String? = null,
+
+ @SerializedName("email")
+ val email: kotlin.String? = null,
+
+ @SerializedName("password")
+ val password: kotlin.String? = null,
+
+ @SerializedName("phone")
+ val phone: kotlin.String? = null,
+
+ /* User Status */
+ @SerializedName("userStatus")
+ val userStatus: kotlin.Int? = null
+
+): ITransformForStorage {
+ companion object { }
+ override fun toRoomModel(): UserRoomModel =
+ UserRoomModel(roomTableId = 0,
+ id = this.id,
+username = this.username,
+firstName = this.firstName,
+lastName = this.lastName,
+email = this.email,
+password = this.password,
+phone = this.phone,
+userStatus = this.userStatus,
+ )
+
+}
+
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/ApiResponseRoomModel.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/ApiResponseRoomModel.kt
new file mode 100644
index 000000000000..aa00942c3977
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/ApiResponseRoomModel.kt
@@ -0,0 +1,52 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models.room
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import org.openapitools.client.models.*
+
+
+@Entity(tableName = "ApiResponse")
+/**
+* Room model for Describes the result of uploading an image resource
+* @param code
+* @param type
+* @param message
+*/
+data class ApiResponseRoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+
+ var code: kotlin.Int? = null,
+ var type: kotlin.String? = null,
+ var message: kotlin.String? = null,
+
+ ) {
+
+ companion object { }
+
+ fun toApiModel(): ApiResponse = ApiResponse(
+ code = this.code,
+ type = this.type,
+ message = this.message,
+ )
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/CategoryRoomModel.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/CategoryRoomModel.kt
new file mode 100644
index 000000000000..896589516aba
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/CategoryRoomModel.kt
@@ -0,0 +1,49 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models.room
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import org.openapitools.client.models.*
+
+
+@Entity(tableName = "Category")
+/**
+* Room model for A category for a pet
+* @param id
+* @param name
+*/
+data class CategoryRoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+
+ var id: kotlin.Long? = null,
+ var name: kotlin.String? = null,
+
+ ) {
+
+ companion object { }
+
+ fun toApiModel(): Category = Category(
+ id = this.id,
+ name = this.name,
+ )
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/ModelApiResponseRoomModel.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/ModelApiResponseRoomModel.kt
new file mode 100644
index 000000000000..87c211434958
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/ModelApiResponseRoomModel.kt
@@ -0,0 +1,52 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models.room
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import org.openapitools.client.models.*
+
+
+@Entity(tableName = "ModelApiResponse")
+/**
+* Room model for Describes the result of uploading an image resource
+* @param code
+* @param type
+* @param message
+*/
+data class ModelApiResponseRoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+
+ var code: kotlin.Int? = null,
+ var type: kotlin.String? = null,
+ var message: kotlin.String? = null,
+
+ ) {
+
+ companion object { }
+
+ fun toApiModel(): ModelApiResponse = ModelApiResponse(
+ code = this.code,
+ type = this.type,
+ message = this.message,
+ )
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/OrderRoomModel.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/OrderRoomModel.kt
new file mode 100644
index 000000000000..5367a0e35436
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/OrderRoomModel.kt
@@ -0,0 +1,61 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models.room
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import org.openapitools.client.models.*
+
+
+@Entity(tableName = "Order")
+/**
+* Room model for 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 OrderRoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+
+ var id: kotlin.Long? = null,
+ var petId: kotlin.Long? = null,
+ var quantity: kotlin.Int? = null,
+ var shipDate: java.time.OffsetDateTime? = null,
+ var complete: kotlin.Boolean? = null,
+
+ var status: Order.Status? = null,
+ ) {
+
+ companion object { }
+
+ fun toApiModel(): Order = Order(
+ id = this.id,
+ petId = this.petId,
+ quantity = this.quantity,
+ shipDate = this.shipDate,
+ status = this.status,
+ complete = this.complete,
+ )
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/PetRoomModel.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/PetRoomModel.kt
new file mode 100644
index 000000000000..81296bb2f9da
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/PetRoomModel.kt
@@ -0,0 +1,63 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models.room
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import org.openapitools.client.models.*
+
+
+@Entity(tableName = "Pet")
+/**
+* Room model for A pet for sale in the pet store
+* @param name
+* @param photoUrls
+* @param id
+* @param category
+* @param tags
+* @param status pet status in the store
+*/
+data class PetRoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+ var photoUrls: kotlin.collections.List,
+
+ var name: kotlin.String,
+ var id: kotlin.Long? = null,
+ var category: Category? = null,
+
+ var status: Pet.Status? = null,
+ ) {
+
+ @Ignore
+ var tags: kotlin.collections.List? = null
+
+ companion object { }
+
+ fun toApiModel(): Pet = Pet(
+ name = this.name,
+ photoUrls = this.photoUrls,
+ id = this.id,
+ category = this.category,
+ tags = this.tags,
+ status = this.status,
+ )
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/TagRoomModel.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/TagRoomModel.kt
new file mode 100644
index 000000000000..42f255a11f11
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/TagRoomModel.kt
@@ -0,0 +1,49 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models.room
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import org.openapitools.client.models.*
+
+
+@Entity(tableName = "Tag")
+/**
+* Room model for A tag for a pet
+* @param id
+* @param name
+*/
+data class TagRoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+
+ var id: kotlin.Long? = null,
+ var name: kotlin.String? = null,
+
+ ) {
+
+ companion object { }
+
+ fun toApiModel(): Tag = Tag(
+ id = this.id,
+ name = this.name,
+ )
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/UserRoomModel.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/UserRoomModel.kt
new file mode 100644
index 000000000000..f0c067fc19f1
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/models/room/UserRoomModel.kt
@@ -0,0 +1,67 @@
+/**
+ * OpenAPI Petstore
+ *
+ * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
+ *
+ * The version of the OpenAPI document: 1.0.0
+ *
+ *
+ * Please note:
+ * This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * Do not edit this file manually.
+ */
+
+@file:Suppress(
+ "ArrayInDataClass",
+ "EnumEntryName",
+ "RemoveRedundantQualifierName",
+ "UnusedImport"
+)
+
+package org.openapitools.client.models.room
+
+import androidx.room.Entity
+import androidx.room.Ignore
+import androidx.room.PrimaryKey
+import org.openapitools.client.models.*
+
+
+@Entity(tableName = "User")
+/**
+* Room model for 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 UserRoomModel (
+ @PrimaryKey(autoGenerate = true) var roomTableId: Int,
+
+ var id: kotlin.Long? = null,
+ var username: kotlin.String? = null,
+ var firstName: kotlin.String? = null,
+ var lastName: kotlin.String? = null,
+ var email: kotlin.String? = null,
+ var password: kotlin.String? = null,
+ var phone: kotlin.String? = null,
+ var userStatus: kotlin.Int? = null,
+
+ ) {
+
+ companion object { }
+
+ fun toApiModel(): User = User(
+ id = this.id,
+ username = this.username,
+ firstName = this.firstName,
+ lastName = this.lastName,
+ email = this.email,
+ password = this.password,
+ phone = this.phone,
+ userStatus = this.userStatus,
+ )
+}
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/request/GsonRequest.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/request/GsonRequest.kt
new file mode 100644
index 000000000000..965eeae66e25
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/request/GsonRequest.kt
@@ -0,0 +1,119 @@
+package org.openapitools.client.request
+
+import com.android.volley.NetworkResponse
+import com.android.volley.ParseError
+import com.android.volley.Request
+import com.android.volley.Response
+import com.android.volley.toolbox.HttpHeaderParser
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonSyntaxException
+import java.io.UnsupportedEncodingException
+import java.nio.charset.Charset
+import java.net.HttpURLConnection
+import java.lang.reflect.Type
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.OffsetDateTime
+
+import org.openapitools.client.infrastructure.OffsetDateTimeAdapter
+import org.openapitools.client.infrastructure.LocalDateTimeAdapter
+import org.openapitools.client.infrastructure.LocalDateAdapter
+import org.openapitools.client.infrastructure.ByteArrayAdapter
+
+class GsonRequest(
+ method: Int,
+ url: String,
+ private val body: Any?,
+ private val headers: Map?,
+ private val params: MutableMap?,
+ private val contentTypeForBody: String?,
+ private val encodingForParams: String?,
+ private val gsonAdapters: Map?,
+ private val type: Type,
+ private val listener: Response.Listener,
+ errorListener: Response.ErrorListener
+) : Request(method, url, errorListener) {
+
+ val gsonBuilder: GsonBuilder = GsonBuilder()
+ .registerTypeAdapter(OffsetDateTime::class.java, OffsetDateTimeAdapter())
+ .registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeAdapter())
+ .registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
+ .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter())
+ .apply {
+ gsonAdapters?.forEach {
+ this.registerTypeAdapter(it.key, it.value)
+ }
+ }
+
+ val gson: Gson by lazy {
+ gsonBuilder.create()
+ }
+
+ private var response: NetworkResponse? = null
+
+ override fun deliverResponse(response: T?) {
+ listener.onResponse(response)
+ }
+
+ override fun getParams(): MutableMap? = params ?: super.getParams()
+
+ override fun getBodyContentType(): String = contentTypeForBody ?: super.getBodyContentType()
+
+ override fun getParamsEncoding(): String = encodingForParams ?: super.getParamsEncoding()
+
+ override fun getHeaders(): MutableMap {
+ val combined = HashMap()
+ combined.putAll(super.getHeaders())
+ if (headers != null) {
+ combined.putAll(headers)
+ }
+ return combined
+ }
+
+ override fun getBody(): ByteArray? {
+ if (body != null) {
+ return gson.toJson(body).toByteArray(Charsets.UTF_8)
+ }
+ return super.getBody()
+ }
+
+ override fun parseNetworkResponse(response: NetworkResponse?): Response {
+ return try {
+ this.response = copyTo(response)
+ val json = String(
+ response?.data ?: ByteArray(0),
+ Charset.forName(HttpHeaderParser.parseCharset(response?.headers))
+ )
+ Response.success(
+ gson.fromJson(json, type),
+ HttpHeaderParser.parseCacheHeaders(response)
+ )
+ } catch (e: UnsupportedEncodingException) {
+ Response.error(ParseError(e))
+ } catch (e: JsonSyntaxException) {
+ Response.error(ParseError(e))
+ }
+ }
+
+ private fun copyTo(response: NetworkResponse?): NetworkResponse {
+ return if (response != null) {
+ NetworkResponse(
+ response.statusCode,
+ response.data,
+ response.notModified,
+ response.networkTimeMs,
+ response.allHeaders
+ )
+ } else {
+ // Return an empty response.
+ NetworkResponse(
+ HttpURLConnection.HTTP_BAD_METHOD,
+ ByteArray(0),
+ false,
+ 0,
+ emptyList()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/request/IRequestFactory.kt b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/request/IRequestFactory.kt
new file mode 100644
index 000000000000..599db0bc0de6
--- /dev/null
+++ b/samples/client/petstore/kotlin-jvm-volley/src/main/java/org/openapitools/client/request/IRequestFactory.kt
@@ -0,0 +1,64 @@
+package org.openapitools.client.request
+
+import com.android.volley.Request
+import com.android.volley.Response
+import java.io.UnsupportedEncodingException
+import java.lang.reflect.Type
+import java.net.URLEncoder
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+import java.time.format.DateTimeFormatter
+import java.time.OffsetDateTime
+import java.time.LocalDate
+
+
+interface IRequestFactory {
+
+ companion object {
+ /**
+ * ISO 8601 date time format.
+ * @see https://en.wikipedia.org/wiki/ISO_8601
+ */
+ fun formatDateTime(datetime: OffsetDateTime) = DateTimeFormatter.ISO_INSTANT.format(datetime)
+ fun formatDate(date: LocalDate) = DateTimeFormatter.ISO_LOCAL_DATE.format(date)
+
+ fun escapeString(str: String): String {
+ return try {
+ URLEncoder.encode(str, "UTF-8")
+ } catch (e: UnsupportedEncodingException) {
+ str
+ }
+ }
+
+ fun parameterToString(param: Any?) =
+ when (param) {
+ null -> ""
+ is OffsetDateTime -> formatDateTime(param)
+ is Collection<*> -> {
+ val b = StringBuilder()
+ for (o in param) {
+ if (b.isNotEmpty()) {
+ b.append(",")
+ }
+ b.append(o.toString())
+ }
+ b.toString()
+ }
+ else -> param.toString()
+ }
+ }
+
+
+ fun build(
+ method: Int,
+ url : String,
+ body: Any?,
+ headers: Map