[Clojure] Add model generation and conforming (#122)

* [Clojure] Add model generation and verification

- Generate clojure.specs from models
- Optionally validate them at runtime (validation is active if
  orchestra.spec.test/instrument is called after specs are imported)
- Coerce the results of the API calls to get objects that conform
  to the spec (e.g. get Date objects for dates and time instead of strings)

* [Clojure] Make model conforming configurable and opt-out

* [Clojure] Move specs from a single file to a ns per model

So that the order of the forms will be resolved by the compiler,
otherwise we'd have to implement a topological ordering.

* [Clojure] Update petstore sample and set automatic decoding off

* [Clojure] Stop testing Clojure generator on Java7

* [Clojure] Fix tests and handling of multiple arity

* [Clojure] Fix tests and add testing for the new decoding feature

* [Clojure] Capitalize names of generated models

* [Clojure] Rename petstore specs to be capitalized

* Revert to lowercase spec names, and postfix the data specs
This commit is contained in:
Fabrizio Ferrai 2018-08-13 09:04:25 +02:00 committed by William Cheng
parent aed8e38584
commit 74d70121d1
18 changed files with 529 additions and 154 deletions

View File

@ -837,7 +837,6 @@
<module>samples/client/petstore/scala-akka</module>
<module>samples/client/petstore/scala-httpclient</module>
<module>samples/client/petstore/scalaz</module>
<module>samples/client/petstore/clojure</module>
<module>samples/client/petstore/java/feign</module>
<module>samples/client/petstore/java/jersey1</module>
<module>samples/client/petstore/java/jersey2</module>

View File

@ -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<String> baseSpecs;
protected Set<String> 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 <projectNname>\")"));
"description of the project (Default: using info.description or \"Client library of <projectName>\")"));
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<String, Schema> 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;
}

View File

