forked from loafle/openapi-generator-original
Merge pull request #1585 from xhh/clojure-client
Add a Clojure client codegen
This commit is contained in:
commit
ff1dd034a8
@ -20,6 +20,7 @@ fi
|
||||
cd $APP_DIR
|
||||
./bin/akka-scala-petstore.sh
|
||||
./bin/android-java-petstore.sh
|
||||
./bin/clojure-petstore.sh
|
||||
./bin/csharp-petstore.sh
|
||||
./bin/dynamic-html.sh
|
||||
./bin/html-petstore.sh
|
||||
|
31
bin/clojure-petstore.sh
Executable file
31
bin/clojure-petstore.sh
Executable file
@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
SCRIPT="$0"
|
||||
|
||||
while [ -h "$SCRIPT" ] ; do
|
||||
ls=`ls -ld "$SCRIPT"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
SCRIPT="$link"
|
||||
else
|
||||
SCRIPT=`dirname "$SCRIPT"`/"$link"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ! -d "${APP_DIR}" ]; then
|
||||
APP_DIR=`dirname "$SCRIPT"`/..
|
||||
APP_DIR=`cd "${APP_DIR}"; pwd`
|
||||
fi
|
||||
|
||||
executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar"
|
||||
|
||||
if [ ! -f "$executable" ]
|
||||
then
|
||||
mvn clean package
|
||||
fi
|
||||
|
||||
# if you've executed sbt assembly previously it will use that instead.
|
||||
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
|
||||
ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l clojure -o samples/client/petstore/clojure"
|
||||
|
||||
java $JAVA_OPTS -jar $executable $ags
|
@ -10,10 +10,10 @@ import java.util.Set;
|
||||
|
||||
public class CodegenOperation {
|
||||
public final List<CodegenProperty> responseHeaders = new ArrayList<CodegenProperty>();
|
||||
public Boolean hasAuthMethods, hasConsumes, hasProduces, hasParams, returnTypeIsPrimitive,
|
||||
returnSimpleType, subresourceOperation, isMapContainer, isListContainer,
|
||||
hasMore = Boolean.TRUE, isMultipart, isResponseBinary = Boolean.FALSE,
|
||||
hasReference = Boolean.FALSE;
|
||||
public Boolean hasAuthMethods, hasConsumes, hasProduces, hasParams, hasOptionalParams,
|
||||
returnTypeIsPrimitive, returnSimpleType, subresourceOperation, isMapContainer,
|
||||
isListContainer, isMultipart, hasMore = Boolean.TRUE,
|
||||
isResponseBinary = Boolean.FALSE, hasReference = Boolean.FALSE;
|
||||
public String path, operationId, returnType, httpMethod, returnBaseType,
|
||||
returnContainer, summary, notes, baseName, defaultResponse;
|
||||
public List<Map<String, String>> consumes, produces;
|
||||
|
@ -1299,6 +1299,9 @@ public class DefaultCodegen {
|
||||
p.isFormParam = new Boolean(true);
|
||||
formParams.add(p.copy());
|
||||
}
|
||||
if (p.required == null || !p.required) {
|
||||
op.hasOptionalParams = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String i : imports) {
|
||||
|
@ -0,0 +1,178 @@
|
||||
package io.swagger.codegen.languages;
|
||||
|
||||
import io.swagger.codegen.CodegenConfig;
|
||||
import io.swagger.codegen.CodegenConstants;
|
||||
import io.swagger.codegen.CodegenOperation;
|
||||
import io.swagger.codegen.CodegenType;
|
||||
import io.swagger.codegen.DefaultCodegen;
|
||||
import io.swagger.codegen.SupportingFile;
|
||||
import io.swagger.models.Contact;
|
||||
import io.swagger.models.Info;
|
||||
import io.swagger.models.License;
|
||||
import io.swagger.models.Swagger;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
public class ClojureClientCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
private static final String PROJECT_NAME = "projectName";
|
||||
private static final String PROJECT_DESCRIPTION = "projectDescription";
|
||||
private static final String PROJECT_VERSION = "projectVersion";
|
||||
private static final String PROJECT_URL = "projectUrl";
|
||||
private static final String LICENSE_NAME = "licenseName";
|
||||
private static final String LICENSE_URL = "licenseUrl";
|
||||
private static final String BASE_NAMESPACE = "baseNamespace";
|
||||
|
||||
protected String projectName = null;
|
||||
protected String projectDescription = null;
|
||||
protected String projectVersion = null;
|
||||
protected String sourceFolder = "src";
|
||||
|
||||
public ClojureClientCodegen() {
|
||||
super();
|
||||
outputFolder = "generated-code" + File.separator + "clojure";
|
||||
apiTemplateFiles.put("api.mustache", ".clj");
|
||||
embeddedTemplateDir = templateDir = "clojure";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodegenType getTag() {
|
||||
return CodegenType.CLIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "clojure";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelp() {
|
||||
return "Generates a Clojure client library.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preprocessSwagger(Swagger swagger) {
|
||||
super.preprocessSwagger(swagger);
|
||||
|
||||
if (additionalProperties.containsKey(PROJECT_NAME)) {
|
||||
projectName = ((String) additionalProperties.get(PROJECT_NAME));
|
||||
}
|
||||
if (additionalProperties.containsKey(PROJECT_DESCRIPTION)) {
|
||||
projectDescription = ((String) additionalProperties.get(PROJECT_DESCRIPTION));
|
||||
}
|
||||
if (additionalProperties.containsKey(PROJECT_VERSION)) {
|
||||
projectVersion = ((String) additionalProperties.get(PROJECT_VERSION));
|
||||
}
|
||||
|
||||
if (swagger.getInfo() != null) {
|
||||
Info info = swagger.getInfo();
|
||||
if (projectName == null && info.getTitle() != null) {
|
||||
// when projectName is not specified, generate it from info.title
|
||||
projectName = dashize(info.getTitle());
|
||||
}
|
||||
if (projectVersion == null) {
|
||||
// when projectVersion is not specified, use info.version
|
||||
projectVersion = info.getVersion();
|
||||
}
|
||||
if (projectDescription == null) {
|
||||
// when projectDescription is not specified, use info.description
|
||||
projectDescription = info.getDescription();
|
||||
}
|
||||
|
||||
if (info.getContact() != null) {
|
||||
Contact contact = info.getContact();
|
||||
if (additionalProperties.get(PROJECT_URL) == null) {
|
||||
additionalProperties.put(PROJECT_URL, contact.getUrl());
|
||||
}
|
||||
}
|
||||
if (info.getLicense() != null) {
|
||||
License license = info.getLicense();
|
||||
if (additionalProperties.get(LICENSE_NAME) == null) {
|
||||
additionalProperties.put(LICENSE_NAME, license.getName());
|
||||
}
|
||||
if (additionalProperties.get(LICENSE_URL) == null) {
|
||||
additionalProperties.put(LICENSE_URL, license.getUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default values
|
||||
if (projectName == null) {
|
||||
projectName = "swagger-clj-client";
|
||||
}
|
||||
if (projectVersion == null) {
|
||||
projectVersion = "1.0.0";
|
||||
}
|
||||
if (projectDescription == null) {
|
||||
projectDescription = "Client library of " + projectName;
|
||||
}
|
||||
|
||||
final String baseNamespace = dashize(projectName);
|
||||
apiPackage = baseNamespace + ".api";
|
||||
|
||||
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);
|
||||
|
||||
final String baseNamespaceFolder = sourceFolder + File.separator + namespaceToFolder(baseNamespace);
|
||||
supportingFiles.add(new SupportingFile("project.mustache", "", "project.clj"));
|
||||
supportingFiles.add(new SupportingFile("core.mustache", baseNamespaceFolder, "core.clj"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apiFileFolder() {
|
||||
return outputFolder + File.separator + sourceFolder + File.separator + namespaceToFolder(apiPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toOperationId(String operationId) {
|
||||
// throw exception if method name is empty
|
||||
if (StringUtils.isEmpty(operationId)) {
|
||||
throw new RuntimeException("Empty method/operation name (operationId) not allowed");
|
||||
}
|
||||
|
||||
return dashize(sanitizeName(operationId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toApiName(String name) {
|
||||
return dashize(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toParamName(String name) {
|
||||
return toVarName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toVarName(String name) {
|
||||
name = name.replaceAll("[^a-zA-Z0-9_-]+", "");
|
||||
name = dashize(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessOperations(Map<String, Object> operations) {
|
||||
Map<String, Object> objs = (Map<String, Object>) operations.get("operations");
|
||||
List<CodegenOperation> ops = (List<CodegenOperation>) objs.get("operation");
|
||||
for (CodegenOperation op : ops) {
|
||||
// Convert httpMethod to lower case, e.g. "get", "post"
|
||||
op.httpMethod = op.httpMethod.toLowerCase();
|
||||
}
|
||||
return operations;
|
||||
}
|
||||
|
||||
protected String namespaceToFolder(String ns) {
|
||||
return ns.replace(".", File.separator).replace("-", "_");
|
||||
}
|
||||
|
||||
protected String dashize(String s) {
|
||||
return underscore(s).replaceAll("[_ ]", "-");
|
||||
}
|
||||
}
|
@ -29,3 +29,4 @@ io.swagger.codegen.languages.TypeScriptAngularClientCodegen
|
||||
io.swagger.codegen.languages.TypeScriptNodeClientCodegen
|
||||
io.swagger.codegen.languages.AkkaScalaClientCodegen
|
||||
io.swagger.codegen.languages.CsharpDotNet2ClientCodegen
|
||||
io.swagger.codegen.languages.ClojureClientCodegen
|
||||
|
@ -0,0 +1,19 @@
|
||||
{{=< >=}}(ns <package>.<classname>
|
||||
(:require [<projectName>.core :refer [call-api check-required-params]])
|
||||
(:import (java.io File)))
|
||||
<#operations><#operation>
|
||||
(defn <nickname>
|
||||
"<&summary><#notes>
|
||||
<¬es></notes>"<#hasOptionalParams>
|
||||
([<#allParams><#required><#isFile>^File </isFile><paramName> </required></allParams>] (<nickname><#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>
|
||||
<#hasOptionalParams> </hasOptionalParams>(check-required-params<#allParams><#required> <paramName></required></allParams>)</hasRequiredParams>
|
||||
<#hasOptionalParams> </hasOptionalParams>(call-api "<path>" :<httpMethod>
|
||||
<#hasOptionalParams> </hasOptionalParams> {:path-params {<#pathParams>"<baseName>" <paramName> </pathParams>}
|
||||
<#hasOptionalParams> </hasOptionalParams> :header-params {<#headerParams>"<baseName>" <paramName> </headerParams>}
|
||||
<#hasOptionalParams> </hasOptionalParams> :query-params {<#queryParams>"<baseName>" <paramName> </queryParams>}
|
||||
<#hasOptionalParams> </hasOptionalParams> :form-params {<#formParams>"<baseName>" <paramName> </formParams>}<#bodyParam>
|
||||
<#hasOptionalParams> </hasOptionalParams> :body-param <paramName></bodyParam>
|
||||
<#hasOptionalParams> </hasOptionalParams> :content-types [<#consumes>"<mediaType>"<#hasMore> </hasMore></consumes>]
|
||||
<#hasOptionalParams> </hasOptionalParams> :accepts [<#produces>"<mediaType>"<#hasMore> </hasMore></produces>]}))<#hasOptionalParams>)</hasOptionalParams>
|
||||
</operation></operations>
|
178
modules/swagger-codegen/src/main/resources/clojure/core.mustache
Normal file
178
modules/swagger-codegen/src/main/resources/clojure/core.mustache
Normal file
@ -0,0 +1,178 @@
|
||||
(ns {{{baseNamespace}}}.core
|
||||
(:require [cheshire.core :refer [generate-string parse-string]]
|
||||
[clojure.string :as str]
|
||||
[clj-http.client :as client])
|
||||
(:import (com.fasterxml.jackson.core JsonParseException)
|
||||
(java.io File)
|
||||
(java.util Date TimeZone)
|
||||
(java.text SimpleDateFormat)))
|
||||
|
||||
(def default-api-context
|
||||
"Default API context."
|
||||
{:base-url "http://petstore.swagger.io/v2"
|
||||
:date-format "yyyy-MM-dd"
|
||||
:datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
|
||||
:debug false})
|
||||
|
||||
(def ^:dynamic *api-context*
|
||||
"Dynamic API context to be applied in API calls."
|
||||
default-api-context)
|
||||
|
||||
(defmacro with-api-context
|
||||
"A helper macro to wrap *api-context* with default values."
|
||||
[context & body]
|
||||
`(binding [*api-context* (merge *api-context* ~context)]
|
||||
~@body))
|
||||
|
||||
(defmacro check-required-params
|
||||
"Throw exception if the given parameter value is nil."
|
||||
[& params]
|
||||
(->> params
|
||||
(map (fn [p]
|
||||
`(if (nil? ~p)
|
||||
(throw (IllegalArgumentException. ~(str "The parameter \"" p "\" is required"))))))
|
||||
(list* 'do)))
|
||||
|
||||
(defn- make-date-format
|
||||
([format-str] (make-date-format format-str nil))
|
||||
([format-str time-zone]
|
||||
(let [date-format (SimpleDateFormat. format-str)]
|
||||
(when time-zone
|
||||
(.setTimeZone date-format (TimeZone/getTimeZone time-zone)))
|
||||
date-format)))
|
||||
|
||||
(defn format-date
|
||||
"Format the given Date object with the :date-format defined in *api-options*.
|
||||
NOTE: The UTC time zone is used."
|
||||
[^Date date]
|
||||
(let [{:keys [date-format]} *api-context*]
|
||||
(-> (make-date-format date-format "UTC")
|
||||
(.format date))))
|
||||
|
||||
(defn parse-date
|
||||
"Parse the given string to a Date object with the :date-format defined in *api-options*.
|
||||
NOTE: The UTC time zone is used."
|
||||
[^String s]
|
||||
(let [{:keys [date-format]} *api-context*]
|
||||
(-> (make-date-format date-format "UTC")
|
||||
(.parse s))))
|
||||
|
||||
(defn format-datetime
|
||||
"Format the given Date object with the :datetime-format defined in *api-options*.
|
||||
NOTE: The system's default time zone is used when not provided."
|
||||
([^Date date] (format-datetime date nil))
|
||||
([^Date date ^String time-zone]
|
||||
(let [{:keys [datetime-format]} *api-context*]
|
||||
(-> (make-date-format datetime-format time-zone)
|
||||
(.format date)))))
|
||||
|
||||
(defn parse-datetime
|
||||
"Parse the given string to a Date object with the :datetime-format defined in *api-options*.
|
||||
NOTE: The system's default time zone is used when not provided."
|
||||
([^String s] (parse-datetime s nil))
|
||||
([^String s ^String time-zone]
|
||||
(let [{:keys [datetime-format]} *api-context*]
|
||||
(-> (make-date-format datetime-format time-zone)
|
||||
(.parse s)))))
|
||||
|
||||
(defn param-to-str [param]
|
||||
"Format the given parameter value to string."
|
||||
(cond
|
||||
(instance? Date param) (format-datetime param)
|
||||
(sequential? param) (str/join "," param)
|
||||
:else (str param)))
|
||||
|
||||
(defn make-url
|
||||
"Make full URL by adding base URL and filling path parameters."
|
||||
[path path-params]
|
||||
(let [path (reduce (fn [p [k v]]
|
||||
(str/replace p (re-pattern (str "\\{" k "\\}")) (param-to-str v)))
|
||||
path
|
||||
path-params)]
|
||||
(str (:base-url *api-context*) path)))
|
||||
|
||||
(defn normalize-param
|
||||
"Normalize parameter value, handling three cases:
|
||||
for sequential value, normalize each elements of it;
|
||||
for File value, do nothing with it;
|
||||
otherwise, call `param-to-string`."
|
||||
[param]
|
||||
(cond
|
||||
(sequential? param) (map normalize-param param)
|
||||
(instance? File param) param
|
||||
:else (param-to-str param)))
|
||||
|
||||
(defn normalize-params
|
||||
"Normalize parameters values: remove nils, format to string with `param-to-str`."
|
||||
[params]
|
||||
(->> params
|
||||
(remove (comp nil? second))
|
||||
(map (fn [[k v]] [k (normalize-param v)]))
|
||||
(into {})))
|
||||
|
||||
(defn json-mime? [mime]
|
||||
"Check if the given MIME is a standard JSON MIME or :json."
|
||||
(if mime
|
||||
(or (= :json mime)
|
||||
(re-matches #"application/json(;.*)?" (name mime)))))
|
||||
|
||||
(defn json-preferred-mime [mimes]
|
||||
"Choose a MIME from the given MIMEs with JSON preferred,
|
||||
i.e. return JSON if included, otherwise return the first one."
|
||||
(-> (filter json-mime? mimes)
|
||||
first
|
||||
(or (first mimes))))
|
||||
|
||||
(defn serialize
|
||||
"Serialize the given data according to content-type.
|
||||
Only JSON is supported for now."
|
||||
[data content-type]
|
||||
(if (json-mime? content-type)
|
||||
(generate-string data {:date-format (:datetime-format *api-context*)})
|
||||
(throw (IllegalArgumentException. (str "Content type \"" content-type "\" is not support for serialization")))))
|
||||
|
||||
(defn deserialize
|
||||
"Deserialize the given HTTP response according to the Content-Type header."
|
||||
[{:keys [body] {:keys [content-type]} :headers}]
|
||||
(cond
|
||||
(json-mime? content-type)
|
||||
(try
|
||||
(parse-string body true)
|
||||
(catch JsonParseException e
|
||||
;; return the body string directly on JSON parsing error
|
||||
body))
|
||||
;; for non-JSON response, return the body string directly
|
||||
:else body))
|
||||
|
||||
(defn form-params-to-multipart
|
||||
"Convert the given form parameters map into a vector as clj-http's :multipart option."
|
||||
[form-params]
|
||||
(->> form-params
|
||||
(map (fn [[k v]] (array-map :name k :content v)))
|
||||
vec))
|
||||
|
||||
(defn call-api
|
||||
"Call an API by making HTTP request and return its response."
|
||||
[path method {:keys [path-params query-params header-params form-params body-param content-types accepts]}]
|
||||
(let [{:keys [debug]} *api-context*
|
||||
url (make-url path path-params)
|
||||
content-type (or (json-preferred-mime content-types)
|
||||
(and body-param :json))
|
||||
accept (or (json-preferred-mime accepts) :json)
|
||||
multipart? (= "multipart/form-data" content-type)
|
||||
opts (cond-> {:url url :method method}
|
||||
accept (assoc :accept accept)
|
||||
(seq query-params) (assoc :query-params (normalize-params query-params))
|
||||
(seq header-params) (assoc :header-params (normalize-params header-params))
|
||||
(and content-type (not multipart?)) (assoc :content-type content-type)
|
||||
multipart? (assoc :multipart (-> form-params
|
||||
normalize-params
|
||||
form-params-to-multipart))
|
||||
(and (not multipart?) (seq form-params)) (assoc :form-params (normalize-params form-params))
|
||||
body-param (assoc :body (serialize body-param content-type))
|
||||
debug (assoc :debug true :debug-body true))
|
||||
resp (client/request opts)]
|
||||
(when debug
|
||||
(println "Response:")
|
||||
(println resp))
|
||||
(deserialize resp)))
|
@ -0,0 +1,8 @@
|
||||
{{=< >=}}(defproject <&projectName> "<&projectVersion>"
|
||||
:description "<&projectDescription>"<#projectUrl>
|
||||
:url "<&projectUrl>"</projectUrl><#licenseName>
|
||||
:license {:name "<&licenseName>"<#licenseUrl>
|
||||
:url "<&licenseUrl>"</licenseUrl>}</licenseName>
|
||||
:dependencies [[org.clojure/clojure "1.7.0"]
|
||||
[clj-http "2.0.0"]
|
||||
[cheshire "5.5.0"]])
|
11
samples/client/petstore/clojure/.gitignore
vendored
Normal file
11
samples/client/petstore/clojure/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/target
|
||||
/classes
|
||||
/checkouts
|
||||
pom.xml
|
||||
pom.xml.asc
|
||||
*.jar
|
||||
*.class
|
||||
/.lein-*
|
||||
/.nrepl-port
|
||||
.hgignore
|
||||
.hg/
|
7
samples/client/petstore/clojure/project.clj
Normal file
7
samples/client/petstore/clojure/project.clj
Normal file
@ -0,0 +1,7 @@
|
||||
(defproject swagger-petstore "1.0.0"
|
||||
:description "This is a sample server Petstore server. You can find out more about Swagger at <a href=\"http://swagger.io\">http://swagger.io</a> or on irc.freenode.net, #swagger. 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 "2.0.0"]
|
||||
[cheshire "5.5.0"]])
|
1
samples/client/petstore/clojure/resources/hello.txt
Normal file
1
samples/client/petstore/clojure/resources/hello.txt
Normal file
@ -0,0 +1 @@
|
||||
Hello world!
|
108
samples/client/petstore/clojure/src/swagger_petstore/api/pet.clj
Normal file
108
samples/client/petstore/clojure/src/swagger_petstore/api/pet.clj
Normal file
@ -0,0 +1,108 @@
|
||||
(ns swagger-petstore.api.pet
|
||||
(:require [swagger-petstore.core :refer [call-api check-required-params]])
|
||||
(:import (java.io File)))
|
||||
|
||||
(defn update-pet
|
||||
"Update an existing pet
|
||||
"
|
||||
([] (update-pet nil))
|
||||
([{:keys [body ]}]
|
||||
(call-api "/pet" :put
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:body-param body
|
||||
:content-types ["application/json" "application/xml"]
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn add-pet
|
||||
"Add a new pet to the store
|
||||
"
|
||||
([] (add-pet nil))
|
||||
([{:keys [body ]}]
|
||||
(call-api "/pet" :post
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:body-param body
|
||||
:content-types ["application/json" "application/xml"]
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn find-pets-by-status
|
||||
"Finds Pets by status
|
||||
Multiple status values can be provided with comma seperated strings"
|
||||
([] (find-pets-by-status nil))
|
||||
([{:keys [status ]}]
|
||||
(call-api "/pet/findByStatus" :get
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {"status" status }
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn find-pets-by-tags
|
||||
"Finds Pets by tags
|
||||
Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing."
|
||||
([] (find-pets-by-tags nil))
|
||||
([{:keys [tags ]}]
|
||||
(call-api "/pet/findByTags" :get
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {"tags" tags }
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn get-pet-by-id
|
||||
"Find pet by ID
|
||||
Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions"
|
||||
[pet-id ]
|
||||
(call-api "/pet/{petId}" :get
|
||||
{:path-params {"petId" pet-id }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]}))
|
||||
|
||||
(defn update-pet-with-form
|
||||
"Updates a pet in the store with form data
|
||||
"
|
||||
([pet-id ] (update-pet-with-form pet-id nil))
|
||||
([pet-id {:keys [name status ]}]
|
||||
(call-api "/pet/{petId}" :post
|
||||
{:path-params {"petId" pet-id }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {"name" name "status" status }
|
||||
:content-types ["application/x-www-form-urlencoded"]
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn delete-pet
|
||||
"Deletes a pet
|
||||
"
|
||||
([pet-id ] (delete-pet pet-id nil))
|
||||
([pet-id {:keys [api-key ]}]
|
||||
(call-api "/pet/{petId}" :delete
|
||||
{:path-params {"petId" pet-id }
|
||||
:header-params {"api_key" api-key }
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn upload-file
|
||||
"uploads an image
|
||||
"
|
||||
([pet-id ] (upload-file pet-id nil))
|
||||
([pet-id {:keys [additional-metadata ^File file ]}]
|
||||
(call-api "/pet/{petId}/uploadImage" :post
|
||||
{:path-params {"petId" pet-id }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {"additionalMetadata" additional-metadata "file" file }
|
||||
:content-types ["multipart/form-data"]
|
||||
:accepts ["application/json" "application/xml"]})))
|
@ -0,0 +1,53 @@
|
||||
(ns swagger-petstore.api.store
|
||||
(:require [swagger-petstore.core :refer [call-api check-required-params]])
|
||||
(:import (java.io File)))
|
||||
|
||||
(defn get-inventory
|
||||
"Returns pet inventories by status
|
||||
Returns a map of status codes to quantities"
|
||||
[]
|
||||
(call-api "/store/inventory" :get
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]}))
|
||||
|
||||
(defn place-order
|
||||
"Place an order for a pet
|
||||
"
|
||||
([] (place-order nil))
|
||||
([{:keys [body ]}]
|
||||
(call-api "/store/order" :post
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:body-param body
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn get-order-by-id
|
||||
"Find purchase order by ID
|
||||
For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions"
|
||||
[order-id ]
|
||||
(call-api "/store/order/{orderId}" :get
|
||||
{:path-params {"orderId" order-id }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]}))
|
||||
|
||||
(defn delete-order
|
||||
"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 ]
|
||||
(call-api "/store/order/{orderId}" :delete
|
||||
{:path-params {"orderId" order-id }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]}))
|
@ -0,0 +1,108 @@
|
||||
(ns swagger-petstore.api.user
|
||||
(:require [swagger-petstore.core :refer [call-api check-required-params]])
|
||||
(:import (java.io File)))
|
||||
|
||||
(defn create-user
|
||||
"Create user
|
||||
This can only be done by the logged in user."
|
||||
([] (create-user nil))
|
||||
([{:keys [body ]}]
|
||||
(call-api "/user" :post
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:body-param body
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn create-users-with-array-input
|
||||
"Creates list of users with given input array
|
||||
"
|
||||
([] (create-users-with-array-input nil))
|
||||
([{:keys [body ]}]
|
||||
(call-api "/user/createWithArray" :post
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:body-param body
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn create-users-with-list-input
|
||||
"Creates list of users with given input array
|
||||
"
|
||||
([] (create-users-with-list-input nil))
|
||||
([{:keys [body ]}]
|
||||
(call-api "/user/createWithList" :post
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:body-param body
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn login-user
|
||||
"Logs user into the system
|
||||
"
|
||||
([] (login-user nil))
|
||||
([{:keys [username password ]}]
|
||||
(call-api "/user/login" :get
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {"username" username "password" password }
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn logout-user
|
||||
"Logs out current logged in user session
|
||||
"
|
||||
[]
|
||||
(call-api "/user/logout" :get
|
||||
{:path-params {}
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]}))
|
||||
|
||||
(defn get-user-by-name
|
||||
"Get user by user name
|
||||
"
|
||||
[username ]
|
||||
(call-api "/user/{username}" :get
|
||||
{:path-params {"username" username }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]}))
|
||||
|
||||
(defn update-user
|
||||
"Updated user
|
||||
This can only be done by the logged in user."
|
||||
([username ] (update-user username nil))
|
||||
([username {:keys [body ]}]
|
||||
(call-api "/user/{username}" :put
|
||||
{:path-params {"username" username }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:body-param body
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]})))
|
||||
|
||||
(defn delete-user
|
||||
"Delete user
|
||||
This can only be done by the logged in user."
|
||||
[username ]
|
||||
(call-api "/user/{username}" :delete
|
||||
{:path-params {"username" username }
|
||||
:header-params {}
|
||||
:query-params {}
|
||||
:form-params {}
|
||||
:content-types []
|
||||
:accepts ["application/json" "application/xml"]}))
|
178
samples/client/petstore/clojure/src/swagger_petstore/core.clj
Normal file
178
samples/client/petstore/clojure/src/swagger_petstore/core.clj
Normal file
@ -0,0 +1,178 @@
|
||||
(ns swagger-petstore.core
|
||||
(:require [cheshire.core :refer [generate-string parse-string]]
|
||||
[clojure.string :as str]
|
||||
[clj-http.client :as client])
|
||||
(:import (com.fasterxml.jackson.core JsonParseException)
|
||||
(java.io File)
|
||||
(java.util Date TimeZone)
|
||||
(java.text SimpleDateFormat)))
|
||||
|
||||
(def default-api-context
|
||||
"Default API context."
|
||||
{:base-url "http://petstore.swagger.io/v2"
|
||||
:date-format "yyyy-MM-dd"
|
||||
:datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
|
||||
:debug false})
|
||||
|
||||
(def ^:dynamic *api-context*
|
||||
"Dynamic API context to be applied in API calls."
|
||||
default-api-context)
|
||||
|
||||
(defmacro with-api-context
|
||||
"A helper macro to wrap *api-context* with default values."
|
||||
[context & body]
|
||||
`(binding [*api-context* (merge *api-context* ~context)]
|
||||
~@body))
|
||||
|
||||
(defmacro check-required-params
|
||||
"Throw exception if the given parameter value is nil."
|
||||
[& params]
|
||||
(->> params
|
||||
(map (fn [p]
|
||||
`(if (nil? ~p)
|
||||
(throw (IllegalArgumentException. ~(str "The parameter \"" p "\" is required"))))))
|
||||
(list* 'do)))
|
||||
|
||||
(defn- make-date-format
|
||||
([format-str] (make-date-format format-str nil))
|
||||
([format-str time-zone]
|
||||
(let [date-format (SimpleDateFormat. format-str)]
|
||||
(when time-zone
|
||||
(.setTimeZone date-format (TimeZone/getTimeZone time-zone)))
|
||||
date-format)))
|
||||
|
||||
(defn format-date
|
||||
"Format the given Date object with the :date-format defined in *api-options*.
|
||||
NOTE: The UTC time zone is used."
|
||||
[^Date date]
|
||||
(let [{:keys [date-format]} *api-context*]
|
||||
(-> (make-date-format date-format "UTC")
|
||||
(.format date))))
|
||||
|
||||
(defn parse-date
|
||||
"Parse the given string to a Date object with the :date-format defined in *api-options*.
|
||||
NOTE: The UTC time zone is used."
|
||||
[^String s]
|
||||
(let [{:keys [date-format]} *api-context*]
|
||||
(-> (make-date-format date-format "UTC")
|
||||
(.parse s))))
|
||||
|
||||
(defn format-datetime
|
||||
"Format the given Date object with the :datetime-format defined in *api-options*.
|
||||
NOTE: The system's default time zone is used when not provided."
|
||||
([^Date date] (format-datetime date nil))
|
||||
([^Date date ^String time-zone]
|
||||
(let [{:keys [datetime-format]} *api-context*]
|
||||
(-> (make-date-format datetime-format time-zone)
|
||||
(.format date)))))
|
||||
|
||||
(defn parse-datetime
|
||||
"Parse the given string to a Date object with the :datetime-format defined in *api-options*.
|
||||
NOTE: The system's default time zone is used when not provided."
|
||||
([^String s] (parse-datetime s nil))
|
||||
([^String s ^String time-zone]
|
||||
(let [{:keys [datetime-format]} *api-context*]
|
||||
(-> (make-date-format datetime-format time-zone)
|
||||
(.parse s)))))
|
||||
|
||||
(defn param-to-str [param]
|
||||
"Format the given parameter value to string."
|
||||
(cond
|
||||
(instance? Date param) (format-datetime param)
|
||||
(sequential? param) (str/join "," param)
|
||||
:else (str param)))
|
||||
|
||||
(defn make-url
|
||||
"Make full URL by adding base URL and filling path parameters."
|
||||
[path path-params]
|
||||
(let [path (reduce (fn [p [k v]]
|
||||
(str/replace p (re-pattern (str "\\{" k "\\}")) (param-to-str v)))
|
||||
path
|
||||
path-params)]
|
||||
(str (:base-url *api-context*) path)))
|
||||
|
||||
(defn normalize-param
|
||||
"Normalize parameter value, handling three cases:
|
||||
for sequential value, normalize each elements of it;
|
||||
for File value, do nothing with it;
|
||||
otherwise, call `param-to-string`."
|
||||
[param]
|
||||
(cond
|
||||
(sequential? param) (map normalize-param param)
|
||||
(instance? File param) param
|
||||
:else (param-to-str param)))
|
||||
|
||||
(defn normalize-params
|
||||
"Normalize parameters values: remove nils, format to string with `param-to-str`."
|
||||
[params]
|
||||
(->> params
|
||||
(remove (comp nil? second))
|
||||
(map (fn [[k v]] [k (normalize-param v)]))
|
||||
(into {})))
|
||||
|
||||
(defn json-mime? [mime]
|
||||
"Check if the given MIME is a standard JSON MIME or :json."
|
||||
(if mime
|
||||
(or (= :json mime)
|
||||
(re-matches #"application/json(;.*)?" (name mime)))))
|
||||
|
||||
(defn json-preferred-mime [mimes]
|
||||
"Choose a MIME from the given MIMEs with JSON preferred,
|
||||
i.e. return JSON if included, otherwise return the first one."
|
||||
(-> (filter json-mime? mimes)
|
||||
first
|
||||
(or (first mimes))))
|
||||
|
||||
(defn serialize
|
||||
"Serialize the given data according to content-type.
|
||||
Only JSON is supported for now."
|
||||
[data content-type]
|
||||
(if (json-mime? content-type)
|
||||
(generate-string data {:date-format (:datetime-format *api-context*)})
|
||||
(throw (IllegalArgumentException. (str "Content type \"" content-type "\" is not support for serialization")))))
|
||||
|
||||
(defn deserialize
|
||||
"Deserialize the given HTTP response according to the Content-Type header."
|
||||
[{:keys [body] {:keys [content-type]} :headers}]
|
||||
(cond
|
||||
(json-mime? content-type)
|
||||
(try
|
||||
(parse-string body true)
|
||||
(catch JsonParseException e
|
||||
;; return the body string directly on JSON parsing error
|
||||
body))
|
||||
;; for non-JSON response, return the body string directly
|
||||
:else body))
|
||||
|
||||
(defn form-params-to-multipart
|
||||
"Convert the given form parameters map into a vector as clj-http's :multipart option."
|
||||
[form-params]
|
||||
(->> form-params
|
||||
(map (fn [[k v]] (array-map :name k :content v)))
|
||||
vec))
|
||||
|
||||
(defn call-api
|
||||
"Call an API by making HTTP request and return its response."
|
||||
[path method {:keys [path-params query-params header-params form-params body-param content-types accepts]}]
|
||||
(let [{:keys [debug]} *api-context*
|
||||
url (make-url path path-params)
|
||||
content-type (or (json-preferred-mime content-types)
|
||||
(and body-param :json))
|
||||
accept (or (json-preferred-mime accepts) :json)
|
||||
multipart? (= "multipart/form-data" content-type)
|
||||
opts (cond-> {:url url :method method}
|
||||
accept (assoc :accept accept)
|
||||
(seq query-params) (assoc :query-params (normalize-params query-params))
|
||||
(seq header-params) (assoc :header-params (normalize-params header-params))
|
||||
(and content-type (not multipart?)) (assoc :content-type content-type)
|
||||
multipart? (assoc :multipart (-> form-params
|
||||
normalize-params
|
||||
form-params-to-multipart))
|
||||
(and (not multipart?) (seq form-params)) (assoc :form-params (normalize-params form-params))
|
||||
body-param (assoc :body (serialize body-param content-type))
|
||||
debug (assoc :debug true :debug-body true))
|
||||
resp (client/request opts)]
|
||||
(when debug
|
||||
(println "Response:")
|
||||
(println resp))
|
||||
(deserialize resp)))
|
@ -0,0 +1,85 @@
|
||||
(ns swagger-petstore.api.pet-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[clojure.java.io :as io]
|
||||
[swagger-petstore.api.pet :refer :all]))
|
||||
|
||||
(defn- make-random-pet
|
||||
([] (make-random-pet nil))
|
||||
([{:keys [id] :as attrs :or {id (System/currentTimeMillis)}}]
|
||||
(merge {:id id
|
||||
:name (str "pet-" id)
|
||||
:status "available"
|
||||
:photoUrls ["http://foo.bar.com/1" "http://foo.bar.com/2"]
|
||||
:category {:name "really-happy"}}
|
||||
attrs)))
|
||||
|
||||
(deftest test-create-and-get-pet
|
||||
(let [{:keys [id] :as pet} (make-random-pet)
|
||||
_ (add-pet {:body pet})
|
||||
fetched (get-pet-by-id id)]
|
||||
(is (identity fetched))
|
||||
(is (= id (:id fetched)))
|
||||
(is (identity (:category fetched)))
|
||||
(is (= (get-in pet [:category :name]) (get-in fetched [:category :name])))
|
||||
(delete-pet id)))
|
||||
|
||||
(deftest test-find-pets-by-status
|
||||
(let [status "pending"
|
||||
{:keys [id] :as pet} (make-random-pet {:status status})
|
||||
_ (add-pet {:body pet})
|
||||
pets (find-pets-by-status {:status [status]})]
|
||||
(is (seq pets))
|
||||
(is (some #{id} (map :id pets)))
|
||||
(delete-pet id)))
|
||||
|
||||
(deftest test-find-pets-by-tags
|
||||
(let [tag-name (str "tag-" (rand-int 1000))
|
||||
tag {:name tag-name}
|
||||
{:keys [id] :as pet} (make-random-pet {:tags [tag]})
|
||||
_ (add-pet {:body pet})
|
||||
pets (find-pets-by-tags {:tags [tag-name]})]
|
||||
(is (seq pets))
|
||||
(is (some #{id} (map :id pets)))
|
||||
(delete-pet id)))
|
||||
|
||||
(deftest test-update-pet-with-form
|
||||
(let [{pet-id :id :as pet} (make-random-pet {:name "new name" :status "available"})
|
||||
_ (add-pet {:body pet})
|
||||
{:keys [id name status]} (get-pet-by-id pet-id)]
|
||||
(is (= pet-id id))
|
||||
(is (= "new name" name))
|
||||
(is (= "available" status))
|
||||
;; update "name" only
|
||||
(update-pet-with-form pet-id {:name "updated name 1"})
|
||||
(let [{:keys [id name status]} (get-pet-by-id pet-id)]
|
||||
(is (= pet-id id))
|
||||
(is (= "updated name 1" name))
|
||||
(is (= "available" status)))
|
||||
;; update "status" only
|
||||
(update-pet-with-form pet-id {:status "pending"})
|
||||
(let [{:keys [id name status]} (get-pet-by-id pet-id)]
|
||||
(is (= pet-id id))
|
||||
(is (= "updated name 1" name))
|
||||
(is (= "pending" status)))
|
||||
;; update both "name" and "status"
|
||||
(update-pet-with-form pet-id {:name "updated name 2" :status "sold"})
|
||||
(let [{:keys [id name status]} (get-pet-by-id pet-id)]
|
||||
(is (= pet-id id))
|
||||
(is (= "updated name 2" name))
|
||||
(is (= "sold" status)))
|
||||
(delete-pet pet-id)))
|
||||
|
||||
(deftest test-delete-pet
|
||||
(let [{:keys [id] :as pet} (make-random-pet)
|
||||
_ (add-pet {:body pet})
|
||||
fetched (get-pet-by-id id)]
|
||||
(is (= id (:id fetched)))
|
||||
(delete-pet id)
|
||||
(is (thrown? RuntimeException (get-pet-by-id id)))))
|
||||
|
||||
(deftest test-upload-file
|
||||
(let [{:keys [id] :as pet} (make-random-pet)
|
||||
_ (add-pet {:body pet})
|
||||
file (io/file (io/resource "hello.txt"))]
|
||||
;; no errors with upload-file
|
||||
(upload-file id {:file file :additional-metadata "uploading file with clojure client"})))
|
@ -0,0 +1,27 @@
|
||||
(ns swagger-petstore.api.store-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[swagger-petstore.api.store :refer :all])
|
||||
(:import (java.util Date)))
|
||||
|
||||
(defn- make-random-order []
|
||||
{:id (+ 90000 (rand-int 10000))
|
||||
:petId 200
|
||||
:quantity 13
|
||||
:shipDate (Date.)
|
||||
:status "placed"
|
||||
:complete true})
|
||||
|
||||
(deftest test-get-inventory
|
||||
(let [inventory (get-inventory)]
|
||||
(is (pos? (count inventory)))))
|
||||
|
||||
(deftest test-place-and-delete-order
|
||||
(let [order (make-random-order)
|
||||
order-id (:id order)
|
||||
_ (place-order {:body order})
|
||||
fetched (get-order-by-id order-id)]
|
||||
(doseq [attr [:id :petId :quantity]]
|
||||
(is (= (attr order) (attr fetched))))
|
||||
(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))))))
|
@ -0,0 +1,57 @@
|
||||
(ns swagger-petstore.api.user-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[swagger-petstore.api.user :refer :all]))
|
||||
|
||||
(defn- make-random-user
|
||||
([] (make-random-user nil))
|
||||
([{:keys [id] :as attrs :or {id (System/currentTimeMillis)}}]
|
||||
(merge {:id id
|
||||
:username (str "user-" id)
|
||||
:password "my-password"
|
||||
:userStatus 0}
|
||||
attrs)))
|
||||
|
||||
(deftest test-create-and-delete-user
|
||||
(let [user (make-random-user)
|
||||
username (:username user)
|
||||
_ (create-user {:body user})
|
||||
fetched (get-user-by-name username)]
|
||||
(doseq [attr [:id :username :password :userStatus]]
|
||||
(is (= (attr user) (attr fetched))))
|
||||
(delete-user username)
|
||||
(is (thrown? RuntimeException (get-user-by-name username)))))
|
||||
|
||||
(deftest test-create-users-with-array-input
|
||||
(let [id1 (System/currentTimeMillis)
|
||||
id2 (inc id1)
|
||||
user1 (make-random-user {:id id1})
|
||||
user2 (make-random-user {:id id2})]
|
||||
(create-users-with-array-input {:body [user1 user2]})
|
||||
(let [fetched (get-user-by-name (:username user1))]
|
||||
(is (= id1 (:id fetched))))
|
||||
(let [fetched (get-user-by-name (:username user2))]
|
||||
(is (= id2 (:id fetched))))
|
||||
(delete-user (:username user1))
|
||||
(delete-user (:username user2))))
|
||||
|
||||
(deftest test-create-users-with-list-input
|
||||
(let [id1 (System/currentTimeMillis)
|
||||
id2 (inc id1)
|
||||
user1 (make-random-user {:id id1})
|
||||
user2 (make-random-user {:id id2})]
|
||||
(create-users-with-list-input {:body [user1 user2]})
|
||||
(let [fetched (get-user-by-name (:username user1))]
|
||||
(is (= id1 (:id fetched))))
|
||||
(let [fetched (get-user-by-name (:username user2))]
|
||||
(is (= id2 (:id fetched))))
|
||||
(delete-user (:username user1))
|
||||
(delete-user (:username user2))))
|
||||
|
||||
(deftest test-login-and-lougout-user
|
||||
(let [{:keys [username password] :as user} (make-random-user)
|
||||
_ (create-user {:body user})
|
||||
result (login-user {:username username :password password})]
|
||||
(is (re-matches #"logged in user session:.+" result))
|
||||
;; no error with logout-user
|
||||
(logout-user)
|
||||
(delete-user username)))
|
@ -0,0 +1,135 @@
|
||||
(ns swagger-petstore.core-test
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.test :refer :all]
|
||||
[swagger-petstore.core :refer :all])
|
||||
(:import (java.text ParseException)))
|
||||
|
||||
(deftest test-api-context
|
||||
(testing "default"
|
||||
(is (= {:base-url "http://petstore.swagger.io/v2"
|
||||
:date-format "yyyy-MM-dd"
|
||||
:datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
|
||||
:debug false}
|
||||
default-api-context
|
||||
*api-context*
|
||||
(with-api-context {}
|
||||
*api-context*))))
|
||||
(testing "customize via with-api-context"
|
||||
(with-api-context {:base-url "http://localhost" :debug true}
|
||||
(is (= {:base-url "http://localhost"
|
||||
:date-format "yyyy-MM-dd"
|
||||
:datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
|
||||
:debug true}
|
||||
*api-context*))
|
||||
;; nested with-api-context inherits values from the outer api context
|
||||
(with-api-context {:datetime-format "yyyy-MM-dd HH:mm:ss"}
|
||||
(is (= {:base-url "http://localhost"
|
||||
:date-format "yyyy-MM-dd"
|
||||
:datetime-format "yyyy-MM-dd HH:mm:ss"
|
||||
:debug true}
|
||||
*api-context*))))
|
||||
;; back to default api context
|
||||
(is (= {:base-url "http://petstore.swagger.io/v2"
|
||||
:date-format "yyyy-MM-dd"
|
||||
:datetime-format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
|
||||
:debug false}
|
||||
*api-context*))))
|
||||
|
||||
(deftest test-check-required-params
|
||||
(let [a nil b :not-nil]
|
||||
(is (thrown? IllegalArgumentException (check-required-params a)))
|
||||
(is (nil? (check-required-params b)))))
|
||||
|
||||
(deftest test-parse-and-format-date
|
||||
(testing "default date format"
|
||||
(is (= "2015-11-07" (-> "2015-11-07T03:49:09.356+00:00" parse-datetime format-date)))
|
||||
(is (= "2015-11-07" (-> "2015-11-07" parse-date format-date)))
|
||||
(is (thrown? ParseException (parse-date "2015-11"))))
|
||||
(testing "custom date format: without day"
|
||||
(with-api-context {:date-format "yyyy-MM"}
|
||||
(is (= "2015-11" (-> "2015-11-07T03:49:09.123Z" parse-datetime format-date)))
|
||||
(is (= "2015-11" (-> "2015-11" parse-date format-date)))
|
||||
(is (thrown? ParseException (parse-date "2015"))))))
|
||||
|
||||
(deftest test-parse-and-format-datetime
|
||||
(testing "default datetime format"
|
||||
(are [s]
|
||||
(is (= "2015-11-07T03:49:09.356Z" (-> s parse-datetime (format-datetime "UTC"))))
|
||||
"2015-11-07T03:49:09.356+00:00"
|
||||
"2015-11-07T05:49:09.356+02:00"
|
||||
"2015-11-07T02:49:09.356-01:00"
|
||||
"2015-11-07T03:49:09.356Z")
|
||||
(is (thrown? ParseException (parse-datetime "2015-11-07 03:49:09"))))
|
||||
(testing "custom datetime format: without milliseconds"
|
||||
(with-api-context {:datetime-format "yyyy-MM-dd'T'HH:mm:ssXXX"}
|
||||
(are [s]
|
||||
(is (= "2015-11-07T13:49:09+10:00" (-> s parse-datetime (format-datetime "GMT+10"))))
|
||||
"2015-11-07T03:49:09+00:00"
|
||||
"2015-11-07T03:49:09Z"
|
||||
"2015-11-07T00:49:09-03:00")
|
||||
(is (thrown? ParseException (parse-datetime "2015-11-07T03:49:09.123Z"))))))
|
||||
|
||||
(deftest test-param-to-str
|
||||
(let [date (parse-datetime "2015-11-07T03:49:09.123Z")]
|
||||
(are [param expected]
|
||||
(is (= expected (param-to-str param)))
|
||||
nil ""
|
||||
"abc" "abc"
|
||||
123 "123"
|
||||
1.0 "1.0"
|
||||
[12 "34"] "12,34"
|
||||
date (format-datetime date))))
|
||||
|
||||
(deftest test-make-url
|
||||
(are [path path-params url]
|
||||
(is (= url (make-url path path-params)))
|
||||
"/pet/{petId}" {"petId" 123} "http://petstore.swagger.io/v2/pet/123"
|
||||
"/" nil "http://petstore.swagger.io/v2/"
|
||||
"/pet" {"id" 1} "http://petstore.swagger.io/v2/pet"
|
||||
"/pet/{id}" nil "http://petstore.swagger.io/v2/pet/{id}"))
|
||||
|
||||
(deftest test-normalize-param
|
||||
(let [file (-> "hello.txt" io/resource io/file)]
|
||||
(are [param expected]
|
||||
(is (= expected (normalize-param param)))
|
||||
[12 "34"] ["12" "34"]
|
||||
file file
|
||||
"abc" "abc"
|
||||
[[12 "34"] file "abc"] [["12" "34"] file "abc"])))
|
||||
|
||||
(deftest test-normalize-params
|
||||
(is (= {:a "123" :b ["4" ["5" "6"]]}
|
||||
(normalize-params {:a 123 :b [4 [5 "6"]] :c nil}))))
|
||||
|
||||
(deftest test-json-mime?
|
||||
(are [mime expected]
|
||||
(is (= expected (boolean (json-mime? mime))))
|
||||
:json true
|
||||
"application/json" true
|
||||
"application/json; charset=utf8" true
|
||||
nil false
|
||||
:xml false
|
||||
"application/pdf" false))
|
||||
|
||||
(deftest test-json-preferred-mime
|
||||
(are [mimes expected]
|
||||
(is (= expected (json-preferred-mime mimes)))
|
||||
["application/xml" "application/json"] "application/json"
|
||||
[:json] :json
|
||||
[] nil
|
||||
nil nil
|
||||
["application/xml"] "application/xml"))
|
||||
|
||||
(deftest test-serialize
|
||||
(is (= "{\"aa\":1,\"bb\":\"2\"}" (serialize {:aa 1 :bb "2"} :json)))
|
||||
(is (= "{}" (serialize {} "application/json")))
|
||||
(is (= "[1,\"2\"]" (serialize [1 "2"] "application/json; charset=UTF8")))
|
||||
(is (thrown? IllegalArgumentException (serialize {} "application/xml"))))
|
||||
|
||||
(deftest test-deserialize
|
||||
(are [body content-type expected]
|
||||
(is (= expected (deserialize {:body body :headers {:content-type content-type}})))
|
||||
"{\"aa\": 1, \"bb\": \"2\"}" "application/json" {:aa 1 :bb "2"}
|
||||
"[1, \"2\"]" "application/json; charset=UTF8" [1 "2"]
|
||||
"{invalid json}" "application/json" "{invalid json}"
|
||||
"plain text" "text/plain" "plain text"))
|
Loading…
x
Reference in New Issue
Block a user