forked from loafle/openapi-generator-original
Code generator for haskell-servant framework
This commit is contained in:
parent
3a2447634c
commit
4e3d94c005
@ -23,6 +23,7 @@ cd $APP_DIR
|
|||||||
./bin/clojure-petstore.sh
|
./bin/clojure-petstore.sh
|
||||||
./bin/csharp-petstore.sh
|
./bin/csharp-petstore.sh
|
||||||
./bin/dynamic-html.sh
|
./bin/dynamic-html.sh
|
||||||
|
./bin/haskell-petstore.sh
|
||||||
./bin/html-petstore.sh
|
./bin/html-petstore.sh
|
||||||
./bin/java-petstore.sh
|
./bin/java-petstore.sh
|
||||||
./bin/java-petstore-jersey2.sh
|
./bin/java-petstore-jersey2.sh
|
||||||
|
31
bin/haskell-petstore.sh
Executable file
31
bin/haskell-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 -t modules/swagger-codegen/src/main/resources/haskell -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l haskell -o samples/server/petstore/haskell"
|
||||||
|
|
||||||
|
java $JAVA_OPTS -jar $executable $ags
|
@ -0,0 +1,345 @@
|
|||||||
|
package io.swagger.codegen.languages;
|
||||||
|
|
||||||
|
import io.swagger.codegen.*;
|
||||||
|
import io.swagger.models.properties.*;
|
||||||
|
import io.swagger.models.Model;
|
||||||
|
import io.swagger.models.Operation;
|
||||||
|
import io.swagger.models.Swagger;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class HaskellServantCodegen extends DefaultCodegen implements CodegenConfig {
|
||||||
|
|
||||||
|
// source folder where to write the files
|
||||||
|
protected String sourceFolder = "src";
|
||||||
|
protected String apiVersion = "0.0.1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the type of generator.
|
||||||
|
*
|
||||||
|
* @return the CodegenType for this generator
|
||||||
|
* @see io.swagger.codegen.CodegenType
|
||||||
|
*/
|
||||||
|
public CodegenType getTag() {
|
||||||
|
return CodegenType.SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a friendly name for the generator. This will be used by the generator
|
||||||
|
* to select the library with the -l flag.
|
||||||
|
*
|
||||||
|
* @return the friendly name for the generator
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return "haskell";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns human-friendly help for the generator. Provide the consumer with help
|
||||||
|
* tips, parameters here
|
||||||
|
*
|
||||||
|
* @return A string value for the help message
|
||||||
|
*/
|
||||||
|
public String getHelp() {
|
||||||
|
return "Generates a HaskellServantCodegen library.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public HaskellServantCodegen() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// set the output folder here
|
||||||
|
outputFolder = "generated-code/HaskellServantCodegen";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models. You can write model files using the modelTemplateFiles map.
|
||||||
|
* if you want to create one template for file, you can do so here.
|
||||||
|
* for multiple files for model, just put another entry in the `modelTemplateFiles` with
|
||||||
|
* a different extension
|
||||||
|
*/
|
||||||
|
modelTemplateFiles.put(
|
||||||
|
"model.mustache", // the template to use
|
||||||
|
".hs"); // the extension for each file to write
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Api classes. You can write classes for each Api file with the apiTemplateFiles map.
|
||||||
|
* as with models, add multiple entries with different extensions for multiple files per
|
||||||
|
* class
|
||||||
|
*/
|
||||||
|
apiTemplateFiles.put(
|
||||||
|
"api.mustache", // the template to use
|
||||||
|
".hs"); // the extension for each file to write
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template Location. This is the location which templates will be read from. The generator
|
||||||
|
* will use the resource stream to attempt to read the templates.
|
||||||
|
*/
|
||||||
|
embeddedTemplateDir = templateDir = "haskell";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Api Package. Optional, if needed, this can be used in templates
|
||||||
|
*/
|
||||||
|
apiPackage = "Api";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model Package. Optional, if needed, this can be used in templates
|
||||||
|
*/
|
||||||
|
modelPackage = "Model";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserved words. Override this with reserved words specific to your language
|
||||||
|
*/
|
||||||
|
// from https://wiki.haskell.org/Keywords
|
||||||
|
reservedWords = new HashSet<String>(
|
||||||
|
Arrays.asList(
|
||||||
|
"as", "case", "of",
|
||||||
|
"class", "data", // "data family", "data instance",
|
||||||
|
"default", "deriving", // "deriving instance",
|
||||||
|
"do",
|
||||||
|
"forall", "foreign", "hiding",
|
||||||
|
"id",
|
||||||
|
"if", "then", "else",
|
||||||
|
"import", "infix", "infixl", "infixr",
|
||||||
|
"instance", "let", "in",
|
||||||
|
"mdo", "module", "newtype",
|
||||||
|
"proc", "qualified", "rec",
|
||||||
|
"type", // "type family", "type instance",
|
||||||
|
"where"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional Properties. These values can be passed to the templates and
|
||||||
|
* are available in models, apis, and supporting files
|
||||||
|
*/
|
||||||
|
additionalProperties.put("apiVersion", apiVersion);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supporting Files. You can write single files for the generator with the
|
||||||
|
* entire object tree available. If the input file has a suffix of `.mustache
|
||||||
|
* it will be processed by the template engine. Otherwise, it will be copied
|
||||||
|
*/
|
||||||
|
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
|
||||||
|
supportingFiles.add(new SupportingFile("stack.mustache", "", "stack.yaml"));
|
||||||
|
supportingFiles.add(new SupportingFile("haskell-servant-codegen.mustache", "", "haskell-servant-codegen.cabal"));
|
||||||
|
supportingFiles.add(new SupportingFile("Setup.mustache", "", "Setup.hs"));
|
||||||
|
supportingFiles.add(new SupportingFile("LICENSE", "", "LICENSE"));
|
||||||
|
supportingFiles.add(new SupportingFile("Apis.mustache", "lib", "Apis.hs"));
|
||||||
|
supportingFiles.add(new SupportingFile("Utils.mustache", "lib", "Utils.hs"));
|
||||||
|
supportingFiles.add(new SupportingFile("Client.mustache", "client", "Main.hs"));
|
||||||
|
supportingFiles.add(new SupportingFile("Server.mustache", "server", "Main.hs"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language Specific Primitives. These types will not trigger imports by
|
||||||
|
* the client generator
|
||||||
|
*/
|
||||||
|
languageSpecificPrimitives = new HashSet<String>(
|
||||||
|
Arrays.asList(
|
||||||
|
"Bool",
|
||||||
|
"String",
|
||||||
|
"Int",
|
||||||
|
"Integer",
|
||||||
|
"Float",
|
||||||
|
"Char",
|
||||||
|
"Double",
|
||||||
|
"List",
|
||||||
|
"FilePath"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
typeMapping.clear();
|
||||||
|
// typeMapping.put("enum", "NSString");
|
||||||
|
typeMapping.put("array", "List");
|
||||||
|
typeMapping.put("set", "Set");
|
||||||
|
typeMapping.put("boolean", "Bool");
|
||||||
|
typeMapping.put("string", "String");
|
||||||
|
typeMapping.put("int", "Int");
|
||||||
|
typeMapping.put("long", "Integer");
|
||||||
|
typeMapping.put("float", "Float");
|
||||||
|
// typeMapping.put("byte", "Byte");
|
||||||
|
typeMapping.put("short", "Int");
|
||||||
|
typeMapping.put("char", "Char");
|
||||||
|
typeMapping.put("double", "Double");
|
||||||
|
typeMapping.put("DateTime", "Integer");
|
||||||
|
// typeMapping.put("object", "Map");
|
||||||
|
typeMapping.put("file", "FilePath");
|
||||||
|
|
||||||
|
importMapping.clear();
|
||||||
|
importMapping.put("Map", "qualified Data.Map as Map");
|
||||||
|
|
||||||
|
cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC));
|
||||||
|
cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
|
||||||
|
* those terms here. This logic is only called if a variable matches the reseved words
|
||||||
|
*
|
||||||
|
* @return the escaped term
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String escapeReservedWord(String name) {
|
||||||
|
return name + "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location to write model files. You can use the modelPackage() as defined when the class is
|
||||||
|
* instantiated
|
||||||
|
*/
|
||||||
|
public String modelFileFolder() {
|
||||||
|
return outputFolder + File.separatorChar + "lib" + File.separatorChar + modelPackage().replace('.', File.separatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location to write api files. You can use the apiPackage() as defined when the class is
|
||||||
|
* instantiated
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String apiFileFolder() {
|
||||||
|
return outputFolder + File.separatorChar + "lib" + File.separatorChar + apiPackage().replace('.', File.separatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional - type declaration. This is a String which is used by the templates to instantiate your
|
||||||
|
* types. There is typically special handling for different property types
|
||||||
|
*
|
||||||
|
* @return a string value used as the `dataType` field for model templates, `returnType` for api templates
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getTypeDeclaration(Property p) {
|
||||||
|
if(p instanceof ArrayProperty) {
|
||||||
|
ArrayProperty ap = (ArrayProperty) p;
|
||||||
|
Property inner = ap.getItems();
|
||||||
|
return "[" + getTypeDeclaration(inner) + "]";
|
||||||
|
}
|
||||||
|
else if (p instanceof MapProperty) {
|
||||||
|
MapProperty mp = (MapProperty) p;
|
||||||
|
Property inner = mp.getAdditionalProperties();
|
||||||
|
return "Map.Map String " + getTypeDeclaration(inner);
|
||||||
|
}
|
||||||
|
return super.getTypeDeclaration(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional - swagger type conversion. This is used to map swagger types in a `Property` into
|
||||||
|
* either language specific types via `typeMapping` or into complex models if there is not a mapping.
|
||||||
|
*
|
||||||
|
* @return a string value of the type or complex model for this property
|
||||||
|
* @see io.swagger.models.properties.Property
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSwaggerType(Property p) {
|
||||||
|
String swaggerType = super.getSwaggerType(p);
|
||||||
|
String type = null;
|
||||||
|
if(typeMapping.containsKey(swaggerType)) {
|
||||||
|
type = typeMapping.get(swaggerType);
|
||||||
|
if(languageSpecificPrimitives.contains(type))
|
||||||
|
return toModelName(type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
type = swaggerType;
|
||||||
|
return toModelName(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String capturePath(String path, List<CodegenParameter> pathParams) {
|
||||||
|
for (CodegenParameter p : pathParams) {
|
||||||
|
String pName = "{"+p.baseName+"}";
|
||||||
|
if (path.indexOf(pName) >= 0) {
|
||||||
|
path = path.replace(pName, "Capture " + "\""+p.baseName+"\" " + p.dataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String queryPath(String path, List<CodegenParameter> queryParams) {
|
||||||
|
for (CodegenParameter p : queryParams) {
|
||||||
|
path += " :> QueryParam \"" + p.baseName + "\" " + p.dataType;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String bodyPath(String path, List<CodegenParameter> bodyParams) {
|
||||||
|
for (CodegenParameter p : bodyParams) {
|
||||||
|
path += " :> ReqBody '[JSON] " + p.dataType;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formPath(String path, List<CodegenParameter> formParams) {
|
||||||
|
String names = "Form";
|
||||||
|
for (CodegenParameter p : formParams) {
|
||||||
|
if(p.dataType.equals("FilePath")){
|
||||||
|
// file data processing
|
||||||
|
}
|
||||||
|
names += p.baseName;
|
||||||
|
}
|
||||||
|
if(formParams.size() > 0){
|
||||||
|
path += " :> ReqBody '[FormUrlEncoded] " + names;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String headerPath(String path, List<CodegenParameter> headerParams) {
|
||||||
|
for (CodegenParameter p : headerParams) {
|
||||||
|
path += " :> Header \"" + p.baseName + "\" " + p.dataType;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String filterReturnType(String rt) {
|
||||||
|
if (rt == null || rt.equals("null")) {
|
||||||
|
return "()";
|
||||||
|
} else if (rt.indexOf(" ") >= 0) {
|
||||||
|
return "(" + rt + ")";
|
||||||
|
}
|
||||||
|
return rt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String addReturnPath(String path, String httpMethod, String returnType) {
|
||||||
|
return path + " :> " + upperCaseFirst(httpMethod) + " '[JSON] " + filterReturnType(returnType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String joinStrings(String sep, List<String> ss) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String s : ss) {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.append(sep);
|
||||||
|
}
|
||||||
|
sb.append(s);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String replacePathSplitter(String path) {
|
||||||
|
String[] ps = path.replaceFirst("/", "").split("/", 0);
|
||||||
|
List<String> rs = new ArrayList<String>();
|
||||||
|
for (String p : ps) {
|
||||||
|
if (p.indexOf("{") < 0) {
|
||||||
|
rs.add("\"" + p + "\"");
|
||||||
|
} else {
|
||||||
|
rs.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return joinStrings(" :> ", rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String upperCaseFirst(String str) {
|
||||||
|
char[] array = str.toLowerCase().toCharArray();
|
||||||
|
array[0] = Character.toUpperCase(array[0]);
|
||||||
|
return new String(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseScheme(String basePath) {
|
||||||
|
return "Http";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodegenOperation fromOperation(String resourcePath, String httpMethod, Operation operation, Map<String, Model> definitions, Swagger swagger){
|
||||||
|
CodegenOperation op = super.fromOperation(resourcePath, httpMethod, operation, definitions, swagger);
|
||||||
|
String path = op.path;
|
||||||
|
op.nickname = addReturnPath(headerPath(formPath(bodyPath(queryPath(capturePath(replacePathSplitter(path), op.pathParams), op.queryParams), op.bodyParams), op.formParams), op.headerParams), op.httpMethod, op.returnType);
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -33,3 +33,4 @@ io.swagger.codegen.languages.TypeScriptNodeClientCodegen
|
|||||||
io.swagger.codegen.languages.AkkaScalaClientCodegen
|
io.swagger.codegen.languages.AkkaScalaClientCodegen
|
||||||
io.swagger.codegen.languages.CsharpDotNet2ClientCodegen
|
io.swagger.codegen.languages.CsharpDotNet2ClientCodegen
|
||||||
io.swagger.codegen.languages.ClojureClientCodegen
|
io.swagger.codegen.languages.ClojureClientCodegen
|
||||||
|
io.swagger.codegen.languages.HaskellServantCodegen
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
module Apis (
|
||||||
|
api
|
||||||
|
, API
|
||||||
|
) where
|
||||||
|
|
||||||
|
{{#apiInfo}}
|
||||||
|
{{#apis}}
|
||||||
|
import {{package}}.{{classname}} ({{classname}})
|
||||||
|
{{/apis}}
|
||||||
|
{{/apiInfo}}
|
||||||
|
|
||||||
|
import Data.Proxy
|
||||||
|
import Servant.API
|
||||||
|
import Test.QuickCheck
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
type API = {{#apiInfo}}{{#apis}}{{classname}}{{#hasMore}} :<|> {{/hasMore}}{{/apis}}{{/apiInfo}}
|
||||||
|
|
||||||
|
api :: Proxy API
|
||||||
|
api = Proxy
|
@ -0,0 +1,35 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Control.Monad (void)
|
||||||
|
import Control.Monad.Trans.Either
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Servant.API
|
||||||
|
import Servant.Client
|
||||||
|
|
||||||
|
import Data.List.Split (splitOn)
|
||||||
|
import Network.URI (URI (..), URIAuth (..), parseURI)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Test.QuickCheck
|
||||||
|
import Control.Monad
|
||||||
|
{{#models}}
|
||||||
|
import {{importPath}}
|
||||||
|
{{/models}}
|
||||||
|
{{#apiInfo}}
|
||||||
|
{{#apis}}
|
||||||
|
import {{package}}.{{classname}}
|
||||||
|
{{/apis}}
|
||||||
|
{{/apiInfo}}
|
||||||
|
|
||||||
|
-- userClient :: IO ()
|
||||||
|
-- userClient = do
|
||||||
|
-- users <- sample' (arbitrary :: Gen String)
|
||||||
|
-- let user = last users
|
||||||
|
-- void . runEitherT $ do
|
||||||
|
-- getUserByName user >>= (liftIO . putStrLn . show)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn "Hello Server!"
|
202
modules/swagger-codegen/src/main/resources/haskell/LICENSE
Normal file
202
modules/swagger-codegen/src/main/resources/haskell/LICENSE
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2015 Masahiro Yamauchi
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
@ -0,0 +1,7 @@
|
|||||||
|
# Generated Servant Codes
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
0. Install haskell-stack
|
||||||
|
1. stack build
|
||||||
|
2. stack exec client
|
@ -0,0 +1,13 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Apis
|
||||||
|
import Servant
|
||||||
|
import Servant.Mock
|
||||||
|
import qualified Network.Wai.Handler.Warp as Warp
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = Warp.run 8080 $ serve api (mock api)
|
@ -0,0 +1,2 @@
|
|||||||
|
import Distribution.Simple
|
||||||
|
main = defaultMain
|
@ -0,0 +1,27 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
module Utils where
|
||||||
|
|
||||||
|
import GHC.Generics
|
||||||
|
import Servant.API
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import Data.List.Split (splitOn)
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Test.QuickCheck
|
||||||
|
|
||||||
|
instance FromText [String] where
|
||||||
|
fromText = Just . splitOn "," . T.unpack
|
||||||
|
|
||||||
|
instance ToText [String] where
|
||||||
|
toText = T.pack . intercalate ","
|
||||||
|
|
||||||
|
lkp inputs l = case lookup l inputs of
|
||||||
|
Nothing -> Left $ "label " ++ T.unpack l ++ " not found"
|
||||||
|
Just v -> Right $ read (T.unpack v)
|
||||||
|
|
||||||
|
instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map.Map k v) where
|
||||||
|
arbitrary = Map.fromList <$> arbitrary
|
@ -0,0 +1,82 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
{{#operations}}
|
||||||
|
module {{package}}.{{classname}} (
|
||||||
|
{{#operation}}{{operationId}}{{#hasMore}}
|
||||||
|
, {{/hasMore}}{{/operation}}
|
||||||
|
, proxy{{classname}}
|
||||||
|
, {{classname}}
|
||||||
|
) where
|
||||||
|
{{/operations}}
|
||||||
|
|
||||||
|
import GHC.Generics
|
||||||
|
import Data.Proxy
|
||||||
|
import Servant.API
|
||||||
|
import Servant.Client
|
||||||
|
import Network.URI (URI (..), URIAuth (..), parseURI)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Servant.Common.Text
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Utils
|
||||||
|
import Test.QuickCheck
|
||||||
|
{{#imports}}import {{import}}
|
||||||
|
{{/imports}}
|
||||||
|
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
{{#hasFormParams}}
|
||||||
|
data Form{{#formParams}}{{baseName}}{{/formParams}} = Form{{#formParams}}{{baseName}}{{/formParams}}
|
||||||
|
{ {{#formParams}}{{baseName}} :: {{dataType}}{{#hasMore}}
|
||||||
|
, {{/hasMore}}{{/formParams}}
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromFormUrlEncoded Form{{#formParams}}{{baseName}}{{/formParams}} where
|
||||||
|
fromFormUrlEncoded inputs = Form{{#formParams}}{{baseName}}{{/formParams}} <$> {{#formParams}} lkp inputs "{{baseName}}"{{#hasMore}} <*> {{/hasMore}}{{/formParams}}
|
||||||
|
instance ToFormUrlEncoded Form{{#formParams}}{{baseName}}{{/formParams}} where
|
||||||
|
toFormUrlEncoded x = [({{#formParams}}(T.pack $ show $ {{package}}.{{classname}}.{{baseName}} x){{#hasMore}}, {{/hasMore}}{{/formParams}})]
|
||||||
|
instance Arbitrary Form{{#formParams}}{{baseName}}{{/formParams}} where
|
||||||
|
arbitrary = Form{{#formParams}}{{baseName}}{{/formParams}} <$> {{#formParams}}arbitrary{{#hasMore}} <*> {{/hasMore}}{{/formParams}}
|
||||||
|
{{/hasFormParams}}
|
||||||
|
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
||||||
|
|
||||||
|
{{#operations}}
|
||||||
|
type {{classname}} = {{#operation}}{{& nickname}} -- {{operationId}}{{#hasMore}}
|
||||||
|
:<|> {{/hasMore}}{{/operation}}
|
||||||
|
{{/operations}}
|
||||||
|
|
||||||
|
proxy{{classname}} :: Proxy {{classname}}
|
||||||
|
proxy{{classname}} = Proxy
|
||||||
|
|
||||||
|
{{#operations}}
|
||||||
|
|
||||||
|
serverPath :: String
|
||||||
|
serverPath = "{{basePath}}"
|
||||||
|
|
||||||
|
parseHostPort :: String -> (String, Int)
|
||||||
|
parseHostPort path = (host,port)
|
||||||
|
where
|
||||||
|
authority = case parseURI path of
|
||||||
|
Just x -> uriAuthority x
|
||||||
|
_ -> Nothing
|
||||||
|
(host, port) = case authority of
|
||||||
|
Just y -> (uriRegName y, (getPort . uriPort) y)
|
||||||
|
_ -> ("localhost", 8080)
|
||||||
|
getPort p = case (length p) of
|
||||||
|
0 -> 80
|
||||||
|
_ -> (read . drop 1) p
|
||||||
|
|
||||||
|
(host, port) = parseHostPort serverPath
|
||||||
|
|
||||||
|
{{#operation}}
|
||||||
|
{{operationId}}{{#hasMore}}
|
||||||
|
:<|> {{/hasMore}}{{/operation}}
|
||||||
|
= client proxy{{classname}} $ BaseUrl Http host port
|
||||||
|
{{/operations}}
|
@ -0,0 +1,66 @@
|
|||||||
|
name: haskell-servant-codegen
|
||||||
|
version: 0.1.0.0
|
||||||
|
synopsis: Swagger-codegen example for Haskell servant
|
||||||
|
description: Please see README.md
|
||||||
|
homepage: http://github.com/algas/haskell-servant-codegen#readme
|
||||||
|
license: Apache-2.0
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Masahiro Yamauchi
|
||||||
|
maintainer: sgt.yamauchi@gmail.com
|
||||||
|
copyright: 2015- Masahiro Yamauchi
|
||||||
|
category: Web
|
||||||
|
build-type: Simple
|
||||||
|
-- extra-source-files:
|
||||||
|
cabal-version: >=1.10
|
||||||
|
|
||||||
|
library
|
||||||
|
hs-source-dirs: lib
|
||||||
|
exposed-modules: Utils{{#models}}{{#model}}
|
||||||
|
, {{importPath}}{{/model}}{{/models}}
|
||||||
|
{{#apiInfo}}
|
||||||
|
{{#apis}}
|
||||||
|
, {{package}}.{{classname}}
|
||||||
|
{{/apis}}
|
||||||
|
{{/apiInfo}}
|
||||||
|
, Apis
|
||||||
|
ghc-options: -Wall
|
||||||
|
build-depends: base
|
||||||
|
, aeson
|
||||||
|
, text
|
||||||
|
, split
|
||||||
|
, containers
|
||||||
|
, network-uri
|
||||||
|
, QuickCheck
|
||||||
|
, servant
|
||||||
|
, servant-client
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
executable client
|
||||||
|
hs-source-dirs: client
|
||||||
|
main-is: Main.hs
|
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||||
|
build-depends: base
|
||||||
|
, either
|
||||||
|
, transformers
|
||||||
|
, split
|
||||||
|
, network-uri
|
||||||
|
, QuickCheck
|
||||||
|
, servant
|
||||||
|
, servant-client
|
||||||
|
, haskell-servant-codegen
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
executable server
|
||||||
|
hs-source-dirs: server
|
||||||
|
main-is: Main.hs
|
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||||
|
build-depends: base
|
||||||
|
, warp
|
||||||
|
, servant-server
|
||||||
|
, servant-mock
|
||||||
|
, haskell-servant-codegen
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
source-repository head
|
||||||
|
type: git
|
||||||
|
location: https://github.com/algas/haskell-servant-codegen
|
@ -0,0 +1,34 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
module {{package}}.{{classname}}
|
||||||
|
( {{classname}} (..)
|
||||||
|
) where
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
|
import GHC.Generics
|
||||||
|
import Test.QuickCheck
|
||||||
|
{{#imports}}import {{import}}
|
||||||
|
{{/imports}}
|
||||||
|
|
||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
|
||||||
|
data {{classname}} = {{classname}}
|
||||||
|
{ {{#vars}}{{& name}} :: {{datatype}}{{#hasMore}}
|
||||||
|
, {{/hasMore}}{{/vars}}
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromJSON {{classname}}
|
||||||
|
instance ToJSON {{classname}}
|
||||||
|
instance Arbitrary {{classname}} where
|
||||||
|
arbitrary = {{classname}} <$> {{#vars}}arbitrary{{#hasMore}} <*> {{/hasMore}}{{/vars}}
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
@ -0,0 +1,33 @@
|
|||||||
|
# For more information, see: https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md
|
||||||
|
|
||||||
|
# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)
|
||||||
|
resolver: lts-3.17
|
||||||
|
|
||||||
|
# Local packages, usually specified by relative directory name
|
||||||
|
packages:
|
||||||
|
- '.'
|
||||||
|
|
||||||
|
# Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3)
|
||||||
|
extra-deps:
|
||||||
|
- servant-mock-0.4.4.5
|
||||||
|
|
||||||
|
# Override default flag values for local packages and extra-deps
|
||||||
|
flags: {}
|
||||||
|
|
||||||
|
# Extra package databases containing global packages
|
||||||
|
extra-package-dbs: []
|
||||||
|
|
||||||
|
# Control whether we use the GHC we find on the path
|
||||||
|
# system-ghc: true
|
||||||
|
|
||||||
|
# Require a specific version of stack, using version ranges
|
||||||
|
# require-stack-version: -any # Default
|
||||||
|
# require-stack-version: >= 0.1.10.0
|
||||||
|
|
||||||
|
# Override the architecture used by stack, especially useful on Windows
|
||||||
|
# arch: i386
|
||||||
|
# arch: x86_64
|
||||||
|
|
||||||
|
# Extra directories used by stack for building
|
||||||
|
# extra-include-dirs: [/path/to/dir]
|
||||||
|
# extra-lib-dirs: [/path/to/dir]
|
@ -0,0 +1,36 @@
|
|||||||
|
package io.swagger.codegen.haskell;
|
||||||
|
|
||||||
|
import io.swagger.codegen.AbstractOptionsTest;
|
||||||
|
import io.swagger.codegen.CodegenConfig;
|
||||||
|
import io.swagger.codegen.languages.HaskellServantCodegen;
|
||||||
|
import io.swagger.codegen.options.HaskellServantOptionsProvider;
|
||||||
|
|
||||||
|
import mockit.Expectations;
|
||||||
|
import mockit.Tested;
|
||||||
|
|
||||||
|
public class HaskellServantOptionsTest extends AbstractOptionsTest {
|
||||||
|
|
||||||
|
@Tested
|
||||||
|
private HaskellServantCodegen clientCodegen;
|
||||||
|
|
||||||
|
public HaskellServantOptionsTest() {
|
||||||
|
super(new HaskellServantOptionsProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CodegenConfig getCodegenConfig() {
|
||||||
|
return clientCodegen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setExpectations() {
|
||||||
|
new Expectations(clientCodegen) {{
|
||||||
|
clientCodegen.setModelPackage(HaskellServantOptionsProvider.MODEL_PACKAGE_VALUE);
|
||||||
|
times = 1;
|
||||||
|
clientCodegen.setApiPackage(HaskellServantOptionsProvider.API_PACKAGE_VALUE);
|
||||||
|
times = 1;
|
||||||
|
clientCodegen.setSortParamsByRequiredFlag(Boolean.valueOf(HaskellServantOptionsProvider.SORT_PARAMS_VALUE));
|
||||||
|
times = 1;
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package io.swagger.codegen.haskell;
|
||||||
|
|
||||||
|
import io.swagger.codegen.CodegenModel;
|
||||||
|
import io.swagger.codegen.CodegenOperation;
|
||||||
|
import io.swagger.codegen.DefaultCodegen;
|
||||||
|
import io.swagger.codegen.languages.HaskellServantCodegen;
|
||||||
|
import io.swagger.models.Operation;
|
||||||
|
import io.swagger.models.Swagger;
|
||||||
|
import io.swagger.parser.SwaggerParser;
|
||||||
|
|
||||||
|
import org.testng.Assert;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
public class HaskellTest {
|
||||||
|
|
||||||
|
@Test(description = "convert a haskell model with dots")
|
||||||
|
public void modelTest() {
|
||||||
|
Assert.assertEquals(true, true);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package io.swagger.codegen.options;
|
||||||
|
|
||||||
|
import io.swagger.codegen.CodegenConstants;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class HaskellServantOptionsProvider implements OptionsProvider {
|
||||||
|
public static final String MODEL_PACKAGE_VALUE = "Model";
|
||||||
|
public static final String API_PACKAGE_VALUE = "Api";
|
||||||
|
public static final String SORT_PARAMS_VALUE = "false";
|
||||||
|
public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLanguage() {
|
||||||
|
return "haskell";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> createOptions() {
|
||||||
|
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
|
||||||
|
return builder.put(CodegenConstants.MODEL_PACKAGE, MODEL_PACKAGE_VALUE)
|
||||||
|
.put(CodegenConstants.API_PACKAGE, API_PACKAGE_VALUE)
|
||||||
|
.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_VALUE)
|
||||||
|
.put(CodegenConstants.ENSURE_UNIQUE_PARAMS, ENSURE_UNIQUE_PARAMS_VALUE)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isServer() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
202
samples/server/petstore/haskell/LICENSE
Normal file
202
samples/server/petstore/haskell/LICENSE
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2015 Masahiro Yamauchi
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
7
samples/server/petstore/haskell/README.md
Normal file
7
samples/server/petstore/haskell/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Generated Servant Codes
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
0. Install haskell-stack
|
||||||
|
1. stack build
|
||||||
|
2. stack exec client
|
2
samples/server/petstore/haskell/Setup.hs
Normal file
2
samples/server/petstore/haskell/Setup.hs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import Distribution.Simple
|
||||||
|
main = defaultMain
|
35
samples/server/petstore/haskell/client/Main.hs
Normal file
35
samples/server/petstore/haskell/client/Main.hs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Control.Monad (void)
|
||||||
|
import Control.Monad.Trans.Either
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Servant.API
|
||||||
|
import Servant.Client
|
||||||
|
|
||||||
|
import Data.List.Split (splitOn)
|
||||||
|
import Network.URI (URI (..), URIAuth (..), parseURI)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Test.QuickCheck
|
||||||
|
import Control.Monad
|
||||||
|
import Model.User
|
||||||
|
import Model.Category
|
||||||
|
import Model.Pet
|
||||||
|
import Model.Tag
|
||||||
|
import Model.Order
|
||||||
|
import Api.UserApi
|
||||||
|
import Api.StoreApi
|
||||||
|
import Api.PetApi
|
||||||
|
|
||||||
|
-- userClient :: IO ()
|
||||||
|
-- userClient = do
|
||||||
|
-- users <- sample' (arbitrary :: Gen String)
|
||||||
|
-- let user = last users
|
||||||
|
-- void . runEitherT $ do
|
||||||
|
-- getUserByName user >>= (liftIO . putStrLn . show)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn "Hello Server!"
|
@ -0,0 +1,68 @@
|
|||||||
|
name: haskell-servant-codegen
|
||||||
|
version: 0.1.0.0
|
||||||
|
synopsis: Swagger-codegen example for Haskell servant
|
||||||
|
description: Please see README.md
|
||||||
|
homepage: http://github.com/algas/haskell-servant-codegen#readme
|
||||||
|
license: Apache-2.0
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Masahiro Yamauchi
|
||||||
|
maintainer: sgt.yamauchi@gmail.com
|
||||||
|
copyright: 2015- Masahiro Yamauchi
|
||||||
|
category: Web
|
||||||
|
build-type: Simple
|
||||||
|
-- extra-source-files:
|
||||||
|
cabal-version: >=1.10
|
||||||
|
|
||||||
|
library
|
||||||
|
hs-source-dirs: lib
|
||||||
|
exposed-modules: Utils
|
||||||
|
, Model.User
|
||||||
|
, Model.Category
|
||||||
|
, Model.Pet
|
||||||
|
, Model.Tag
|
||||||
|
, Model.Order
|
||||||
|
, Api.UserApi
|
||||||
|
, Api.StoreApi
|
||||||
|
, Api.PetApi
|
||||||
|
, Apis
|
||||||
|
ghc-options: -Wall
|
||||||
|
build-depends: base
|
||||||
|
, aeson
|
||||||
|
, text
|
||||||
|
, split
|
||||||
|
, containers
|
||||||
|
, network-uri
|
||||||
|
, QuickCheck
|
||||||
|
, servant
|
||||||
|
, servant-client
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
executable client
|
||||||
|
hs-source-dirs: client
|
||||||
|
main-is: Main.hs
|
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||||
|
build-depends: base
|
||||||
|
, either
|
||||||
|
, transformers
|
||||||
|
, split
|
||||||
|
, network-uri
|
||||||
|
, QuickCheck
|
||||||
|
, servant
|
||||||
|
, servant-client
|
||||||
|
, haskell-servant-codegen
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
executable server
|
||||||
|
hs-source-dirs: server
|
||||||
|
main-is: Main.hs
|
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||||
|
build-depends: base
|
||||||
|
, warp
|
||||||
|
, servant-server
|
||||||
|
, servant-mock
|
||||||
|
, haskell-servant-codegen
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
source-repository head
|
||||||
|
type: git
|
||||||
|
location: https://github.com/algas/haskell-servant-codegen
|
104
samples/server/petstore/haskell/lib/Api/PetApi.hs
Normal file
104
samples/server/petstore/haskell/lib/Api/PetApi.hs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Api.PetApi (
|
||||||
|
updatePet
|
||||||
|
, addPet
|
||||||
|
, findPetsByStatus
|
||||||
|
, findPetsByTags
|
||||||
|
, getPetById
|
||||||
|
, updatePetWithForm
|
||||||
|
, deletePet
|
||||||
|
, uploadFile
|
||||||
|
, proxyPetApi
|
||||||
|
, PetApi
|
||||||
|
) where
|
||||||
|
|
||||||
|
import GHC.Generics
|
||||||
|
import Data.Proxy
|
||||||
|
import Servant.API
|
||||||
|
import Servant.Client
|
||||||
|
import Network.URI (URI (..), URIAuth (..), parseURI)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Servant.Common.Text
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Utils
|
||||||
|
import Test.QuickCheck
|
||||||
|
import Model.Pet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
data Formnamestatus = Formnamestatus
|
||||||
|
{ name :: String
|
||||||
|
, status :: String
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromFormUrlEncoded Formnamestatus where
|
||||||
|
fromFormUrlEncoded inputs = Formnamestatus <$> lkp inputs "name" <*> lkp inputs "status"
|
||||||
|
instance ToFormUrlEncoded Formnamestatus where
|
||||||
|
toFormUrlEncoded x = [((T.pack $ show $ Api.PetApi.name x), (T.pack $ show $ Api.PetApi.status x))]
|
||||||
|
instance Arbitrary Formnamestatus where
|
||||||
|
arbitrary = Formnamestatus <$> arbitrary <*> arbitrary
|
||||||
|
|
||||||
|
|
||||||
|
data FormadditionalMetadatafile = FormadditionalMetadatafile
|
||||||
|
{ additionalMetadata :: String
|
||||||
|
, file :: FilePath
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromFormUrlEncoded FormadditionalMetadatafile where
|
||||||
|
fromFormUrlEncoded inputs = FormadditionalMetadatafile <$> lkp inputs "additionalMetadata" <*> lkp inputs "file"
|
||||||
|
instance ToFormUrlEncoded FormadditionalMetadatafile where
|
||||||
|
toFormUrlEncoded x = [((T.pack $ show $ Api.PetApi.additionalMetadata x), (T.pack $ show $ Api.PetApi.file x))]
|
||||||
|
instance Arbitrary FormadditionalMetadatafile where
|
||||||
|
arbitrary = FormadditionalMetadatafile <$> arbitrary <*> arbitrary
|
||||||
|
|
||||||
|
|
||||||
|
type PetApi = "pet" :> ReqBody '[JSON] Pet :> Put '[JSON] () -- updatePet
|
||||||
|
:<|> "pet" :> ReqBody '[JSON] Pet :> Post '[JSON] () -- addPet
|
||||||
|
:<|> "pet" :> "findByStatus" :> QueryParam "status" [String] :> Get '[JSON] [Pet] -- findPetsByStatus
|
||||||
|
:<|> "pet" :> "findByTags" :> QueryParam "tags" [String] :> Get '[JSON] [Pet] -- findPetsByTags
|
||||||
|
:<|> "pet" :> Capture "petId" Integer :> Get '[JSON] Pet -- getPetById
|
||||||
|
:<|> "pet" :> Capture "petId" String :> ReqBody '[FormUrlEncoded] Formnamestatus :> Post '[JSON] () -- updatePetWithForm
|
||||||
|
:<|> "pet" :> Capture "petId" Integer :> Header "api_key" String :> Delete '[JSON] () -- deletePet
|
||||||
|
:<|> "pet" :> Capture "petId" Integer :> "uploadImage" :> ReqBody '[FormUrlEncoded] FormadditionalMetadatafile :> Post '[JSON] () -- uploadFile
|
||||||
|
|
||||||
|
proxyPetApi :: Proxy PetApi
|
||||||
|
proxyPetApi = Proxy
|
||||||
|
|
||||||
|
|
||||||
|
serverPath :: String
|
||||||
|
serverPath = "http://petstore.swagger.io/v2"
|
||||||
|
|
||||||
|
parseHostPort :: String -> (String, Int)
|
||||||
|
parseHostPort path = (host,port)
|
||||||
|
where
|
||||||
|
authority = case parseURI path of
|
||||||
|
Just x -> uriAuthority x
|
||||||
|
_ -> Nothing
|
||||||
|
(host, port) = case authority of
|
||||||
|
Just y -> (uriRegName y, (getPort . uriPort) y)
|
||||||
|
_ -> ("localhost", 8080)
|
||||||
|
getPort p = case (length p) of
|
||||||
|
0 -> 80
|
||||||
|
_ -> (read . drop 1) p
|
||||||
|
|
||||||
|
(host, port) = parseHostPort serverPath
|
||||||
|
|
||||||
|
updatePet
|
||||||
|
:<|> addPet
|
||||||
|
:<|> findPetsByStatus
|
||||||
|
:<|> findPetsByTags
|
||||||
|
:<|> getPetById
|
||||||
|
:<|> updatePetWithForm
|
||||||
|
:<|> deletePet
|
||||||
|
:<|> uploadFile
|
||||||
|
= client proxyPetApi $ BaseUrl Http host port
|
67
samples/server/petstore/haskell/lib/Api/StoreApi.hs
Normal file
67
samples/server/petstore/haskell/lib/Api/StoreApi.hs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Api.StoreApi (
|
||||||
|
getInventory
|
||||||
|
, placeOrder
|
||||||
|
, getOrderById
|
||||||
|
, deleteOrder
|
||||||
|
, proxyStoreApi
|
||||||
|
, StoreApi
|
||||||
|
) where
|
||||||
|
|
||||||
|
import GHC.Generics
|
||||||
|
import Data.Proxy
|
||||||
|
import Servant.API
|
||||||
|
import Servant.Client
|
||||||
|
import Network.URI (URI (..), URIAuth (..), parseURI)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Servant.Common.Text
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Utils
|
||||||
|
import Test.QuickCheck
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import Model.Order
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type StoreApi = "store" :> "inventory" :> Get '[JSON] (Map.Map String Integer) -- getInventory
|
||||||
|
:<|> "store" :> "order" :> ReqBody '[JSON] Order :> Post '[JSON] Order -- placeOrder
|
||||||
|
:<|> "store" :> "order" :> Capture "orderId" String :> Get '[JSON] Order -- getOrderById
|
||||||
|
:<|> "store" :> "order" :> Capture "orderId" String :> Delete '[JSON] () -- deleteOrder
|
||||||
|
|
||||||
|
proxyStoreApi :: Proxy StoreApi
|
||||||
|
proxyStoreApi = Proxy
|
||||||
|
|
||||||
|
|
||||||
|
serverPath :: String
|
||||||
|
serverPath = "http://petstore.swagger.io/v2"
|
||||||
|
|
||||||
|
parseHostPort :: String -> (String, Int)
|
||||||
|
parseHostPort path = (host,port)
|
||||||
|
where
|
||||||
|
authority = case parseURI path of
|
||||||
|
Just x -> uriAuthority x
|
||||||
|
_ -> Nothing
|
||||||
|
(host, port) = case authority of
|
||||||
|
Just y -> (uriRegName y, (getPort . uriPort) y)
|
||||||
|
_ -> ("localhost", 8080)
|
||||||
|
getPort p = case (length p) of
|
||||||
|
0 -> 80
|
||||||
|
_ -> (read . drop 1) p
|
||||||
|
|
||||||
|
(host, port) = parseHostPort serverPath
|
||||||
|
|
||||||
|
getInventory
|
||||||
|
:<|> placeOrder
|
||||||
|
:<|> getOrderById
|
||||||
|
:<|> deleteOrder
|
||||||
|
= client proxyStoreApi $ BaseUrl Http host port
|
82
samples/server/petstore/haskell/lib/Api/UserApi.hs
Normal file
82
samples/server/petstore/haskell/lib/Api/UserApi.hs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Api.UserApi (
|
||||||
|
createUser
|
||||||
|
, createUsersWithArrayInput
|
||||||
|
, createUsersWithListInput
|
||||||
|
, loginUser
|
||||||
|
, logoutUser
|
||||||
|
, getUserByName
|
||||||
|
, updateUser
|
||||||
|
, deleteUser
|
||||||
|
, proxyUserApi
|
||||||
|
, UserApi
|
||||||
|
) where
|
||||||
|
|
||||||
|
import GHC.Generics
|
||||||
|
import Data.Proxy
|
||||||
|
import Servant.API
|
||||||
|
import Servant.Client
|
||||||
|
import Network.URI (URI (..), URIAuth (..), parseURI)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Servant.Common.Text
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Utils
|
||||||
|
import Test.QuickCheck
|
||||||
|
import Model.User
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type UserApi = "user" :> ReqBody '[JSON] User :> Post '[JSON] () -- createUser
|
||||||
|
:<|> "user" :> "createWithArray" :> ReqBody '[JSON] [User] :> Post '[JSON] () -- createUsersWithArrayInput
|
||||||
|
:<|> "user" :> "createWithList" :> ReqBody '[JSON] [User] :> Post '[JSON] () -- createUsersWithListInput
|
||||||
|
:<|> "user" :> "login" :> QueryParam "username" String :> QueryParam "password" String :> Get '[JSON] String -- loginUser
|
||||||
|
:<|> "user" :> "logout" :> Get '[JSON] () -- logoutUser
|
||||||
|
:<|> "user" :> Capture "username" String :> Get '[JSON] User -- getUserByName
|
||||||
|
:<|> "user" :> Capture "username" String :> ReqBody '[JSON] User :> Put '[JSON] () -- updateUser
|
||||||
|
:<|> "user" :> Capture "username" String :> Delete '[JSON] () -- deleteUser
|
||||||
|
|
||||||
|
proxyUserApi :: Proxy UserApi
|
||||||
|
proxyUserApi = Proxy
|
||||||
|
|
||||||
|
|
||||||
|
serverPath :: String
|
||||||
|
serverPath = "http://petstore.swagger.io/v2"
|
||||||
|
|
||||||
|
parseHostPort :: String -> (String, Int)
|
||||||
|
parseHostPort path = (host,port)
|
||||||
|
where
|
||||||
|
authority = case parseURI path of
|
||||||
|
Just x -> uriAuthority x
|
||||||
|
_ -> Nothing
|
||||||
|
(host, port) = case authority of
|
||||||
|
Just y -> (uriRegName y, (getPort . uriPort) y)
|
||||||
|
_ -> ("localhost", 8080)
|
||||||
|
getPort p = case (length p) of
|
||||||
|
0 -> 80
|
||||||
|
_ -> (read . drop 1) p
|
||||||
|
|
||||||
|
(host, port) = parseHostPort serverPath
|
||||||
|
|
||||||
|
createUser
|
||||||
|
:<|> createUsersWithArrayInput
|
||||||
|
:<|> createUsersWithListInput
|
||||||
|
:<|> loginUser
|
||||||
|
:<|> logoutUser
|
||||||
|
:<|> getUserByName
|
||||||
|
:<|> updateUser
|
||||||
|
:<|> deleteUser
|
||||||
|
= client proxyUserApi $ BaseUrl Http host port
|
24
samples/server/petstore/haskell/lib/Apis.hs
Normal file
24
samples/server/petstore/haskell/lib/Apis.hs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
module Apis (
|
||||||
|
api
|
||||||
|
, API
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Api.UserApi (UserApi)
|
||||||
|
import Api.StoreApi (StoreApi)
|
||||||
|
import Api.PetApi (PetApi)
|
||||||
|
|
||||||
|
import Data.Proxy
|
||||||
|
import Servant.API
|
||||||
|
import Test.QuickCheck
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import Utils
|
||||||
|
|
||||||
|
type API = UserApi :<|> StoreApi :<|> PetApi
|
||||||
|
|
||||||
|
api :: Proxy API
|
||||||
|
api = Proxy
|
24
samples/server/petstore/haskell/lib/Model/Category.hs
Normal file
24
samples/server/petstore/haskell/lib/Model/Category.hs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
|
module Model.Category
|
||||||
|
( Category (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
|
import GHC.Generics
|
||||||
|
import Test.QuickCheck
|
||||||
|
|
||||||
|
|
||||||
|
data Category = Category
|
||||||
|
{ id_ :: Integer
|
||||||
|
, name :: String
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromJSON Category
|
||||||
|
instance ToJSON Category
|
||||||
|
instance Arbitrary Category where
|
||||||
|
arbitrary = Category <$> arbitrary <*> arbitrary
|
28
samples/server/petstore/haskell/lib/Model/Order.hs
Normal file
28
samples/server/petstore/haskell/lib/Model/Order.hs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
|
module Model.Order
|
||||||
|
( Order (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
|
import GHC.Generics
|
||||||
|
import Test.QuickCheck
|
||||||
|
|
||||||
|
|
||||||
|
data Order = Order
|
||||||
|
{ id_ :: Integer
|
||||||
|
, petId :: Integer
|
||||||
|
, quantity :: Integer
|
||||||
|
, shipDate :: Integer
|
||||||
|
, status :: String
|
||||||
|
, complete :: Bool
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromJSON Order
|
||||||
|
instance ToJSON Order
|
||||||
|
instance Arbitrary Order where
|
||||||
|
arbitrary = Order <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
|
30
samples/server/petstore/haskell/lib/Model/Pet.hs
Normal file
30
samples/server/petstore/haskell/lib/Model/Pet.hs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
|
module Model.Pet
|
||||||
|
( Pet (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
|
import GHC.Generics
|
||||||
|
import Test.QuickCheck
|
||||||
|
import Model.Category
|
||||||
|
import Model.Tag
|
||||||
|
|
||||||
|
|
||||||
|
data Pet = Pet
|
||||||
|
{ id_ :: Integer
|
||||||
|
, category :: Category
|
||||||
|
, name :: String
|
||||||
|
, photoUrls :: [String]
|
||||||
|
, tags :: [Tag]
|
||||||
|
, status :: String
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromJSON Pet
|
||||||
|
instance ToJSON Pet
|
||||||
|
instance Arbitrary Pet where
|
||||||
|
arbitrary = Pet <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
|
24
samples/server/petstore/haskell/lib/Model/Tag.hs
Normal file
24
samples/server/petstore/haskell/lib/Model/Tag.hs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
|
module Model.Tag
|
||||||
|
( Tag (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
|
import GHC.Generics
|
||||||
|
import Test.QuickCheck
|
||||||
|
|
||||||
|
|
||||||
|
data Tag = Tag
|
||||||
|
{ id_ :: Integer
|
||||||
|
, name :: String
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromJSON Tag
|
||||||
|
instance ToJSON Tag
|
||||||
|
instance Arbitrary Tag where
|
||||||
|
arbitrary = Tag <$> arbitrary <*> arbitrary
|
30
samples/server/petstore/haskell/lib/Model/User.hs
Normal file
30
samples/server/petstore/haskell/lib/Model/User.hs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
|
module Model.User
|
||||||
|
( User (..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson
|
||||||
|
import GHC.Generics
|
||||||
|
import Test.QuickCheck
|
||||||
|
|
||||||
|
|
||||||
|
data User = User
|
||||||
|
{ id_ :: Integer
|
||||||
|
, username :: String
|
||||||
|
, firstName :: String
|
||||||
|
, lastName :: String
|
||||||
|
, email :: String
|
||||||
|
, password :: String
|
||||||
|
, phone :: String
|
||||||
|
, userStatus :: Integer
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
|
||||||
|
instance FromJSON User
|
||||||
|
instance ToJSON User
|
||||||
|
instance Arbitrary User where
|
||||||
|
arbitrary = User <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
|
27
samples/server/petstore/haskell/lib/Utils.hs
Normal file
27
samples/server/petstore/haskell/lib/Utils.hs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
module Utils where
|
||||||
|
|
||||||
|
import GHC.Generics
|
||||||
|
import Servant.API
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import Data.List.Split (splitOn)
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import Test.QuickCheck
|
||||||
|
|
||||||
|
instance FromText [String] where
|
||||||
|
fromText = Just . splitOn "," . T.unpack
|
||||||
|
|
||||||
|
instance ToText [String] where
|
||||||
|
toText = T.pack . intercalate ","
|
||||||
|
|
||||||
|
lkp inputs l = case lookup l inputs of
|
||||||
|
Nothing -> Left $ "label " ++ T.unpack l ++ " not found"
|
||||||
|
Just v -> Right $ read (T.unpack v)
|
||||||
|
|
||||||
|
instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map.Map k v) where
|
||||||
|
arbitrary = Map.fromList <$> arbitrary
|
13
samples/server/petstore/haskell/server/Main.hs
Normal file
13
samples/server/petstore/haskell/server/Main.hs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
|
||||||
|
module Main where
|
||||||
|
|
||||||
|
import Apis
|
||||||
|
import Servant
|
||||||
|
import Servant.Mock
|
||||||
|
import qualified Network.Wai.Handler.Warp as Warp
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = Warp.run 8080 $ serve api (mock api)
|
33
samples/server/petstore/haskell/stack.yaml
Normal file
33
samples/server/petstore/haskell/stack.yaml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# For more information, see: https://github.com/commercialhaskell/stack/blob/release/doc/yaml_configuration.md
|
||||||
|
|
||||||
|
# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)
|
||||||
|
resolver: lts-3.17
|
||||||
|
|
||||||
|
# Local packages, usually specified by relative directory name
|
||||||
|
packages:
|
||||||
|
- '.'
|
||||||
|
|
||||||
|
# Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3)
|
||||||
|
extra-deps:
|
||||||
|
- servant-mock-0.4.4.5
|
||||||
|
|
||||||
|
# Override default flag values for local packages and extra-deps
|
||||||
|
flags: {}
|
||||||
|
|
||||||
|
# Extra package databases containing global packages
|
||||||
|
extra-package-dbs: []
|
||||||
|
|
||||||
|
# Control whether we use the GHC we find on the path
|
||||||
|
# system-ghc: true
|
||||||
|
|
||||||
|
# Require a specific version of stack, using version ranges
|
||||||
|
# require-stack-version: -any # Default
|
||||||
|
# require-stack-version: >= 0.1.10.0
|
||||||
|
|
||||||
|
# Override the architecture used by stack, especially useful on Windows
|
||||||
|
# arch: i386
|
||||||
|
# arch: x86_64
|
||||||
|
|
||||||
|
# Extra directories used by stack for building
|
||||||
|
# extra-include-dirs: [/path/to/dir]
|
||||||
|
# extra-lib-dirs: [/path/to/dir]
|
Loading…
x
Reference in New Issue
Block a user