@ -1,12 +1,18 @@
{{=< >=}}(ns <package>.<classname>
(:require [<baseNamespace>.core :refer [call-api check-required-params with-collection-format]])
(:require [<baseNamespace>.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>[<modelPackage>.<.> :refer :all]
</vendorExtensions.x-models></-first></operation></operations>)
(:import (java.io File)))
<#operations><#operation>
(defn <operationId>-with-http-info
(defn-spec <operationId>-with-http-info any?
"<&summary><#notes>
<&notes></notes>"<#hasOptionalParams>
([<#allParams><#required><#isFile>^File </isFile><paramName> </required></allParams>] (<operationId>-with-http-info<#allParams><#required> <paramName></required></allParams> nil))</hasOptionalParams>
<#hasOptionalParams>(</hasOptionalParams>[<#allParams><#required><#isFile>^File </isFile><paramName> </required></allParams><#hasOptionalParams>{:keys [<#allParams><^required><#isFile>^File </isFile><paramName> </required></allParams>]}</hasOptionalParams>]<#hasRequiredParams>
([<#allParams><#required><#isFile>^File </isFile><paramName> <dataType><#hasMore>, </hasMore></required></allParams>] (<operationId>-with-http-info<#allParams><#required> <paramName></required></allParams> nil))</hasOptionalParams>
<#hasOptionalParams>(</hasOptionalParams>[<#allParams><#required><#isFile>^File </isFile><paramName> <dataType><#hasMore>, </hasMore></required></allParams><#hasOptionalParams>{:keys [<#allParams><^required><#isFile>^File </isFile><paramName><#hasMore> </hasMore></required></allParams>]} (s/map-of keyword? any?)</hasOptionalParams>]<#hasRequiredParams>
<#hasOptionalParams> </hasOptionalParams>(check-required-params<#allParams><#required> <paramName></required></allParams>)</hasRequiredParams>
<#hasOptionalParams> </hasOptionalParams>(call-api "<path>" :<httpMethod>
<#hasOptionalParams> </hasOptionalParams> {:path-params {<#pathParams>"<baseName>" <#collectionFormat>(with-collection-format <paramName> :<collectionFormat>)</collectionFormat><^collectionFormat><paramName></collectionFormat> </pathParams>}
@ -18,10 +24,14 @@
<#hasOptionalParams> </hasOptionalParams> :accepts [<#produces>"<& mediaType>"<#hasMore> </hasMore></produces>]
<#hasOptionalParams> </hasOptionalParams> :auth-names [<#authMethods>"<&name>"<#hasMore> </hasMore></authMethods>]})<#hasOptionalParams>)</hasOptionalParams>)
(defn <operationId>
(defn-spec <operationId> <#returnType><returnType></returnType><^returnType>any?</returnType>
"<&summary><#notes>
<&notes></notes>"<#hasOptionalParams>
([<#allParams><#required><#isFile>^File </isFile><paramName> </required></allParams>] (<operationId><#allParams><#required> <paramName></required></allParams> nil))</hasOptionalParams>
<#hasOptionalParams>(</hasOptionalParams>[<#allParams><#required><#isFile>^File </isFile><paramName> </required></allParams><#hasOptionalParams>optional-params</hasOptionalParams>]
<#hasOptionalParams> </hasOptionalParams>(:data (<operationId>-with-http-info<#allParams><#required> <paramName></required></allParams><#hasOptionalParams> optional-params</hasOptionalParams>))<#hasOptionalParams>)</hasOptionalParams>)
([<#allParams><#required><#isFile>^File </isFile><paramName> <dataType><#hasMore>, </hasMore></required></allParams>] (<operationId><#allParams><#required> <paramName></required></allParams> nil))</hasOptionalParams>
<#hasOptionalParams>(</hasOptionalParams>[<#allParams><#required><#isFile>^File </isFile><paramName> <dataType><#hasMore>, </hasMore></required></allParams><#hasOptionalParams>optional-params any?</hasOptionalParams>]
<#hasOptionalParams> </hasOptionalParams>(let [res (:data (<operationId>-with-http-info<#allParams><#required> <paramName></required></allParams><#hasOptionalParams> optional-params</hasOptionalParams>))]
<#hasOptionalParams> </hasOptionalParams> (if (:decode-models *api-context*)
<#hasOptionalParams> </hasOptionalParams> (st/decode <#returnType><returnType></returnType><^returnType>any?</returnType> res st/string-transformer)
<#hasOptionalParams> </hasOptionalParams> res))<#hasOptionalParams>)</hasOptionalParams>)
</operation></operations>

View File

@ -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>
</hasMore></authMethods>}})

View File

@ -3,6 +3,8 @@
:url "<&projectUrl>"</projectUrl><#projectLicenseName>
:license {:name "<&projectLicenseName>"<#projectLicenseUrl>
:url "<&projectLicenseUrl>"</projectLicenseUrl>}</projectLicenseName>
: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"]])

View File

@ -0,0 +1,19 @@
{{=< >=}}(ns <package>.<classname>
(:require [clojure.spec.alpha :as s]
[spec-tools.data-spec :as ds]
<#models><#model><#vars><^isContainer><^vendorExtensions.x-baseSpec>[<package>.<complexType> :refer :all]
</vendorExtensions.x-baseSpec></isContainer><#isContainer><^vendorExtensions.x-baseSpec>[<package>.<complexType> :refer :all]
</vendorExtensions.x-baseSpec></isContainer></vars></model></models>)
(:import (java.io File)))
<#models><#model>
(def <classname>-data
{<#vars>
(ds/<#required>req</required><^required>opt</required> :<name>) <datatype></vars>
})
(def <classname>-spec
(ds/spec
{:name ::<classname>
:spec <classname>-data}))
</model></models>

View File

@ -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"]])

View File

@ -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))))

View File

@ -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))))

View File

@ -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))))

View File

@ -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}})

View File

@ -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}))

View File

@ -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}))

View File

@ -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}))

View File

@ -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}))

View File

@ -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}))

View File

@ -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)))))

View File

@ -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}}