forked from loafle/openapi-generator-original
[haskell-http-client] add support for auth methods (#6622)
* add support for auth methods * use newtypes for required params * fix duplicate operationId issues * prevent aliasing of vendorextension references in fromOperation * add --fast to stack ci build
This commit is contained in:
@@ -5,15 +5,14 @@ import io.swagger.models.Model;
|
||||
import io.swagger.models.ModelImpl;
|
||||
import io.swagger.models.Operation;
|
||||
import io.swagger.models.Swagger;
|
||||
import io.swagger.models.properties.ArrayProperty;
|
||||
import io.swagger.models.properties.MapProperty;
|
||||
import io.swagger.models.properties.Property;
|
||||
import io.swagger.models.properties.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import io.swagger.models.auth.SecuritySchemeDefinition;
|
||||
import io.swagger.codegen.CliOption;
|
||||
import io.swagger.codegen.CodegenConstants;
|
||||
import io.swagger.codegen.CodegenModel;
|
||||
@@ -26,6 +25,7 @@ import java.io.IOException;
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
@@ -65,8 +65,8 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
static final String MEDIA_IS_JSON = "x-mediaIsJson";
|
||||
|
||||
|
||||
protected Map<String, CodegenParameter> uniqueOptionalParamsByName = new HashMap<String, CodegenParameter>();
|
||||
protected Map<String, CodegenModel> modelNames = new HashMap<String, CodegenModel>();
|
||||
protected Map<String, CodegenParameter> uniqueParamsByName = new HashMap<String, CodegenParameter>();
|
||||
protected Set<String> typeNames = new HashSet<String>();
|
||||
protected Map<String, Map<String,String>> allMimeTypes = new HashMap<String, Map<String,String>>();
|
||||
protected Map<String, String> knownMimeDataTypes = new HashMap<String, String>();
|
||||
protected Map<String, Set<String>> modelMimeTypes = new HashMap<String, Set<String>>();
|
||||
@@ -466,42 +466,49 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
public CodegenOperation fromOperation(String resourcePath, String httpMethod, Operation operation, Map<String, Model> definitions, Swagger swagger) {
|
||||
CodegenOperation op = super.fromOperation(resourcePath, httpMethod, operation, definitions, swagger);
|
||||
|
||||
op.vendorExtensions.put("x-baseOperationId", op.operationId);
|
||||
// prevent aliasing/sharing of operation.vendorExtensions reference
|
||||
op.vendorExtensions = new LinkedHashMap();
|
||||
|
||||
String operationType = toTypeName("Op", op.operationId);
|
||||
op.vendorExtensions.put("x-operationType", operationType);
|
||||
typeNames.add(operationType);
|
||||
|
||||
op.vendorExtensions.put("x-haddockPath", String.format("%s %s", op.httpMethod, op.path.replace("/", "\\/")));
|
||||
op.operationId = toVarName(op.operationId);
|
||||
op.vendorExtensions.put("x-operationType", toTypeName("Op", op.operationId));
|
||||
op.vendorExtensions.put("x-hasBodyOrFormParam", op.getHasBodyParam() || op.getHasFormParams());
|
||||
|
||||
for (CodegenParameter param : op.allParams) {
|
||||
param.vendorExtensions.put("x-operationType", WordUtils.capitalize(op.operationId));
|
||||
param.vendorExtensions = new LinkedHashMap(); // prevent aliasing/sharing
|
||||
param.vendorExtensions.put("x-operationType", operationType);
|
||||
param.vendorExtensions.put("x-isBodyOrFormParam", param.isBodyParam || param.isFormParam);
|
||||
if (!StringUtils.isBlank(param.collectionFormat)) {
|
||||
param.vendorExtensions.put("x-collectionFormat", mapCollectionFormat(param.collectionFormat));
|
||||
}
|
||||
if (!param.required) {
|
||||
if(!param.required) {
|
||||
op.vendorExtensions.put("x-hasOptionalParams", true);
|
||||
|
||||
}
|
||||
if (typeMapping.containsKey(param.dataType) || param.isPrimitiveType || param.isListContainer || param.isMapContainer || param.isFile) {
|
||||
String paramNameType = toTypeName("Param", param.paramName);
|
||||
|
||||
if (uniqueOptionalParamsByName.containsKey(paramNameType)) {
|
||||
CodegenParameter lastParam = this.uniqueOptionalParamsByName.get(paramNameType);
|
||||
if (uniqueParamsByName.containsKey(paramNameType)) {
|
||||
CodegenParameter lastParam = this.uniqueParamsByName.get(paramNameType);
|
||||
if (lastParam.dataType != null && lastParam.dataType.equals(param.dataType)) {
|
||||
param.vendorExtensions.put("x-duplicate", true);
|
||||
} else {
|
||||
paramNameType = paramNameType + param.dataType;
|
||||
while (modelNames.containsKey(paramNameType)) {
|
||||
while (typeNames.contains(paramNameType)) {
|
||||
paramNameType = generateNextName(paramNameType);
|
||||
}
|
||||
uniqueParamsByName.put(paramNameType, param);
|
||||
}
|
||||
} else {
|
||||
while (modelNames.containsKey(paramNameType)) {
|
||||
while (typeNames.contains(paramNameType)) {
|
||||
paramNameType = generateNextName(paramNameType);
|
||||
}
|
||||
uniqueOptionalParamsByName.put(paramNameType, param);
|
||||
uniqueParamsByName.put(paramNameType, param);
|
||||
}
|
||||
|
||||
param.vendorExtensions.put("x-paramNameType", paramNameType);
|
||||
op.vendorExtensions.put("x-hasBodyOrFormParam", op.getHasBodyParam() || op.getHasFormParams());
|
||||
typeNames.add(paramNameType);
|
||||
}
|
||||
}
|
||||
if (op.getHasPathParams()) {
|
||||
@@ -572,7 +579,18 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
public List<CodegenSecurity> fromSecurity(Map<String, SecuritySchemeDefinition> schemes) {
|
||||
List<CodegenSecurity> secs = super.fromSecurity(schemes);
|
||||
for(CodegenSecurity sec : secs) {
|
||||
String prefix = "";
|
||||
if(sec.isBasic) prefix = "AuthBasic";
|
||||
if(sec.isApiKey) prefix = "AuthApiKey";
|
||||
if(sec.isOAuth) prefix = "AuthOAuth";
|
||||
sec.name = prefix + toTypeName("",sec.name);
|
||||
}
|
||||
return secs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
|
||||
@@ -586,6 +604,7 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
|
||||
additionalProperties.put("x-hasUnknownMimeTypes", !unknownMimeTypes.isEmpty());
|
||||
additionalProperties.put("x-unknownMimeTypes", unknownMimeTypes);
|
||||
additionalProperties.put("x-allUniqueParams", uniqueParamsByName.values());
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -619,12 +638,13 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
public CodegenModel fromModel(String name, Model mod, Map<String, Model> allDefinitions) {
|
||||
CodegenModel model = super.fromModel(name, mod, allDefinitions);
|
||||
|
||||
while (uniqueOptionalParamsByName.containsKey(model.classname)) {
|
||||
while (typeNames.contains(model.classname)) {
|
||||
model.classname = generateNextName(model.classname);
|
||||
}
|
||||
typeNames.add(model.classname);
|
||||
|
||||
// From the model name, compute the prefix for the fields.
|
||||
String prefix = WordUtils.uncapitalize(model.classname);
|
||||
String prefix = StringUtils.uncapitalize(model.classname);
|
||||
for (CodegenProperty prop : model.vars) {
|
||||
prop.name = toVarName(prefix, prop.name);
|
||||
}
|
||||
@@ -635,7 +655,6 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
return model;
|
||||
}
|
||||
|
||||
modelNames.put(model.classname, model);
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -674,6 +693,7 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
if(StringUtils.isBlank(mediaType)) return;
|
||||
|
||||
String mimeType = getMimeDataType(mediaType);
|
||||
typeNames.add(mimeType);
|
||||
m.put(MEDIA_DATA_TYPE, mimeType);
|
||||
if (isJsonMimeType(mediaType)) {
|
||||
m.put(MEDIA_IS_JSON, "true");
|
||||
@@ -761,6 +781,7 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toVarName(String name) {
|
||||
return toVarName("", name);
|
||||
@@ -794,8 +815,28 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
return toTypeName("Model", name);
|
||||
}
|
||||
public String toTypeName(String prefix, String name) {
|
||||
name = camelize(underscore(sanitizeName(name)));
|
||||
|
||||
name = escapeIdentifier(prefix, camelize(sanitizeName(name)));
|
||||
return name;
|
||||
}
|
||||
@Override
|
||||
public String toOperationId(String operationId) {
|
||||
if (StringUtils.isEmpty(operationId)) {
|
||||
throw new RuntimeException("Empty method/operation name (operationId) not allowed");
|
||||
}
|
||||
operationId = escapeIdentifier("op",camelize(sanitizeName(operationId), true));
|
||||
String uniqueName = operationId;
|
||||
String uniqueNameType = toTypeName("Op", operationId);
|
||||
while (typeNames.contains(uniqueNameType)) {
|
||||
uniqueName = generateNextName(uniqueName);
|
||||
uniqueNameType = toTypeName("Op", uniqueName);
|
||||
}
|
||||
typeNames.add(uniqueNameType);
|
||||
if(!operationId.equals(uniqueName)) {
|
||||
LOGGER.warn("generated unique operationId `" + uniqueName + "`");
|
||||
}
|
||||
return uniqueName;
|
||||
}
|
||||
public String escapeIdentifier(String prefix, String name) {
|
||||
if(StringUtils.isBlank(prefix)) return name;
|
||||
|
||||
if (isReservedWord(name)) {
|
||||
@@ -815,4 +856,65 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
static boolean isJsonMimeType(String mime) {
|
||||
return mime != null && JSON_MIME_PATTERN.matcher(mime).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toDefaultValue(Property p) {
|
||||
if (p instanceof StringProperty) {
|
||||
StringProperty dp = (StringProperty) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return "\"" + escapeText(dp.getDefault()) + "\"";
|
||||
}
|
||||
} else if (p instanceof BooleanProperty) {
|
||||
BooleanProperty dp = (BooleanProperty) p;
|
||||
if (dp.getDefault() != null) {
|
||||
if (dp.getDefault().toString().equalsIgnoreCase("false"))
|
||||
return "False";
|
||||
else
|
||||
return "True";
|
||||
}
|
||||
} else if (p instanceof DoubleProperty) {
|
||||
DoubleProperty dp = (DoubleProperty) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return dp.getDefault().toString();
|
||||
}
|
||||
} else if (p instanceof FloatProperty) {
|
||||
FloatProperty dp = (FloatProperty) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return dp.getDefault().toString();
|
||||
}
|
||||
} else if (p instanceof IntegerProperty) {
|
||||
IntegerProperty dp = (IntegerProperty) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return dp.getDefault().toString();
|
||||
}
|
||||
} else if (p instanceof LongProperty) {
|
||||
LongProperty dp = (LongProperty) p;
|
||||
if (dp.getDefault() != null) {
|
||||
return dp.getDefault().toString();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// override with any special text escaping logic
|
||||
@SuppressWarnings("static-method")
|
||||
public String escapeText(String input) {
|
||||
if (input == null) {
|
||||
return input;
|
||||
}
|
||||
|
||||
// remove \t, \n, \r
|
||||
// replace \ with \\
|
||||
// replace " with \"
|
||||
// outter unescape to retain the original multi-byte characters
|
||||
// finally escalate characters avoiding code injection
|
||||
return escapeUnsafeCharacters(
|
||||
StringEscapeUtils.unescapeJava(
|
||||
StringEscapeUtils.escapeJava(input)
|
||||
.replace("\\/", "/"))
|
||||
.replaceAll("[\\t\\n\\r]"," ")
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
Module : {{title}}.API
|
||||
-}
|
||||
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE ConstraintKinds #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-# LANGUAGE InstanceSigs #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE ExistentialQuantification #-}
|
||||
{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds -fno-warn-unused-imports #-}
|
||||
|
||||
module {{title}}.API where
|
||||
@@ -31,6 +32,7 @@ import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.ByteString.Builder as BB
|
||||
import qualified Data.ByteString.Char8 as BC
|
||||
import qualified Data.ByteString.Lazy.Char8 as BCL
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
|
||||
import qualified Network.HTTP.Client.MultipartFormData as NH
|
||||
import qualified Network.HTTP.Media as ME
|
||||
@@ -40,7 +42,7 @@ import qualified Web.HttpApiData as WH
|
||||
import qualified Web.FormUrlEncoded as WH
|
||||
|
||||
import qualified Data.CaseInsensitive as CI
|
||||
import qualified Data.Data as P (Typeable)
|
||||
import qualified Data.Data as P (Typeable, TypeRep, typeOf, typeRep)
|
||||
import qualified Data.Foldable as P
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Set as Set
|
||||
@@ -76,30 +78,31 @@ import qualified Prelude as P
|
||||
-- {{/summary}}{{#notes}}
|
||||
-- {{{.}}}
|
||||
-- {{/notes}}{{#hasAuthMethods}}
|
||||
-- AuthMethod: {{#authMethods}}{{{name}}}{{#hasMore}}, {{/hasMore}}{{/authMethods}}
|
||||
-- AuthMethod: {{#authMethods}}'{{{name}}}'{{#hasMore}}, {{/hasMore}}{{/authMethods}}
|
||||
-- {{/hasAuthMethods}}{{#vendorExtensions.x-hasUnknownReturn}}
|
||||
-- Note: Has 'Produces' instances, but no response schema
|
||||
-- {{/vendorExtensions.x-hasUnknownReturn}}
|
||||
{{operationId}}
|
||||
:: {{#vendorExtensions.x-hasBodyOrFormParam}}(Consumes {{{vendorExtensions.x-operationType}}} contentType{{#allParams}}{{#isBodyParam}}{{#required}}, MimeRender contentType {{dataType}}{{/required}}{{/isBodyParam}}{{/allParams}})
|
||||
:: {{#vendorExtensions.x-hasBodyOrFormParam}}(Consumes {{{vendorExtensions.x-operationType}}} contentType{{#allParams}}{{#isBodyParam}}{{#required}}, MimeRender contentType {{#vendorExtensions.x-paramNameType}}{{{.}}}{{/vendorExtensions.x-paramNameType}}{{^vendorExtensions.x-paramNameType}}{{{dataType}}}{{/vendorExtensions.x-paramNameType}}{{/required}}{{/isBodyParam}}{{/allParams}})
|
||||
=> contentType -- ^ request content-type ('MimeType')
|
||||
-> {{/vendorExtensions.x-hasBodyOrFormParam}}{{#allParams}}{{#required}}{{dataType}} -- ^ "{{{paramName}}}"{{#description}} - {{/description}} {{{description}}}
|
||||
-> {{/vendorExtensions.x-hasBodyOrFormParam}}{{#allParams}}{{#required}}{{#vendorExtensions.x-paramNameType}}{{{.}}}{{/vendorExtensions.x-paramNameType}}{{^vendorExtensions.x-paramNameType}}{{{dataType}}}{{/vendorExtensions.x-paramNameType}} -- ^ "{{{paramName}}}"{{#description}} - {{/description}} {{{description}}}
|
||||
-> {{/required}}{{/allParams}}{{requestType}} {{{vendorExtensions.x-operationType}}} {{#vendorExtensions.x-hasBodyOrFormParam}}contentType{{/vendorExtensions.x-hasBodyOrFormParam}}{{^vendorExtensions.x-hasBodyOrFormParam}}MimeNoContent{{/vendorExtensions.x-hasBodyOrFormParam}} {{vendorExtensions.x-returnType}}
|
||||
{{operationId}} {{#vendorExtensions.x-hasBodyOrFormParam}}_ {{/vendorExtensions.x-hasBodyOrFormParam}}{{#allParams}}{{#required}}{{{paramName}}} {{/required}}{{/allParams}}=
|
||||
{{operationId}} {{#vendorExtensions.x-hasBodyOrFormParam}}_ {{/vendorExtensions.x-hasBodyOrFormParam}}{{#allParams}}{{#required}}{{#isBodyParam}}{{{paramName}}}{{/isBodyParam}}{{^isBodyParam}}({{{vendorExtensions.x-paramNameType}}} {{{paramName}}}){{/isBodyParam}} {{/required}}{{/allParams}}=
|
||||
_mkRequest "{{httpMethod}}" [{{#pathParams}}{{#vendorExtensions.x-pathPrefix}}"{{.}}",{{/vendorExtensions.x-pathPrefix}}toPath {{{paramName}}}{{#hasMore}},{{/hasMore}}{{/pathParams}}{{#vendorExtensions.x-pathSuffix}}{{#vendorExtensions.x-hasPathParams}},{{/vendorExtensions.x-hasPathParams}}"{{.}}"{{/vendorExtensions.x-pathSuffix}}]{{#allParams}}{{#required}}
|
||||
{{#isHeaderParam}}`setHeader` {{>_headerColl}} ("{{{baseName}}}", {{{paramName}}}){{/isHeaderParam}}{{#isQueryParam}}`_setQuery` {{>_queryColl}} ("{{{baseName}}}", Just {{{paramName}}}){{/isQueryParam}}{{#isFormParam}}{{#isFile}}`_addMultiFormPart` NH.partFileSource "{{{baseName}}}" {{{paramName}}}{{/isFile}}{{^isFile}}{{#isMultipart}}`_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData {{{paramName}}}){{/isMultipart}}{{^isMultipart}}`_addForm` {{>_formColl}} ("{{{baseName}}}", {{{paramName}}}){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{#isBodyParam}}`setBodyParam` {{{paramName}}}{{/isBodyParam}}{{/required}}{{/allParams}}{{#isDeprecated}}
|
||||
{{#isHeaderParam}}`setHeader` {{>_headerColl}} ("{{{baseName}}}", {{{paramName}}}){{/isHeaderParam}}{{#isQueryParam}}`setQuery` {{>_queryColl}} ("{{{baseName}}}", Just {{{paramName}}}){{/isQueryParam}}{{#isFormParam}}{{#isFile}}`_addMultiFormPart` NH.partFileSource "{{{baseName}}}" {{{paramName}}}{{/isFile}}{{^isFile}}{{#isMultipart}}`_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData {{{paramName}}}){{/isMultipart}}{{^isMultipart}}`addForm` {{>_formColl}} ("{{{baseName}}}", {{{paramName}}}){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{#isBodyParam}}`setBodyParam` {{{paramName}}}{{/isBodyParam}}{{/required}}{{/allParams}}{{#authMethods}}
|
||||
`_hasAuthType` (P.Proxy :: P.Proxy {{name}}){{/authMethods}}{{#isDeprecated}}
|
||||
|
||||
{-# DEPRECATED {{operationId}} "" #-}{{/isDeprecated}}
|
||||
|
||||
data {{{vendorExtensions.x-operationType}}} {{#allParams}}{{#isBodyParam}}{{#description}}
|
||||
|
||||
-- | /Body Param/ "{{{baseName}}}" - {{{description}}}{{/description}}
|
||||
instance HasBodyParam {{{vendorExtensions.x-operationType}}} {{{dataType}}}{{/isBodyParam}}{{/allParams}} {{#vendorExtensions.x-hasOptionalParams}}{{#allParams}}{{^isBodyParam}}{{^required}}{{#description}}
|
||||
instance HasBodyParam {{{vendorExtensions.x-operationType}}} {{#vendorExtensions.x-paramNameType}}{{{.}}}{{/vendorExtensions.x-paramNameType}}{{^vendorExtensions.x-paramNameType}}{{{dataType}}}{{/vendorExtensions.x-paramNameType}}{{/isBodyParam}}{{/allParams}} {{#vendorExtensions.x-hasOptionalParams}}{{#allParams}}{{^isBodyParam}}{{^required}}{{#description}}
|
||||
|
||||
-- | /Optional Param/ "{{{baseName}}}" - {{{description}}}{{/description}}
|
||||
instance HasOptionalParam {{{vendorExtensions.x-operationType}}} {{{vendorExtensions.x-paramNameType}}} where
|
||||
applyOptionalParam req ({{{vendorExtensions.x-paramNameType}}} xs) =
|
||||
{{#isHeaderParam}}req `setHeader` {{>_headerColl}} ("{{{baseName}}}", xs){{/isHeaderParam}}{{#isQueryParam}}req `_setQuery` {{>_queryColl}} ("{{{baseName}}}", Just xs){{/isQueryParam}}{{#isFormParam}}{{#isFile}}req `_addMultiFormPart` NH.partFileSource "{{{baseName}}}" xs{{/isFile}}{{^isFile}}{{#isMultipart}}req `_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData xs){{/isMultipart}}{{^isMultipart}}req `_addForm` {{>_formColl}} ("{{{baseName}}}", xs){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{/required}}{{/isBodyParam}}{{/allParams}}{{/vendorExtensions.x-hasOptionalParams}}{{#hasConsumes}}
|
||||
{{#isHeaderParam}}req `setHeader` {{>_headerColl}} ("{{{baseName}}}", xs){{/isHeaderParam}}{{#isQueryParam}}req `setQuery` {{>_queryColl}} ("{{{baseName}}}", Just xs){{/isQueryParam}}{{#isFormParam}}{{#isFile}}req `_addMultiFormPart` NH.partFileSource "{{{baseName}}}" xs{{/isFile}}{{^isFile}}{{#isMultipart}}req `_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData xs){{/isMultipart}}{{^isMultipart}}req `addForm` {{>_formColl}} ("{{{baseName}}}", xs){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{/required}}{{/isBodyParam}}{{/allParams}}{{/vendorExtensions.x-hasOptionalParams}}{{#hasConsumes}}
|
||||
|
||||
{{#consumes}}-- | @{{{mediaType}}}@
|
||||
instance Consumes {{{vendorExtensions.x-operationType}}} {{{x-mediaDataType}}}
|
||||
@@ -135,11 +138,12 @@ class HasOptionalParam req param where
|
||||
|
||||
infixl 2 -&-
|
||||
|
||||
-- * Optional Request Parameter Types
|
||||
-- * Request Parameter Types
|
||||
|
||||
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#vendorExtensions.x-hasOptionalParams}}{{#allParams}}{{^required}}{{^vendorExtensions.x-duplicate}}
|
||||
newtype {{{vendorExtensions.x-paramNameType}}} = {{{vendorExtensions.x-paramNameType}}} { un{{{vendorExtensions.x-paramNameType}}} :: {{{dataType}}} } deriving (P.Eq, P.Show)
|
||||
{{/vendorExtensions.x-duplicate}}{{/required}}{{/allParams}}{{/vendorExtensions.x-hasOptionalParams}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
|
||||
{{#x-allUniqueParams}}
|
||||
-- | {{{vendorExtensions.x-paramNameType}}}
|
||||
newtype {{{vendorExtensions.x-paramNameType}}} = {{{vendorExtensions.x-paramNameType}}} { un{{{vendorExtensions.x-paramNameType}}} :: {{{dataType}}} } deriving (P.Eq, P.Show{{#isBodyParam}}, A.ToJSON{{/isBodyParam}})
|
||||
{{/x-allUniqueParams}}
|
||||
|
||||
-- * {{requestType}}
|
||||
|
||||
@@ -148,6 +152,7 @@ data {{requestType}} req contentType res = {{requestType}}
|
||||
{ rMethod :: NH.Method -- ^ Method of {{requestType}}
|
||||
, rUrlPath :: [BCL.ByteString] -- ^ Endpoint of {{requestType}}
|
||||
, rParams :: Params -- ^ params of {{requestType}}
|
||||
, rAuthTypes :: [P.TypeRep] -- ^ types of auth methods
|
||||
}
|
||||
deriving (P.Show)
|
||||
|
||||
@@ -166,6 +171,11 @@ rParamsL :: Lens_' ({{requestType}} req contentType res) Params
|
||||
rParamsL f {{requestType}}{..} = (\rParams -> {{requestType}} { rParams, ..} ) <$> f rParams
|
||||
{-# INLINE rParamsL #-}
|
||||
|
||||
-- | 'rParams' Lens
|
||||
rAuthTypesL :: Lens_' ({{requestType}} req contentType res) [P.TypeRep]
|
||||
rAuthTypesL f {{requestType}}{..} = (\rAuthTypes -> {{requestType}} { rAuthTypes, ..} ) <$> f rAuthTypes
|
||||
{-# INLINE rAuthTypesL #-}
|
||||
|
||||
-- | Request Params
|
||||
data Params = Params
|
||||
{ paramsQuery :: NH.Query
|
||||
@@ -203,7 +213,7 @@ data ParamBody
|
||||
_mkRequest :: NH.Method -- ^ Method
|
||||
-> [BCL.ByteString] -- ^ Endpoint
|
||||
-> {{requestType}} req contentType res -- ^ req: Request Type, res: Response Type
|
||||
_mkRequest m u = {{requestType}} m u _mkParams
|
||||
_mkRequest m u = {{requestType}} m u _mkParams []
|
||||
|
||||
_mkParams :: Params
|
||||
_mkParams = Params [] [] ParamBodyNone
|
||||
@@ -235,8 +245,8 @@ _setAcceptHeader req accept =
|
||||
Just m -> req `setHeader` [("accept", BC.pack $ P.show m)]
|
||||
Nothing -> req `removeHeader` ["accept"]
|
||||
|
||||
_setQuery :: {{requestType}} req contentType res -> [NH.QueryItem] -> {{requestType}} req contentType res
|
||||
_setQuery req query =
|
||||
setQuery :: {{requestType}} req contentType res -> [NH.QueryItem] -> {{requestType}} req contentType res
|
||||
setQuery req query =
|
||||
req &
|
||||
L.over
|
||||
(rParamsL . paramsQueryL)
|
||||
@@ -244,8 +254,8 @@ _setQuery req query =
|
||||
where
|
||||
cifst = CI.mk . P.fst
|
||||
|
||||
_addForm :: {{requestType}} req contentType res -> WH.Form -> {{requestType}} req contentType res
|
||||
_addForm req newform =
|
||||
addForm :: {{requestType}} req contentType res -> WH.Form -> {{requestType}} req contentType res
|
||||
addForm req newform =
|
||||
let form = case paramsBody (rParams req) of
|
||||
ParamBodyFormUrlEncoded _form -> _form
|
||||
_ -> mempty
|
||||
@@ -266,6 +276,9 @@ _setBodyLBS :: {{requestType}} req contentType res -> BL.ByteString -> {{request
|
||||
_setBodyLBS req body =
|
||||
req & L.set (rParamsL . paramsBodyL) (ParamBodyBL body)
|
||||
|
||||
_hasAuthType :: AuthMethod authMethod => {{requestType}} req contentType res -> P.Proxy authMethod -> {{requestType}} req contentType res
|
||||
_hasAuthType req proxy =
|
||||
req & L.over rAuthTypesL (P.typeRep proxy :)
|
||||
|
||||
-- ** Params Utils
|
||||
|
||||
@@ -330,3 +343,49 @@ _toCollA' c encode one xs = case c of
|
||||
{-# INLINE expandList #-}
|
||||
{-# INLINE combine #-}
|
||||
|
||||
-- * AuthMethods
|
||||
|
||||
-- | Provides a method to apply auth methods to requests
|
||||
class P.Typeable a => AuthMethod a where
|
||||
applyAuthMethod :: {{requestType}} req contentType res -> a -> {{requestType}} req contentType res
|
||||
|
||||
-- | An existential wrapper for any AuthMethod
|
||||
data AnyAuthMethod = forall a. AuthMethod a => AnyAuthMethod a deriving (P.Typeable)
|
||||
|
||||
instance AuthMethod AnyAuthMethod where applyAuthMethod req (AnyAuthMethod a) = applyAuthMethod req a
|
||||
|
||||
{{#authMethods}}{{#isBasic}}-- ** {{name}}
|
||||
data {{name}} =
|
||||
{{name}} B.ByteString B.ByteString -- ^ username password
|
||||
deriving (P.Eq, P.Show, P.Typeable)
|
||||
|
||||
instance AuthMethod {{name}} where
|
||||
applyAuthMethod req a@({{name}} user pw) =
|
||||
if (P.typeOf a `P.elem` rAuthTypes req)
|
||||
then req `setHeader` toHeader ("Authorization", T.decodeUtf8 cred)
|
||||
else req
|
||||
where cred = BC.append "Basic " (B64.encode $ BC.concat [ user, ":", pw ])
|
||||
|
||||
{{/isBasic}}{{#isApiKey}}-- ** {{name}}
|
||||
data {{name}} =
|
||||
{{name}} Text -- ^ secret
|
||||
deriving (P.Eq, P.Show, P.Typeable)
|
||||
|
||||
instance AuthMethod {{name}} where
|
||||
applyAuthMethod req a@({{name}} secret) =
|
||||
if (P.typeOf a `P.elem` rAuthTypes req)
|
||||
then req {{#isKeyInHeader}}`setHeader` toHeader ("{{keyParamName}}", secret){{/isKeyInHeader}}{{^isKeyInHeader}}`setQuery` toQuery ("{{keyParamName}}", Just secret){{/isKeyInHeader}}
|
||||
else req
|
||||
|
||||
{{/isApiKey}}{{#isOAuth}}-- ** {{name}}
|
||||
data {{name}} =
|
||||
{{name}} Text -- ^ secret
|
||||
deriving (P.Eq, P.Show, P.Typeable)
|
||||
|
||||
instance AuthMethod {{name}} where
|
||||
applyAuthMethod req a@({{name}} secret) =
|
||||
if (P.typeOf a `P.elem` rAuthTypes req)
|
||||
then req `setHeader` toHeader ("Authorization", "Bearer " <> secret)
|
||||
else req
|
||||
|
||||
{{/isOAuth}}{{/authMethods}}
|
||||
|
||||
@@ -59,6 +59,7 @@ data {{configType}} = {{configType}}
|
||||
, configUserAgent :: Text -- ^ user-agent supplied in the Request
|
||||
, configLogExecWithContext :: LogExecWithContext -- ^ Run a block using a Logger instance
|
||||
, configLogContext :: LogContext -- ^ Configures the logger
|
||||
, configAuthMethods :: [AnyAuthMethod] -- ^ List of configured auth methods
|
||||
}
|
||||
|
||||
-- | display the config
|
||||
@@ -87,13 +88,21 @@ newConfig = do
|
||||
, configUserAgent = "{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}{{{artifactId}}}/{{{artifactVersion}}}{{/httpUserAgent}}"
|
||||
, configLogExecWithContext = runDefaultLogExecWithContext
|
||||
, configLogContext = logCxt
|
||||
, configAuthMethods = []
|
||||
}
|
||||
|
||||
-- | updates config use AuthMethod on matching requests
|
||||
addAuthMethod :: AuthMethod auth => {{configType}} -> auth -> {{configType}}
|
||||
addAuthMethod config@{{configType}} {configAuthMethods = as} a =
|
||||
config { configAuthMethods = AnyAuthMethod a : as}
|
||||
|
||||
-- | updates the config to use stdout logging
|
||||
withStdoutLogging :: {{configType}} -> IO {{configType}}
|
||||
withStdoutLogging p = do
|
||||
logCxt <- stdoutLoggingContext (configLogContext p)
|
||||
return $ p { configLogExecWithContext = stdoutLoggingExec, configLogContext = logCxt }
|
||||
|
||||
-- | updates the config to use stderr logging
|
||||
withStderrLogging :: {{configType}} -> IO {{configType}}
|
||||
withStderrLogging p = do
|
||||
logCxt <- stderrLoggingContext (configLogContext p)
|
||||
@@ -226,7 +235,9 @@ _toInitRequest
|
||||
-> IO (InitRequest req contentType res accept) -- ^ initialized request
|
||||
_toInitRequest config req0 accept = do
|
||||
parsedReq <- NH.parseRequest $ BCL.unpack $ BCL.append (configHost config) (BCL.concat (rUrlPath req0))
|
||||
let req1 = _setAcceptHeader req0 accept & _setContentTypeHeader
|
||||
let req1 = _applyAuthMethods req0 config
|
||||
& _setContentTypeHeader
|
||||
& flip _setAcceptHeader accept
|
||||
reqHeaders = ("User-Agent", WH.toHeader (configUserAgent config)) : paramsHeaders (rParams req1)
|
||||
reqQuery = NH.renderQuery True (paramsQuery (rParams req1))
|
||||
pReq = parsedReq { NH.method = (rMethod req1)
|
||||
@@ -242,6 +253,16 @@ _toInitRequest config req0 accept = do
|
||||
|
||||
pure (InitRequest outReq)
|
||||
|
||||
-- | apply all matching AuthMethods in config to request
|
||||
_applyAuthMethods
|
||||
:: {{requestType}} req contentType res
|
||||
-> {{configType}}
|
||||
-> {{requestType}} req contentType res
|
||||
_applyAuthMethods req {{configType}} {configAuthMethods = as} =
|
||||
foldl go req as
|
||||
where
|
||||
go r (AnyAuthMethod a) = r `applyAuthMethod` a
|
||||
|
||||
-- | modify the underlying Request
|
||||
modifyInitRequest :: InitRequest req contentType res accept -> (NH.Request -> NH.Request) -> InitRequest req contentType res accept
|
||||
modifyInitRequest (InitRequest req) f = InitRequest (f req)
|
||||
|
||||
@@ -56,7 +56,7 @@ import qualified Prelude as P
|
||||
{{#model}}
|
||||
|
||||
-- ** {{classname}}
|
||||
-- |{{#title}}
|
||||
-- | {{classname}}{{#title}}
|
||||
-- {{{.}}}
|
||||
-- {{/title}}{{#description}}
|
||||
-- {{{.}}}{{/description}}
|
||||
@@ -65,12 +65,15 @@ data {{classname}} = {{classname}}
|
||||
, {{/hasMore}}{{/vars}}
|
||||
} deriving (P.Show,P.Eq,P.Typeable{{#modelDeriving}},{{modelDeriving}}{{/modelDeriving}})
|
||||
|
||||
|
||||
-- | FromJSON {{classname}}
|
||||
instance A.FromJSON {{classname}} where
|
||||
parseJSON = A.withObject "{{classname}}" $ \o ->
|
||||
{{^hasVars}}pure {{/hasVars}}{{classname}}
|
||||
{{#hasVars}}<$>{{/hasVars}}{{#vars}} (o {{#required}}.: {{/required}}{{^required}}{{^allowFromJsonNulls}}.:!{{/allowFromJsonNulls}}{{#allowFromJsonNulls}}.:?{{/allowFromJsonNulls}}{{/required}} "{{baseName}}"){{#hasMore}}
|
||||
<*>{{/hasMore}}{{/vars}}
|
||||
|
||||
-- | ToJSON {{classname}}
|
||||
instance A.ToJSON {{classname}} where
|
||||
toJSON {{classname}} {{#hasVars}}{..}{{/hasVars}} =
|
||||
{{^allowToJsonNulls}}_omitNulls{{/allowToJsonNulls}}{{#allowToJsonNulls}}A.object{{/allowToJsonNulls}}
|
||||
@@ -79,12 +82,14 @@ instance A.ToJSON {{classname}} where
|
||||
]
|
||||
|
||||
{{#vendorExtensions.x-hasMimeFormUrlEncoded}}
|
||||
-- | FromForm {{classname}}
|
||||
instance WH.FromForm {{classname}} where
|
||||
fromForm f =
|
||||
{{^hasVars}}pure {{/hasVars}}{{classname}}
|
||||
{{#hasVars}}<$>{{/hasVars}}{{#vars}} ({{#required}}WH.parseUnique {{/required}}{{^required}}WH.parseMaybe {{/required}}"{{baseName}}" f){{#hasMore}}
|
||||
<*>{{/hasMore}}{{/vars}}
|
||||
|
||||
-- | ToForm {{classname}}
|
||||
instance WH.ToForm {{classname}} where
|
||||
toForm {{classname}} {{#hasVars}}{..}{{/hasVars}} =
|
||||
WH.Form $ HM.fromList $ P.catMaybes $
|
||||
@@ -111,21 +116,23 @@ mk{{classname}} {{#requiredVars}}{{name}} {{/requiredVars}}=
|
||||
-- * Utils
|
||||
|
||||
-- | Removes Null fields. (OpenAPI-Specification 2.0 does not allow Null in JSON)
|
||||
|
||||
_omitNulls :: [(Text, A.Value)] -> A.Value
|
||||
_omitNulls = A.object . P.filter notNull
|
||||
where
|
||||
notNull (_, A.Null) = False
|
||||
notNull _ = True
|
||||
|
||||
-- | Encodes fields using WH.toQueryParam
|
||||
_toFormItem :: (WH.ToHttpApiData a, Functor f) => t -> f a -> f (t, [Text])
|
||||
_toFormItem name x = (name,) . (:[]) . WH.toQueryParam <$> x
|
||||
|
||||
-- | Collapse (Just "") to Nothing
|
||||
_emptyToNothing :: Maybe String -> Maybe String
|
||||
_emptyToNothing (Just "") = Nothing
|
||||
_emptyToNothing x = x
|
||||
{-# INLINE _emptyToNothing #-}
|
||||
|
||||
-- | Collapse (Just mempty) to Nothing
|
||||
_memptyToNothing :: (P.Monoid a, P.Eq a) => Maybe a -> Maybe a
|
||||
_memptyToNothing (Just x) | x P.== P.mempty = Nothing
|
||||
_memptyToNothing x = x
|
||||
@@ -158,6 +165,7 @@ _showDateTime =
|
||||
{{^dateTimeFormat}}TI.formatISO8601Millis{{/dateTimeFormat}}{{#dateTimeFormat}}TI.formatTime TI.defaultTimeLocale "{{{dateTimeFormat}}}"{{/dateTimeFormat}}
|
||||
{-# INLINE _showDateTime #-}
|
||||
|
||||
-- | parse an ISO8601 date-time string
|
||||
_parseISO8601 :: (TI.ParseTime t, Monad m, Alternative m) => String -> m t
|
||||
_parseISO8601 t =
|
||||
P.asum $
|
||||
|
||||
@@ -46,10 +46,6 @@ haskell-http-client
|
||||
|
||||
### Unsupported Swagger Features
|
||||
|
||||
* Auth Methods (https://swagger.io/docs/specification/2-0/authentication/)
|
||||
|
||||
- use `setHeader` to add any required headers to requests
|
||||
|
||||
* Model Inheritance
|
||||
|
||||
* Default Parameter Values
|
||||
@@ -92,6 +88,7 @@ View the full list of Codegen "config option" parameters with the command:
|
||||
java -jar swagger-codegen-cli.jar config-help -l haskell-http-client
|
||||
```
|
||||
|
||||
## Usage Notes
|
||||
|
||||
### Example SwaggerPetstore Haddock documentation
|
||||
|
||||
@@ -103,12 +100,12 @@ An example of the generated haddock documentation targeting the server http://pe
|
||||
|
||||
An example application using the auto-generated haskell-http-client bindings for the server http://petstore.swagger.io/ can be found [here][3]
|
||||
|
||||
[3]: https://github.com/swagger-api/swagger-codegen/tree/c7d145a4ba3c0627e04ece9eb97e354ac91be821/samples/client/petstore/haskell-http-client/example-app
|
||||
|
||||
### Usage Notes
|
||||
[3]: https://github.com/swagger-api/swagger-codegen/tree/master/samples/client/petstore/haskell-http-client/example-app
|
||||
|
||||
This library is intended to be imported qualified.
|
||||
|
||||
### Modules
|
||||
|
||||
| MODULE | NOTES |
|
||||
| ------------------- | --------------------------------------------------- |
|
||||
| {{title}}.Client | use the "dispatch" functions to send requests |
|
||||
@@ -118,6 +115,9 @@ This library is intended to be imported qualified.
|
||||
| {{title}}.Lens | lenses for model fields |
|
||||
| {{title}}.Logging | logging functions and utils |
|
||||
|
||||
|
||||
### MimeTypes
|
||||
|
||||
This library adds type safety around what swagger specifies as
|
||||
Produces and Consumes for each Operation (e.g. the list of MIME types an
|
||||
Operation can Produce (using 'accept' headers) and Consume (using 'content-type' headers).
|
||||
@@ -151,16 +151,6 @@ this would indicate that:
|
||||
* the _addFoo_ operation can set it's body param of _FooModel_ via `setBodyParam`
|
||||
* the _addFoo_ operation can set 2 different optional parameters via `applyOptionalParam`
|
||||
|
||||
putting this together:
|
||||
|
||||
```haskell
|
||||
let addFooRequest = addFoo MimeJSON foomodel requiredparam1 requiredparam2
|
||||
`applyOptionalParam` FooId 1
|
||||
`applyOptionalParam` FooName "name"
|
||||
`setHeader` [("api_key","xxyy")]
|
||||
addFooResult <- dispatchMime mgr config addFooRequest MimeXML
|
||||
```
|
||||
|
||||
If the swagger spec doesn't declare it can accept or produce a certain
|
||||
MIME type for a given Operation, you should either add a Produces or
|
||||
Consumes instance for the desired MIME types (assuming the server
|
||||
@@ -174,4 +164,29 @@ x-www-form-urlencoded instances (FromFrom, ToForm) will also be
|
||||
generated if the model fields are primitive types, and there are
|
||||
Operations using x-www-form-urlencoded which use those models.
|
||||
|
||||
See the example app and the haddocks for details.
|
||||
### Authentication
|
||||
|
||||
A haskell data type will be generated for each swagger authentication type.
|
||||
|
||||
If for example the AuthMethod `AuthOAuthFoo` is generated for OAuth operations, then
|
||||
`addAuthMethod` should be used to add the AuthMethod config.
|
||||
|
||||
When a request is dispatched, if a matching auth method is found in
|
||||
the config, it will be applied to the request.
|
||||
|
||||
### Example
|
||||
|
||||
```haskell
|
||||
mgr <- newManager defaultManagerSettings
|
||||
config0 <- withStdoutLogging =<< newConfig
|
||||
let config = config0
|
||||
`addAuthMethod` AuthOAuthFoo "secret-key"
|
||||
|
||||
let addFooRequest = addFoo MimeJSON foomodel requiredparam1 requiredparam2
|
||||
`applyOptionalParam` FooId 1
|
||||
`applyOptionalParam` FooName "name"
|
||||
`setHeader` [("qux_header","xxyy")]
|
||||
addFooResult <- dispatchMime mgr config addFooRequest MimeXML
|
||||
```
|
||||
|
||||
See the example app and the haddocks for details.
|
||||
@@ -94,6 +94,7 @@ test-suite tests
|
||||
, time
|
||||
, iso8601-time
|
||||
, aeson
|
||||
, vector
|
||||
, semigroups
|
||||
, QuickCheck
|
||||
other-modules:
|
||||
|
||||
@@ -2,20 +2,23 @@
|
||||
|
||||
module Instances where
|
||||
|
||||
import Data.Text (Text, pack)
|
||||
import Control.Monad
|
||||
import Data.Char (isSpace)
|
||||
import Data.List (sort)
|
||||
import qualified Data.Time as TI
|
||||
import Test.QuickCheck
|
||||
import qualified Data.Aeson as A
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import qualified Data.Set as Set
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Time as TI
|
||||
import qualified Data.Vector as V
|
||||
|
||||
import ApproxEq
|
||||
import {{title}}.Model
|
||||
|
||||
instance Arbitrary Text where
|
||||
arbitrary = pack <$> arbitrary
|
||||
instance Arbitrary T.Text where
|
||||
arbitrary = T.pack <$> arbitrary
|
||||
|
||||
instance Arbitrary TI.Day where
|
||||
arbitrary = TI.ModifiedJulianDay . (2000 +) <$> arbitrary
|
||||
@@ -45,6 +48,27 @@ instance Arbitrary Date where
|
||||
arbitrary = Date <$> arbitrary
|
||||
shrink (Date xs) = Date <$> shrink xs
|
||||
|
||||
-- | A naive Arbitrary instance for A.Value:
|
||||
instance Arbitrary A.Value where
|
||||
arbitrary = frequency [(3, simpleTypes), (1, arrayTypes), (1, objectTypes)]
|
||||
where
|
||||
simpleTypes :: Gen A.Value
|
||||
simpleTypes =
|
||||
frequency
|
||||
[ (1, return A.Null)
|
||||
, (2, liftM A.Bool (arbitrary :: Gen Bool))
|
||||
, (2, liftM (A.Number . fromIntegral) (arbitrary :: Gen Int))
|
||||
, (2, liftM (A.String . T.pack) (arbitrary :: Gen String))
|
||||
]
|
||||
mapF (k, v) = (T.pack k, v)
|
||||
simpleAndArrays = frequency [(1, sized sizedArray), (4, simpleTypes)]
|
||||
arrayTypes = sized sizedArray
|
||||
objectTypes = sized sizedObject
|
||||
sizedArray n = liftM (A.Array . V.fromList) $ replicateM n simpleTypes
|
||||
sizedObject n =
|
||||
liftM (A.object . map mapF) $
|
||||
replicateM n $ (,) <$> (arbitrary :: Gen String) <*> simpleAndArrays
|
||||
|
||||
-- | Checks if a given list has no duplicates in _O(n log n)_.
|
||||
hasNoDups
|
||||
:: (Ord a)
|
||||
|
||||
@@ -17,7 +17,8 @@ import {{title}}.MimeTypes
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
hspec $ modifyMaxSize (const 10) $
|
||||
do describe "JSON instances" $
|
||||
do {{#models}}{{#model}}propMimeEq MimeJSON (Proxy :: Proxy {{classname}})
|
||||
{{/model}}{{/models}}
|
||||
hspec $ modifyMaxSize (const 10) $ do
|
||||
describe "JSON instances" $ do
|
||||
pure ()
|
||||
{{#models}}{{#model}}propMimeEq MimeJSON (Proxy :: Proxy {{classname}})
|
||||
{{/model}}{{/models}}
|
||||
|
||||
Reference in New Issue
Block a user