diff --git a/CI/pom.xml.circleci.java7 b/CI/pom.xml.circleci.java7 index b4630eda541..e66fe6f5111 100644 --- a/CI/pom.xml.circleci.java7 +++ b/CI/pom.xml.circleci.java7 @@ -837,7 +837,6 @@ samples/client/petstore/scala-akka samples/client/petstore/scala-httpclient samples/client/petstore/scalaz - samples/client/petstore/clojure samples/client/petstore/java/feign samples/client/petstore/java/jersey1 samples/client/petstore/java/jersey2 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java index 604211bd703..e1814e522b3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ClojureClientCodegen.java @@ -17,23 +17,16 @@ package org.openapitools.codegen.languages; -import org.openapitools.codegen.CliOption; -import org.openapitools.codegen.CodegenConfig; -import org.openapitools.codegen.CodegenConstants; -import org.openapitools.codegen.CodegenOperation; -import org.openapitools.codegen.CodegenType; -import org.openapitools.codegen.DefaultCodegen; -import org.openapitools.codegen.SupportingFile; +import org.openapitools.codegen.*; import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.*; import io.swagger.v3.oas.models.info.*; import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.utils.ModelUtils; import java.io.File; -import java.util.Map; -import java.util.List; +import java.util.*; public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfig { private static final String PROJECT_NAME = "projectName"; @@ -44,23 +37,29 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi private static final String PROJECT_LICENSE_URL = "projectLicenseUrl"; private static final String BASE_NAMESPACE = "baseNamespace"; + static final String X_BASE_SPEC = "x-baseSpec"; + static final String X_MODELS = "x-models"; + protected String projectName; protected String projectDescription; protected String projectVersion; protected String baseNamespace; + protected Set baseSpecs; + protected Set models = new HashSet<>(); protected String sourceFolder = "src"; public ClojureClientCodegen() { super(); outputFolder = "generated-code" + File.separator + "clojure"; + modelTemplateFiles.put("spec.mustache", ".clj"); apiTemplateFiles.put("api.mustache", ".clj"); embeddedTemplateDir = templateDir = "clojure"; cliOptions.add(new CliOption(PROJECT_NAME, "name of the project (Default: generated from info.title or \"openapi-clj-client\")")); cliOptions.add(new CliOption(PROJECT_DESCRIPTION, - "description of the project (Default: using info.description or \"Client library of \")")); + "description of the project (Default: using info.description or \"Client library of \")")); cliOptions.add(new CliOption(PROJECT_VERSION, "version of the project (Default: using info.version or \"1.0.0\")")); cliOptions.add(new CliOption(PROJECT_URL, @@ -71,6 +70,49 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi "URL of the license the project uses (Default: using info.license.url or not included in project.clj)")); cliOptions.add(new CliOption(BASE_NAMESPACE, "the base/top namespace (Default: generated from projectName)")); + + typeMapping.clear(); + + // We have specs for most of the types: + typeMapping.put("integer", "int?"); + typeMapping.put("long", "int?"); + typeMapping.put("short", "int?"); + typeMapping.put("number", "float?"); + typeMapping.put("float", "float?"); + typeMapping.put("double", "float?"); + typeMapping.put("array", "list?"); + typeMapping.put("map", "map?"); + typeMapping.put("boolean", "boolean?"); + typeMapping.put("string", "string?"); + typeMapping.put("char", "char?"); + typeMapping.put("date", "inst?"); + typeMapping.put("DateTime", "inst?"); + typeMapping.put("UUID", "uuid?"); + + // But some type mappings are not really worth/meaningful to check for: + typeMapping.put("object", "any?"); // Like, everything is an object. + typeMapping.put("file", "any?"); // We don't really have specs for files, + typeMapping.put("binary", "any?"); // nor binary. + // And while there is a way to easily check if something is a bytearray, + // (https://stackoverflow.com/questions/14796964/), it's not possible + // to conform it yet, so we leave it as is. + typeMapping.put("ByteArray", "any?"); + + // Set of base specs that don't need to be imported + baseSpecs = new HashSet<>( + Arrays.asList( + "int?", + "float?", + "list?", + "map?", + "boolean?", + "string?", + "char?", + "inst?", + "uuid?", + "any?" + ) + ); } @Override @@ -88,6 +130,75 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi return "Generates a Clojure client library."; } + @Override + public String getTypeDeclaration(Schema p) { + if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + + return "(s/coll-of " + getTypeDeclaration(inner) + ")"; + } else if (ModelUtils.isMapSchema(p)) { + Schema inner = (Schema) p.getAdditionalProperties(); + + return "(s/map-of string? " + getTypeDeclaration(inner) + ")"; + } + + // If it's a type we defined, we want to append the spec suffix + if (!typeMapping.containsKey(super.getSchemaType(p))) { + return super.getTypeDeclaration(p) + "-spec"; + } else { + return super.getTypeDeclaration(p); + } + } + + @Override + public String getSchemaType(Schema p) { + String openAPIType = super.getSchemaType(p); + + if (typeMapping.containsKey(openAPIType)) { + return typeMapping.get(openAPIType); + } else { + return toModelName(openAPIType); + } + } + + @Override + public String toModelName(String name) { + return dashize(name); + } + + @Override + public String toVarName(String name) { + name = name.replaceAll("[^a-zA-Z0-9_-]+", ""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + return name; + } + + @Override + public CodegenModel fromModel(String name, Schema mod, Map allDefinitions) { + CodegenModel model = super.fromModel(name, mod, allDefinitions); + + // If a var is a base spec we won't need to import it + for (CodegenProperty var : model.vars) { + if (baseSpecs.contains(var.complexType)) { + var.vendorExtensions.put(X_BASE_SPEC, true); + } else { + var.vendorExtensions.put(X_BASE_SPEC, false); + } + if (var.items != null) { + if (baseSpecs.contains(var.items.complexType)) { + var.items.vendorExtensions.put(X_BASE_SPEC, true); + } else { + var.items.vendorExtensions.put(X_BASE_SPEC, false); + } + } + } + + // We also add all models to our model list so we can import them e.g. in operations + models.add(model.classname); + + return model; + } + @Override public void preprocessOpenAPI(OpenAPI openAPI) { super.preprocessOpenAPI(openAPI); @@ -151,12 +262,14 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi baseNamespace = dashize(projectName); } apiPackage = baseNamespace + ".api"; + modelPackage = baseNamespace + ".specs"; additionalProperties.put(PROJECT_NAME, projectName); additionalProperties.put(PROJECT_DESCRIPTION, escapeText(projectDescription)); additionalProperties.put(PROJECT_VERSION, projectVersion); additionalProperties.put(BASE_NAMESPACE, baseNamespace); additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage); + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage); final String baseNamespaceFolder = sourceFolder + File.separator + namespaceToFolder(baseNamespace); supportingFiles.add(new SupportingFile("project.mustache", "", "project.clj")); @@ -175,6 +288,11 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi return outputFolder + File.separator + sourceFolder + File.separator + namespaceToFolder(apiPackage); } + @Override + public String modelFileFolder() { + return outputFolder + File.separator + sourceFolder + File.separator + namespaceToFolder(modelPackage); + } + @Override public String toOperationId(String operationId) { // throw exception if method name is empty @@ -190,6 +308,11 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi return underscore(toApiName(name)); } + @Override + public String toModelFilename(String name) { + return underscore(toModelName(name)); + } + @Override public String toApiName(String name) { return dashize(name); @@ -200,13 +323,6 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi return toVarName(name); } - @Override - public String toVarName(String name) { - name = name.replaceAll("[^a-zA-Z0-9_-]+", ""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. - name = dashize(name); - return name; - } - @Override public String escapeText(String input) { if (input == null) { @@ -222,6 +338,8 @@ public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfi for (CodegenOperation op : ops) { // Convert httpMethod to lower case, e.g. "get", "post" op.httpMethod = op.httpMethod.toLowerCase(); + + op.vendorExtensions.put(X_MODELS, models); } return operations; } diff --git a/modules/openapi-generator/src/main/resources/clojure/api.mustache b/modules/openapi-generator/src/main/resources/clojure/api.mustache index cf821cfdd3c..b38c8d55b2c 100644 --- a/modules/openapi-generator/src/main/resources/clojure/api.mustache +++ b/modules/openapi-generator/src/main/resources/clojure/api.mustache @@ -1,12 +1,18 @@ {{=< >=}}(ns . - (:require [.core :refer [call-api check-required-params with-collection-format]]) + (:require [.core :refer [call-api check-required-params with-collection-format *api-context*]] + [clojure.spec.alpha :as s] + [spec-tools.core :as st] + [orchestra.core :refer [defn-spec]] + <#operations><#operation><#-first><#vendorExtensions.x-models>[.<.> :refer :all] + ) (:import (java.io File))) + <#operations><#operation> -(defn -with-http-info +(defn-spec -with-http-info any? "<&summary><#notes> <¬es>"<#hasOptionalParams> - ([<#allParams><#required><#isFile>^File ] (-with-http-info<#allParams><#required> nil)) - <#hasOptionalParams>([<#allParams><#required><#isFile>^File <#hasOptionalParams>{:keys [<#allParams><^required><#isFile>^File ]}]<#hasRequiredParams> + ([<#allParams><#required><#isFile>^File <#hasMore>, ] (-with-http-info<#allParams><#required> nil)) + <#hasOptionalParams>([<#allParams><#required><#isFile>^File <#hasMore>, <#hasOptionalParams>{:keys [<#allParams><^required><#isFile>^File <#hasMore> ]} (s/map-of keyword? any?)]<#hasRequiredParams> <#hasOptionalParams> (check-required-params<#allParams><#required> ) <#hasOptionalParams> (call-api "" : <#hasOptionalParams> {:path-params {<#pathParams>"" <#collectionFormat>(with-collection-format :)<^collectionFormat> } @@ -18,10 +24,14 @@ <#hasOptionalParams> :accepts [<#produces>"<& mediaType>"<#hasMore> ] <#hasOptionalParams> :auth-names [<#authMethods>"<&name>"<#hasMore> ]})<#hasOptionalParams>)) -(defn +(defn-spec <#returnType><^returnType>any? "<&summary><#notes> <¬es>"<#hasOptionalParams> - ([<#allParams><#required><#isFile>^File ] (<#allParams><#required> nil)) - <#hasOptionalParams>([<#allParams><#required><#isFile>^File <#hasOptionalParams>optional-params] - <#hasOptionalParams> (:data (-with-http-info<#allParams><#required> <#hasOptionalParams> optional-params))<#hasOptionalParams>)) + ([<#allParams><#required><#isFile>^File <#hasMore>, ] (<#allParams><#required> nil)) + <#hasOptionalParams>([<#allParams><#required><#isFile>^File <#hasMore>, <#hasOptionalParams>optional-params any?] + <#hasOptionalParams> (let [res (:data (-with-http-info<#allParams><#required> <#hasOptionalParams> optional-params))] + <#hasOptionalParams> (if (:decode-models *api-context*) + <#hasOptionalParams> (st/decode <#returnType><^returnType>any? res st/string-transformer) + <#hasOptionalParams> res))<#hasOptionalParams>)) + diff --git a/modules/openapi-generator/src/main/resources/clojure/core.mustache b/modules/openapi-generator/src/main/resources/clojure/core.mustache index 4506eab9f22..b073e6c086b 100644 --- a/modules/openapi-generator/src/main/resources/clojure/core.mustache +++ b/modules/openapi-generator/src/main/resources/clojure/core.mustache @@ -16,6 +16,7 @@ {:base-url "<&basePath>" :date-format "yyyy-MM-dd" :datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" + :decode-models false :debug false :auths {<#authMethods>"<&name>" nil<#hasMore> }}) diff --git a/modules/openapi-generator/src/main/resources/clojure/project.mustache b/modules/openapi-generator/src/main/resources/clojure/project.mustache index 0fe990d02c8..e7a4ea685fe 100644 --- a/modules/openapi-generator/src/main/resources/clojure/project.mustache +++ b/modules/openapi-generator/src/main/resources/clojure/project.mustache @@ -3,6 +3,8 @@ :url "<&projectUrl>"<#projectLicenseName> :license {:name "<&projectLicenseName>"<#projectLicenseUrl> :url "<&projectLicenseUrl>"} - :dependencies [[org.clojure/clojure "1.7.0"] - [clj-http "3.6.0"] - [cheshire "5.5.0"]]) + :dependencies [[org.clojure/clojure "1.9.0"] + [metosin/spec-tools "0.7.0"] + [clj-http "3.8.0"] + [orchestra "2017.11.12-1"] + [cheshire "5.8.0"]]) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/clojure/spec.mustache b/modules/openapi-generator/src/main/resources/clojure/spec.mustache new file mode 100644 index 00000000000..9a4221ad4bc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/clojure/spec.mustache @@ -0,0 +1,19 @@ +{{=< >=}}(ns . + (:require [clojure.spec.alpha :as s] + [spec-tools.data-spec :as ds] + <#models><#model><#vars><^isContainer><^vendorExtensions.x-baseSpec>[. :refer :all] + <#isContainer><^vendorExtensions.x-baseSpec>[. :refer :all] + ) + (:import (java.io File))) + +<#models><#model> +(def -data + {<#vars> + (ds/<#required>req<^required>opt :) + }) + +(def -spec + (ds/spec + {:name :: + :spec -data})) + \ No newline at end of file diff --git a/samples/client/petstore/clojure/project.clj b/samples/client/petstore/clojure/project.clj index 2496800da02..5e9dca8ebf3 100644 --- a/samples/client/petstore/clojure/project.clj +++ b/samples/client/petstore/clojure/project.clj @@ -2,6 +2,8 @@ :description "This is a sample server Petstore server. For this sample, you can use the api key \"special-key\" to test the authorization filters" :license {:name "Apache-2.0" :url "http://www.apache.org/licenses/LICENSE-2.0.html"} - :dependencies [[org.clojure/clojure "1.7.0"] - [clj-http "3.6.0"] - [cheshire "5.5.0"]]) + :dependencies [[org.clojure/clojure "1.9.0"] + [metosin/spec-tools "0.7.0"] + [clj-http "3.8.0"] + [orchestra "2017.11.12-1"] + [cheshire "5.8.0"]]) \ No newline at end of file diff --git a/samples/client/petstore/clojure/src/open_api_petstore/api/pet.clj b/samples/client/petstore/clojure/src/open_api_petstore/api/pet.clj index b0faed710bf..bd1339b46a1 100644 --- a/samples/client/petstore/clojure/src/open_api_petstore/api/pet.clj +++ b/samples/client/petstore/clojure/src/open_api_petstore/api/pet.clj @@ -1,11 +1,21 @@ (ns open-api-petstore.api.pet - (:require [open-api-petstore.core :refer [call-api check-required-params with-collection-format]]) + (:require [open-api-petstore.core :refer [call-api check-required-params with-collection-format *api-context*]] + [clojure.spec.alpha :as s] + [spec-tools.core :as st] + [orchestra.core :refer [defn-spec]] + [open-api-petstore.specs.tag :refer :all] + [open-api-petstore.specs.category :refer :all] + [open-api-petstore.specs.user :refer :all] + [open-api-petstore.specs.pet :refer :all] + [open-api-petstore.specs.order :refer :all] + ) (:import (java.io File))) -(defn add-pet-with-http-info + +(defn-spec add-pet-with-http-info any? "Add a new pet to the store" ([] (add-pet-with-http-info nil)) - ([{:keys [pet ]}] + ([{:keys [pet]} (s/map-of keyword? any?)] (call-api "/pet" :post {:path-params {} :header-params {} @@ -16,37 +26,45 @@ :accepts [] :auth-names ["petstore_auth"]}))) -(defn add-pet +(defn-spec add-pet any? "Add a new pet to the store" ([] (add-pet nil)) - ([optional-params] - (:data (add-pet-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (add-pet-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) -(defn delete-pet-with-http-info + +(defn-spec delete-pet-with-http-info any? "Deletes a pet" - ([pet-id ] (delete-pet-with-http-info pet-id nil)) - ([pet-id {:keys [api-key ]}] - (check-required-params pet-id) + ([petId int?, ] (delete-pet-with-http-info petId nil)) + ([petId int?, {:keys [api_key]} (s/map-of keyword? any?)] + (check-required-params petId) (call-api "/pet/{petId}" :delete - {:path-params {"petId" pet-id } - :header-params {"api_key" api-key } + {:path-params {"petId" petId } + :header-params {"api_key" api_key } :query-params {} :form-params {} :content-types [] :accepts [] :auth-names ["petstore_auth"]}))) -(defn delete-pet +(defn-spec delete-pet any? "Deletes a pet" - ([pet-id ] (delete-pet pet-id nil)) - ([pet-id optional-params] - (:data (delete-pet-with-http-info pet-id optional-params)))) + ([petId int?, ] (delete-pet petId nil)) + ([petId int?, optional-params any?] + (let [res (:data (delete-pet-with-http-info petId optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) -(defn find-pets-by-status-with-http-info + +(defn-spec find-pets-by-status-with-http-info any? "Finds Pets by status Multiple status values can be provided with comma separated strings" ([] (find-pets-by-status-with-http-info nil)) - ([{:keys [status ]}] + ([{:keys [status]} (s/map-of keyword? any?)] (call-api "/pet/findByStatus" :get {:path-params {} :header-params {} @@ -56,18 +74,22 @@ :accepts ["application/json" "application/xml"] :auth-names ["petstore_auth"]}))) -(defn find-pets-by-status +(defn-spec find-pets-by-status (s/coll-of pet-spec) "Finds Pets by status Multiple status values can be provided with comma separated strings" ([] (find-pets-by-status nil)) - ([optional-params] - (:data (find-pets-by-status-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (find-pets-by-status-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode (s/coll-of pet-spec) res st/string-transformer) + res)))) -(defn find-pets-by-tags-with-http-info + +(defn-spec find-pets-by-tags-with-http-info any? "Finds Pets by tags Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing." ([] (find-pets-by-tags-with-http-info nil)) - ([{:keys [tags ]}] + ([{:keys [tags]} (s/map-of keyword? any?)] (call-api "/pet/findByTags" :get {:path-params {} :header-params {} @@ -77,20 +99,24 @@ :accepts ["application/json" "application/xml"] :auth-names ["petstore_auth"]}))) -(defn find-pets-by-tags +(defn-spec find-pets-by-tags (s/coll-of pet-spec) "Finds Pets by tags Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing." ([] (find-pets-by-tags nil)) - ([optional-params] - (:data (find-pets-by-tags-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (find-pets-by-tags-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode (s/coll-of pet-spec) res st/string-transformer) + res)))) -(defn get-pet-by-id-with-http-info + +(defn-spec get-pet-by-id-with-http-info any? "Find pet by ID Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions" - [pet-id ] - (check-required-params pet-id) + [petId int?] + (check-required-params petId) (call-api "/pet/{petId}" :get - {:path-params {"petId" pet-id } + {:path-params {"petId" petId } :header-params {} :query-params {} :form-params {} @@ -98,16 +124,20 @@ :accepts ["application/json" "application/xml"] :auth-names ["api_key" "petstore_auth"]})) -(defn get-pet-by-id +(defn-spec get-pet-by-id pet-spec "Find pet by ID Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions" - [pet-id ] - (:data (get-pet-by-id-with-http-info pet-id))) + [petId int?] + (let [res (:data (get-pet-by-id-with-http-info petId))] + (if (:decode-models *api-context*) + (st/decode pet-spec res st/string-transformer) + res))) -(defn update-pet-with-http-info + +(defn-spec update-pet-with-http-info any? "Update an existing pet" ([] (update-pet-with-http-info nil)) - ([{:keys [pet ]}] + ([{:keys [pet]} (s/map-of keyword? any?)] (call-api "/pet" :put {:path-params {} :header-params {} @@ -118,19 +148,23 @@ :accepts [] :auth-names ["petstore_auth"]}))) -(defn update-pet +(defn-spec update-pet any? "Update an existing pet" ([] (update-pet nil)) - ([optional-params] - (:data (update-pet-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (update-pet-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) -(defn update-pet-with-form-with-http-info + +(defn-spec update-pet-with-form-with-http-info any? "Updates a pet in the store with form data" - ([pet-id ] (update-pet-with-form-with-http-info pet-id nil)) - ([pet-id {:keys [name status ]}] - (check-required-params pet-id) + ([petId string?, ] (update-pet-with-form-with-http-info petId nil)) + ([petId string?, {:keys [name status]} (s/map-of keyword? any?)] + (check-required-params petId) (call-api "/pet/{petId}" :post - {:path-params {"petId" pet-id } + {:path-params {"petId" petId } :header-params {} :query-params {} :form-params {"name" name "status" status } @@ -138,29 +172,37 @@ :accepts [] :auth-names ["petstore_auth"]}))) -(defn update-pet-with-form +(defn-spec update-pet-with-form any? "Updates a pet in the store with form data" - ([pet-id ] (update-pet-with-form pet-id nil)) - ([pet-id optional-params] - (:data (update-pet-with-form-with-http-info pet-id optional-params)))) + ([petId string?, ] (update-pet-with-form petId nil)) + ([petId string?, optional-params any?] + (let [res (:data (update-pet-with-form-with-http-info petId optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) -(defn upload-file-with-http-info + +(defn-spec upload-file-with-http-info any? "uploads an image" - ([pet-id ] (upload-file-with-http-info pet-id nil)) - ([pet-id {:keys [additional-metadata ^File file ]}] - (check-required-params pet-id) + ([petId int?, ] (upload-file-with-http-info petId nil)) + ([petId int?, {:keys [additionalMetadata ^File file]} (s/map-of keyword? any?)] + (check-required-params petId) (call-api "/pet/{petId}/uploadImage" :post - {:path-params {"petId" pet-id } + {:path-params {"petId" petId } :header-params {} :query-params {} - :form-params {"additionalMetadata" additional-metadata "file" file } + :form-params {"additionalMetadata" additionalMetadata "file" file } :content-types ["multipart/form-data"] :accepts [] :auth-names ["petstore_auth"]}))) -(defn upload-file +(defn-spec upload-file any? "uploads an image" - ([pet-id ] (upload-file pet-id nil)) - ([pet-id optional-params] - (:data (upload-file-with-http-info pet-id optional-params)))) + ([petId int?, ] (upload-file petId nil)) + ([petId int?, optional-params any?] + (let [res (:data (upload-file-with-http-info petId optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) + diff --git a/samples/client/petstore/clojure/src/open_api_petstore/api/store.clj b/samples/client/petstore/clojure/src/open_api_petstore/api/store.clj index 92265329577..a91c086c3fd 100644 --- a/samples/client/petstore/clojure/src/open_api_petstore/api/store.clj +++ b/samples/client/petstore/clojure/src/open_api_petstore/api/store.clj @@ -1,14 +1,24 @@ (ns open-api-petstore.api.store - (:require [open-api-petstore.core :refer [call-api check-required-params with-collection-format]]) + (:require [open-api-petstore.core :refer [call-api check-required-params with-collection-format *api-context*]] + [clojure.spec.alpha :as s] + [spec-tools.core :as st] + [orchestra.core :refer [defn-spec]] + [open-api-petstore.specs.tag :refer :all] + [open-api-petstore.specs.category :refer :all] + [open-api-petstore.specs.user :refer :all] + [open-api-petstore.specs.pet :refer :all] + [open-api-petstore.specs.order :refer :all] + ) (:import (java.io File))) -(defn delete-order-with-http-info + +(defn-spec delete-order-with-http-info any? "Delete purchase order by ID For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors" - [order-id ] - (check-required-params order-id) + [orderId string?] + (check-required-params orderId) (call-api "/store/order/{orderId}" :delete - {:path-params {"orderId" order-id } + {:path-params {"orderId" orderId } :header-params {} :query-params {} :form-params {} @@ -16,13 +26,17 @@ :accepts [] :auth-names []})) -(defn delete-order +(defn-spec delete-order any? "Delete purchase order by ID For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors" - [order-id ] - (:data (delete-order-with-http-info order-id))) + [orderId string?] + (let [res (:data (delete-order-with-http-info orderId))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res))) -(defn get-inventory-with-http-info + +(defn-spec get-inventory-with-http-info any? "Returns pet inventories by status Returns a map of status codes to quantities" [] @@ -35,19 +49,23 @@ :accepts ["application/json" "application/xml"] :auth-names ["api_key"]})) -(defn get-inventory +(defn-spec get-inventory (s/map-of string? int?) "Returns pet inventories by status Returns a map of status codes to quantities" [] - (:data (get-inventory-with-http-info))) + (let [res (:data (get-inventory-with-http-info))] + (if (:decode-models *api-context*) + (st/decode (s/map-of string? int?) res st/string-transformer) + res))) -(defn get-order-by-id-with-http-info + +(defn-spec get-order-by-id-with-http-info any? "Find purchase order by ID For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions" - [order-id ] - (check-required-params order-id) + [orderId string?] + (check-required-params orderId) (call-api "/store/order/{orderId}" :get - {:path-params {"orderId" order-id } + {:path-params {"orderId" orderId } :header-params {} :query-params {} :form-params {} @@ -55,16 +73,20 @@ :accepts ["application/json" "application/xml"] :auth-names []})) -(defn get-order-by-id +(defn-spec get-order-by-id order-spec "Find purchase order by ID For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions" - [order-id ] - (:data (get-order-by-id-with-http-info order-id))) + [orderId string?] + (let [res (:data (get-order-by-id-with-http-info orderId))] + (if (:decode-models *api-context*) + (st/decode order-spec res st/string-transformer) + res))) -(defn place-order-with-http-info + +(defn-spec place-order-with-http-info any? "Place an order for a pet" ([] (place-order-with-http-info nil)) - ([{:keys [order ]}] + ([{:keys [order]} (s/map-of keyword? any?)] (call-api "/store/order" :post {:path-params {} :header-params {} @@ -75,9 +97,13 @@ :accepts ["application/json" "application/xml"] :auth-names []}))) -(defn place-order +(defn-spec place-order order-spec "Place an order for a pet" ([] (place-order nil)) - ([optional-params] - (:data (place-order-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (place-order-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode order-spec res st/string-transformer) + res)))) + diff --git a/samples/client/petstore/clojure/src/open_api_petstore/api/user.clj b/samples/client/petstore/clojure/src/open_api_petstore/api/user.clj index fe38cf2c495..ee758efedcd 100644 --- a/samples/client/petstore/clojure/src/open_api_petstore/api/user.clj +++ b/samples/client/petstore/clojure/src/open_api_petstore/api/user.clj @@ -1,12 +1,22 @@ (ns open-api-petstore.api.user - (:require [open-api-petstore.core :refer [call-api check-required-params with-collection-format]]) + (:require [open-api-petstore.core :refer [call-api check-required-params with-collection-format *api-context*]] + [clojure.spec.alpha :as s] + [spec-tools.core :as st] + [orchestra.core :refer [defn-spec]] + [open-api-petstore.specs.tag :refer :all] + [open-api-petstore.specs.category :refer :all] + [open-api-petstore.specs.user :refer :all] + [open-api-petstore.specs.pet :refer :all] + [open-api-petstore.specs.order :refer :all] + ) (:import (java.io File))) -(defn create-user-with-http-info + +(defn-spec create-user-with-http-info any? "Create user This can only be done by the logged in user." ([] (create-user-with-http-info nil)) - ([{:keys [user ]}] + ([{:keys [user]} (s/map-of keyword? any?)] (call-api "/user" :post {:path-params {} :header-params {} @@ -17,17 +27,21 @@ :accepts [] :auth-names []}))) -(defn create-user +(defn-spec create-user any? "Create user This can only be done by the logged in user." ([] (create-user nil)) - ([optional-params] - (:data (create-user-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (create-user-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) -(defn create-users-with-array-input-with-http-info + +(defn-spec create-users-with-array-input-with-http-info any? "Creates list of users with given input array" ([] (create-users-with-array-input-with-http-info nil)) - ([{:keys [user ]}] + ([{:keys [user]} (s/map-of keyword? any?)] (call-api "/user/createWithArray" :post {:path-params {} :header-params {} @@ -38,16 +52,20 @@ :accepts [] :auth-names []}))) -(defn create-users-with-array-input +(defn-spec create-users-with-array-input any? "Creates list of users with given input array" ([] (create-users-with-array-input nil)) - ([optional-params] - (:data (create-users-with-array-input-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (create-users-with-array-input-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) -(defn create-users-with-list-input-with-http-info + +(defn-spec create-users-with-list-input-with-http-info any? "Creates list of users with given input array" ([] (create-users-with-list-input-with-http-info nil)) - ([{:keys [user ]}] + ([{:keys [user]} (s/map-of keyword? any?)] (call-api "/user/createWithList" :post {:path-params {} :header-params {} @@ -58,16 +76,20 @@ :accepts [] :auth-names []}))) -(defn create-users-with-list-input +(defn-spec create-users-with-list-input any? "Creates list of users with given input array" ([] (create-users-with-list-input nil)) - ([optional-params] - (:data (create-users-with-list-input-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (create-users-with-list-input-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) -(defn delete-user-with-http-info + +(defn-spec delete-user-with-http-info any? "Delete user This can only be done by the logged in user." - [username ] + [username string?] (check-required-params username) (call-api "/user/{username}" :delete {:path-params {"username" username } @@ -78,15 +100,19 @@ :accepts [] :auth-names []})) -(defn delete-user +(defn-spec delete-user any? "Delete user This can only be done by the logged in user." - [username ] - (:data (delete-user-with-http-info username))) + [username string?] + (let [res (:data (delete-user-with-http-info username))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res))) -(defn get-user-by-name-with-http-info + +(defn-spec get-user-by-name-with-http-info any? "Get user by user name" - [username ] + [username string?] (check-required-params username) (call-api "/user/{username}" :get {:path-params {"username" username } @@ -97,15 +123,19 @@ :accepts ["application/json" "application/xml"] :auth-names []})) -(defn get-user-by-name +(defn-spec get-user-by-name user-spec "Get user by user name" - [username ] - (:data (get-user-by-name-with-http-info username))) + [username string?] + (let [res (:data (get-user-by-name-with-http-info username))] + (if (:decode-models *api-context*) + (st/decode user-spec res st/string-transformer) + res))) -(defn login-user-with-http-info + +(defn-spec login-user-with-http-info any? "Logs user into the system" ([] (login-user-with-http-info nil)) - ([{:keys [username password ]}] + ([{:keys [username password]} (s/map-of keyword? any?)] (call-api "/user/login" :get {:path-params {} :header-params {} @@ -115,13 +145,17 @@ :accepts ["application/json" "application/xml"] :auth-names []}))) -(defn login-user +(defn-spec login-user string? "Logs user into the system" ([] (login-user nil)) - ([optional-params] - (:data (login-user-with-http-info optional-params)))) + ([optional-params any?] + (let [res (:data (login-user-with-http-info optional-params))] + (if (:decode-models *api-context*) + (st/decode string? res st/string-transformer) + res)))) -(defn logout-user-with-http-info + +(defn-spec logout-user-with-http-info any? "Logs out current logged in user session" [] (call-api "/user/logout" :get @@ -133,16 +167,20 @@ :accepts [] :auth-names []})) -(defn logout-user +(defn-spec logout-user any? "Logs out current logged in user session" [] - (:data (logout-user-with-http-info))) + (let [res (:data (logout-user-with-http-info))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res))) -(defn update-user-with-http-info + +(defn-spec update-user-with-http-info any? "Updated user This can only be done by the logged in user." - ([username ] (update-user-with-http-info username nil)) - ([username {:keys [user ]}] + ([username string?, ] (update-user-with-http-info username nil)) + ([username string?, {:keys [user]} (s/map-of keyword? any?)] (check-required-params username) (call-api "/user/{username}" :put {:path-params {"username" username } @@ -154,10 +192,14 @@ :accepts [] :auth-names []}))) -(defn update-user +(defn-spec update-user any? "Updated user This can only be done by the logged in user." - ([username ] (update-user username nil)) - ([username optional-params] - (:data (update-user-with-http-info username optional-params)))) + ([username string?, ] (update-user username nil)) + ([username string?, optional-params any?] + (let [res (:data (update-user-with-http-info username optional-params))] + (if (:decode-models *api-context*) + (st/decode any? res st/string-transformer) + res)))) + diff --git a/samples/client/petstore/clojure/src/open_api_petstore/core.clj b/samples/client/petstore/clojure/src/open_api_petstore/core.clj index 28993e8b0d6..ef30d6d2516 100644 --- a/samples/client/petstore/clojure/src/open_api_petstore/core.clj +++ b/samples/client/petstore/clojure/src/open_api_petstore/core.clj @@ -16,6 +16,7 @@ {:base-url "http://petstore.swagger.io/v2" :date-format "yyyy-MM-dd" :datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" + :decode-models false :debug false :auths {"api_key" nil "petstore_auth" nil}}) diff --git a/samples/client/petstore/clojure/src/open_api_petstore/specs/category.clj b/samples/client/petstore/clojure/src/open_api_petstore/specs/category.clj new file mode 100644 index 00000000000..21af0a6eeab --- /dev/null +++ b/samples/client/petstore/clojure/src/open_api_petstore/specs/category.clj @@ -0,0 +1,17 @@ +(ns open-api-petstore.specs.category + (:require [clojure.spec.alpha :as s] + [spec-tools.data-spec :as ds] + ) + (:import (java.io File))) + + +(def category-data + { + (ds/opt :id) int? + (ds/opt :name) string? + }) + +(def category-spec + (ds/spec + {:name ::category + :spec category-data})) diff --git a/samples/client/petstore/clojure/src/open_api_petstore/specs/order.clj b/samples/client/petstore/clojure/src/open_api_petstore/specs/order.clj new file mode 100644 index 00000000000..d87391bdf81 --- /dev/null +++ b/samples/client/petstore/clojure/src/open_api_petstore/specs/order.clj @@ -0,0 +1,21 @@ +(ns open-api-petstore.specs.order + (:require [clojure.spec.alpha :as s] + [spec-tools.data-spec :as ds] + ) + (:import (java.io File))) + + +(def order-data + { + (ds/opt :id) int? + (ds/opt :petId) int? + (ds/opt :quantity) int? + (ds/opt :shipDate) inst? + (ds/opt :status) string? + (ds/opt :complete) boolean? + }) + +(def order-spec + (ds/spec + {:name ::order + :spec order-data})) diff --git a/samples/client/petstore/clojure/src/open_api_petstore/specs/pet.clj b/samples/client/petstore/clojure/src/open_api_petstore/specs/pet.clj new file mode 100644 index 00000000000..cce6965d2f8 --- /dev/null +++ b/samples/client/petstore/clojure/src/open_api_petstore/specs/pet.clj @@ -0,0 +1,23 @@ +(ns open-api-petstore.specs.pet + (:require [clojure.spec.alpha :as s] + [spec-tools.data-spec :as ds] + [open-api-petstore.specs.category :refer :all] + [open-api-petstore.specs.tag :refer :all] + ) + (:import (java.io File))) + + +(def pet-data + { + (ds/opt :id) int? + (ds/opt :category) category-spec + (ds/req :name) string? + (ds/req :photoUrls) (s/coll-of string?) + (ds/opt :tags) (s/coll-of tag-spec) + (ds/opt :status) string? + }) + +(def pet-spec + (ds/spec + {:name ::pet + :spec pet-data})) diff --git a/samples/client/petstore/clojure/src/open_api_petstore/specs/tag.clj b/samples/client/petstore/clojure/src/open_api_petstore/specs/tag.clj new file mode 100644 index 00000000000..1f679625825 --- /dev/null +++ b/samples/client/petstore/clojure/src/open_api_petstore/specs/tag.clj @@ -0,0 +1,17 @@ +(ns open-api-petstore.specs.tag + (:require [clojure.spec.alpha :as s] + [spec-tools.data-spec :as ds] + ) + (:import (java.io File))) + + +(def tag-data + { + (ds/opt :id) int? + (ds/opt :name) string? + }) + +(def tag-spec + (ds/spec + {:name ::tag + :spec tag-data})) diff --git a/samples/client/petstore/clojure/src/open_api_petstore/specs/user.clj b/samples/client/petstore/clojure/src/open_api_petstore/specs/user.clj new file mode 100644 index 00000000000..cac72a0c72b --- /dev/null +++ b/samples/client/petstore/clojure/src/open_api_petstore/specs/user.clj @@ -0,0 +1,23 @@ +(ns open-api-petstore.specs.user + (:require [clojure.spec.alpha :as s] + [spec-tools.data-spec :as ds] + ) + (:import (java.io File))) + + +(def user-data + { + (ds/opt :id) int? + (ds/opt :username) string? + (ds/opt :firstName) string? + (ds/opt :lastName) string? + (ds/opt :email) string? + (ds/opt :password) string? + (ds/opt :phone) string? + (ds/opt :userStatus) int? + }) + +(def user-spec + (ds/spec + {:name ::user + :spec user-data})) diff --git a/samples/client/petstore/clojure/test/open_api_petstore/api/store_test.clj b/samples/client/petstore/clojure/test/open_api_petstore/api/store_test.clj index 0de8d9c5883..b7f3edc31c2 100644 --- a/samples/client/petstore/clojure/test/open_api_petstore/api/store_test.clj +++ b/samples/client/petstore/clojure/test/open_api_petstore/api/store_test.clj @@ -32,3 +32,11 @@ (delete-order order-id) (comment "it seems that delete-order does not really delete the order" (is (thrown? RuntimeException (get-order-by-id order-id)))))) + +(deftest test-order-spec-conforming + (with-api-context {:decode-models true} + (let [order (make-random-order) + order-id (:id order) + _ (place-order {:order order}) + fetched (get-order-by-id order-id)] + (is (= order fetched))))) \ No newline at end of file diff --git a/samples/client/petstore/clojure/test/open_api_petstore/core_test.clj b/samples/client/petstore/clojure/test/open_api_petstore/core_test.clj index 6b0487f6088..d3cfcdfdb8b 100644 --- a/samples/client/petstore/clojure/test/open_api_petstore/core_test.clj +++ b/samples/client/petstore/clojure/test/open_api_petstore/core_test.clj @@ -9,6 +9,7 @@ (is (= {:base-url "http://petstore.swagger.io/v2" :date-format "yyyy-MM-dd" :datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" + :decode-models false :debug false :auths {"api_key" nil "petstore_auth" nil}} @@ -24,6 +25,7 @@ (is (= {:base-url "http://localhost" :date-format "yyyy-MM-dd" :datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" + :decode-models false :debug true :auths (merge (:auths default-api-context) {"api_key" "key1" @@ -35,6 +37,7 @@ (is (= {:base-url "http://localhost" :date-format "yyyy-MM-dd" :datetime-format "yyyy-MM-dd HH:mm:ss" + :decode-models false :debug true :auths (merge (:auths default-api-context) {"api_key" "key2" @@ -44,6 +47,7 @@ (is (= {:base-url "http://petstore.swagger.io/v2" :date-format "yyyy-MM-dd" :datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" + :decode-models false :debug false :auths {"api_key" nil "petstore_auth" nil}}