forked from loafle/openapi-generator-original
Implement scala http4s server generator (#17430)
* Implement scala http4s server generator * Fix types and auth * Add proper handling of various responses * Fix configs * Drop null values in json encoder * Add sample files --------- Co-authored-by: m.tkachev <m.tkachev@tinkoff.ru>
This commit is contained in:
parent
11caad92df
commit
455add6d80
1
.github/workflows/samples-scala.yaml
vendored
1
.github/workflows/samples-scala.yaml
vendored
@ -31,6 +31,7 @@ jobs:
|
|||||||
- samples/server/petstore/scala-pekko-http-server
|
- samples/server/petstore/scala-pekko-http-server
|
||||||
- samples/server/petstore/scalatra
|
- samples/server/petstore/scalatra
|
||||||
- samples/server/petstore/scala-finch # cannot be tested with jdk11
|
- samples/server/petstore/scala-finch # cannot be tested with jdk11
|
||||||
|
- samples/server/petstore/scala-http4s-server
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v4
|
||||||
|
6
bin/configs/scala-http4s-server.yaml
Normal file
6
bin/configs/scala-http4s-server.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
generatorName: scala-http4s-server
|
||||||
|
outputDir: samples/server/petstore/scala-http4s-server
|
||||||
|
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
|
||||||
|
templateDir: modules/openapi-generator/src/main/resources/scala-http4s-server
|
||||||
|
additionalProperties:
|
||||||
|
artifactId: openapi-scala-http4s-server
|
@ -0,0 +1,852 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* https://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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openapitools.codegen.languages;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.media.ArraySchema;
|
||||||
|
import io.swagger.v3.oas.models.media.Schema;
|
||||||
|
import org.openapitools.codegen.*;
|
||||||
|
import org.openapitools.codegen.meta.features.*;
|
||||||
|
import org.openapitools.codegen.model.*;
|
||||||
|
import org.openapitools.codegen.utils.ModelUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ScalaHttp4sServerCodegen extends DefaultCodegen implements CodegenConfig {
|
||||||
|
private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sServerCodegen.class);
|
||||||
|
protected String artifactId = "http4s-server";
|
||||||
|
protected String artifactVersion = "1.0.0";
|
||||||
|
protected String sourceFolder = "scala";
|
||||||
|
protected String sourceSubFolder = "main";
|
||||||
|
private String packageName = "org.openapitools";
|
||||||
|
|
||||||
|
public static final String EXCLUDE_SBT = "excludeSbt"; // generate as whole project
|
||||||
|
public static final String SOURCE_SUBFOLDER = "sourceSubfolder"; // generate as whole project
|
||||||
|
|
||||||
|
public ScalaHttp4sServerCodegen() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
modifyFeatureSet(features -> features
|
||||||
|
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom))
|
||||||
|
.securityFeatures(EnumSet.noneOf(SecurityFeature.class))
|
||||||
|
.excludeGlobalFeatures(
|
||||||
|
GlobalFeature.XMLStructureDefinitions,
|
||||||
|
GlobalFeature.Callbacks,
|
||||||
|
GlobalFeature.LinkObjects,
|
||||||
|
GlobalFeature.ParameterStyling
|
||||||
|
)
|
||||||
|
.excludeSchemaSupportFeatures(
|
||||||
|
SchemaSupportFeature.Polymorphism
|
||||||
|
)
|
||||||
|
.excludeParameterFeatures(
|
||||||
|
ParameterFeature.Cookie
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
embeddedTemplateDir = templateDir = "scala-http4s-server";
|
||||||
|
|
||||||
|
apiPackage = packageName + ".apis";
|
||||||
|
modelPackage = packageName + ".models";
|
||||||
|
useOneOfInterfaces = true;
|
||||||
|
supportsMultipleInheritance = true;
|
||||||
|
supportsInheritance = true;
|
||||||
|
supportsMixins = true;
|
||||||
|
addOneOfInterfaceImports =true;
|
||||||
|
|
||||||
|
|
||||||
|
setReservedWordsLowerCase(
|
||||||
|
Arrays.asList(
|
||||||
|
// Scala
|
||||||
|
"abstract", "case", "catch", "class", "def",
|
||||||
|
"do", "else", "extends", "false", "final",
|
||||||
|
"finally", "for", "forSome", "if", "implicit",
|
||||||
|
"import", "lazy", "match", "new", "null",
|
||||||
|
"object", "override", "package", "private", "protected",
|
||||||
|
"return", "sealed", "super", "this", "throw",
|
||||||
|
"trait", "try", "true", "type", "val",
|
||||||
|
"var", "while", "with", "yield",
|
||||||
|
// Scala-interop languages keywords
|
||||||
|
"abstract", "continue", "switch", "assert",
|
||||||
|
"default", "synchronized", "goto",
|
||||||
|
"break", "double", "implements", "byte",
|
||||||
|
"public", "throws", "enum", "instanceof", "transient",
|
||||||
|
"int", "short", "char", "interface", "static",
|
||||||
|
"void", "finally", "long", "strictfp", "volatile", "const", "float",
|
||||||
|
"native")
|
||||||
|
);
|
||||||
|
|
||||||
|
defaultIncludes = new HashSet<>(
|
||||||
|
Arrays.asList("double",
|
||||||
|
"Int",
|
||||||
|
"Long",
|
||||||
|
"Float",
|
||||||
|
"Double",
|
||||||
|
"char",
|
||||||
|
"float",
|
||||||
|
"String",
|
||||||
|
"boolean",
|
||||||
|
"Boolean",
|
||||||
|
"Double",
|
||||||
|
"Integer",
|
||||||
|
"Long",
|
||||||
|
"Float",
|
||||||
|
"List",
|
||||||
|
"Set",
|
||||||
|
"Map")
|
||||||
|
);
|
||||||
|
|
||||||
|
typeMapping = new HashMap<>();
|
||||||
|
typeMapping.put("string", "String");
|
||||||
|
typeMapping.put("boolean", "Boolean");
|
||||||
|
typeMapping.put("integer", "Int");
|
||||||
|
typeMapping.put("long", "Long");
|
||||||
|
typeMapping.put("float", "Float");
|
||||||
|
typeMapping.put("double", "Double");
|
||||||
|
typeMapping.put("number", "BigDecimal");
|
||||||
|
typeMapping.put("decimal", "BigDecimal");
|
||||||
|
typeMapping.put("date-time", "ZonedDateTime");
|
||||||
|
typeMapping.put("offset-date-time", "OffsetDateTime");
|
||||||
|
typeMapping.put("date", "LocalDate");
|
||||||
|
typeMapping.put("file", "File");
|
||||||
|
typeMapping.put("array", "List");
|
||||||
|
typeMapping.put("list", "List");
|
||||||
|
typeMapping.put("map", "Map");
|
||||||
|
typeMapping.put("object", "Object");
|
||||||
|
typeMapping.put("binary", "Array[Byte]");
|
||||||
|
typeMapping.put("Date", "LocalDate");
|
||||||
|
typeMapping.put("DateTime", "ZonedDateTime");
|
||||||
|
typeMapping.put("OffsetDateTime", "OffsetDateTime");
|
||||||
|
typeMapping.put("uuid", "UUID");
|
||||||
|
|
||||||
|
additionalProperties.put("modelPackage", modelPackage());
|
||||||
|
additionalProperties.put("apiPackage", apiPackage());
|
||||||
|
additionalProperties.put("infoUrl", "http://org.openapitools");
|
||||||
|
additionalProperties.put("infoEmail", "team@openapitools.org");
|
||||||
|
additionalProperties.put("licenseInfo", "Apache 2.0");
|
||||||
|
additionalProperties.put("licenseUrl", "http://apache.org/licenses/LICENSE-2.0.html");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
languageSpecificPrimitives = new HashSet<>(
|
||||||
|
Arrays.asList(
|
||||||
|
"String",
|
||||||
|
"Boolean",
|
||||||
|
"Double",
|
||||||
|
"Int",
|
||||||
|
"Integer",
|
||||||
|
"Long",
|
||||||
|
"Float",
|
||||||
|
"Any",
|
||||||
|
"AnyVal",
|
||||||
|
"AnyRef",
|
||||||
|
"Object",
|
||||||
|
"BigDecimal"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
instantiationTypes.put("array", "ArrayList");
|
||||||
|
instantiationTypes.put("map", "HashMap");
|
||||||
|
|
||||||
|
importMapping = new HashMap<>();
|
||||||
|
importMapping.put("UUID", "java.util.UUID");
|
||||||
|
importMapping.put("URI", "java.net.URI");
|
||||||
|
importMapping.put("File", "java.io.File");
|
||||||
|
importMapping.put("Date", "java.util.Date");
|
||||||
|
importMapping.put("Timestamp", "java.sql.Timestamp");
|
||||||
|
importMapping.put("Map", "scala.collection.immutable.Map");
|
||||||
|
importMapping.put("HashMap", "scala.collection.immutable.HashMap");
|
||||||
|
importMapping.put("Seq", "scala.collection.immutable.Seq");
|
||||||
|
importMapping.put("ArrayBuffer", "scala.collection.mutable.ArrayBuffer");
|
||||||
|
importMapping.put("DateTime", "java.time.LocalDateTime");
|
||||||
|
importMapping.put("LocalDateTime", "java.time.LocalDateTime");
|
||||||
|
importMapping.put("LocalDate", "java.time.LocalDate");
|
||||||
|
importMapping.put("LocalTime", "java.time.LocalTime");
|
||||||
|
importMapping.put("ZonedDateTime", "java.time.ZonedDateTime");
|
||||||
|
importMapping.put("OffsetDateTime", "java.time.OffsetDateTime");
|
||||||
|
//refined
|
||||||
|
importMapping.put("Refined", "eu.timepit.refined.api.Refined");
|
||||||
|
importMapping.put("And", "eu.timepit.refined.boolean.And");
|
||||||
|
importMapping.put("MinSize", "eu.timepit.refined.collection.MinSize");
|
||||||
|
importMapping.put("MaxSize", "eu.timepit.refined.collection.MaxSize");
|
||||||
|
importMapping.put("MatchesRegex", "eu.timepit.refined.string.MatchesRegex");
|
||||||
|
importMapping.put("Greater", "eu.timepit.refined.numeric.Greater");
|
||||||
|
importMapping.put("GreaterEqual", "eu.timepit.refined.numeric.GreaterEqual");
|
||||||
|
importMapping.put("Less", "eu.timepit.refined.numeric.Less");
|
||||||
|
importMapping.put("LessEqual", "eu.timepit.refined.numeric.LessEqual");
|
||||||
|
|
||||||
|
|
||||||
|
cliOptions.add(new CliOption(EXCLUDE_SBT, "exclude sbt from generation"));
|
||||||
|
cliOptions.add(new CliOption(SOURCE_SUBFOLDER, "name of subfolder, for example to generate code in src/scala/generated"));
|
||||||
|
|
||||||
|
inlineSchemaOption.put("SKIP_SCHEMA_REUSE", "true");
|
||||||
|
inlineSchemaOption.put("REFACTOR_ALLOF_INLINE_SCHEMAS", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Map<String, String> locationStatusToResponse = new HashMap<>();
|
||||||
|
static {
|
||||||
|
locationStatusToResponse.put("300", "MultipleChoices");
|
||||||
|
locationStatusToResponse.put("301", "MovedPermanently");
|
||||||
|
locationStatusToResponse.put("302", "Found");
|
||||||
|
locationStatusToResponse.put("303", "SeeOther");
|
||||||
|
locationStatusToResponse.put("307", "TemporaryRedirect");
|
||||||
|
locationStatusToResponse.put("308", "PermanentRedirect");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Map<String, String> wwwAuthStatusToResponse = new HashMap<>();
|
||||||
|
static {
|
||||||
|
wwwAuthStatusToResponse.put("401", "Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Map<String, String> allowStatusToResponse = new HashMap<>();
|
||||||
|
static {
|
||||||
|
allowStatusToResponse.put("405", "MethodNotAllowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Map<String, String> proxyAuthStatusToResponse = new HashMap<>();
|
||||||
|
static {
|
||||||
|
proxyAuthStatusToResponse.put("407", "ProxyAuthenticationRequired");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Map<String, String> statusToResponse = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
statusToResponse.put("100", "Continue");
|
||||||
|
|
||||||
|
statusToResponse.put("101", "SwitchingProtocols");
|
||||||
|
|
||||||
|
statusToResponse.put("102", "Processing");
|
||||||
|
statusToResponse.put("103", "EarlyHints");
|
||||||
|
|
||||||
|
statusToResponse.put("200", "Ok");
|
||||||
|
statusToResponse.put("201", "Created");
|
||||||
|
statusToResponse.put("202", "Accepted");
|
||||||
|
statusToResponse.put("203", "NonAuthoritativeInformation");
|
||||||
|
statusToResponse.put("204", "NoContent");
|
||||||
|
statusToResponse.put("205", "ResetContent");
|
||||||
|
statusToResponse.put("206", "PartialContent");
|
||||||
|
statusToResponse.put("207", "MultiStatus");
|
||||||
|
statusToResponse.put("208", "AlreadyReported");
|
||||||
|
statusToResponse.put("226", "IMUsed");
|
||||||
|
|
||||||
|
statusToResponse.put("304", "NotModified");
|
||||||
|
statusToResponse.put("305", "UseProxy");
|
||||||
|
|
||||||
|
statusToResponse.put("400", "BadRequest");
|
||||||
|
statusToResponse.put("402", "PaymentRequired");
|
||||||
|
statusToResponse.put("403", "Forbidden");
|
||||||
|
statusToResponse.put("404", "NotFound");
|
||||||
|
statusToResponse.put("406", "NotAcceptable");
|
||||||
|
statusToResponse.put("408", "RequestTimeout");
|
||||||
|
statusToResponse.put("409", "Conflict");
|
||||||
|
statusToResponse.put("410", "Gone");
|
||||||
|
statusToResponse.put("411", "LengthRequired");
|
||||||
|
statusToResponse.put("412", "PreconditionFailed");
|
||||||
|
statusToResponse.put("413", "PayloadTooLarge");
|
||||||
|
statusToResponse.put("414", "UriTooLong");
|
||||||
|
statusToResponse.put("415", "UnsupportedMediaType");
|
||||||
|
statusToResponse.put("416", "RangeNotSatisfiable");
|
||||||
|
statusToResponse.put("417", "ExpectationFailed");
|
||||||
|
statusToResponse.put("418", "ImATeapot");
|
||||||
|
statusToResponse.put("421", "MisdirectedRequest");
|
||||||
|
statusToResponse.put("422", "UnprocessableEntity");
|
||||||
|
statusToResponse.put("423", "Locked");
|
||||||
|
statusToResponse.put("424", "FailedDependency");
|
||||||
|
statusToResponse.put("425", "TooEarly");
|
||||||
|
statusToResponse.put("426", "UpgradeRequired");
|
||||||
|
statusToResponse.put("428", "PreconditionRequired");
|
||||||
|
statusToResponse.put("429", "TooManyRequests");
|
||||||
|
statusToResponse.put("431", "RequestHeaderFieldsTooLarge");
|
||||||
|
statusToResponse.put("451", "UnavailableForLegalReasons");
|
||||||
|
|
||||||
|
statusToResponse.put("500", "InternalServerError");
|
||||||
|
statusToResponse.put("501", "NotImplemented");
|
||||||
|
statusToResponse.put("502", "BadGateway");
|
||||||
|
statusToResponse.put("503", "ServiceUnavailable");
|
||||||
|
statusToResponse.put("504", "GatewayTimeout");
|
||||||
|
statusToResponse.put("505", "HttpVersionNotSupported");
|
||||||
|
statusToResponse.put("506", "VariantAlsoNegotiates");
|
||||||
|
statusToResponse.put("507", "InsufficientStorage");
|
||||||
|
statusToResponse.put("508", "LoopDetected");
|
||||||
|
statusToResponse.put("510", "NotExtended");
|
||||||
|
statusToResponse.put("511", "NetworkAuthenticationRequired");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processOpts() {
|
||||||
|
super.processOpts();
|
||||||
|
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
|
||||||
|
packageName = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME);
|
||||||
|
|
||||||
|
setApiPackage(packageName + ".apis");
|
||||||
|
additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage());
|
||||||
|
|
||||||
|
setModelPackage(packageName + ".models");
|
||||||
|
additionalProperties.put(CodegenConstants.PACKAGE_NAME, modelPackage());
|
||||||
|
} else {
|
||||||
|
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalProperties.containsKey(SOURCE_SUBFOLDER)) {
|
||||||
|
sourceSubFolder = (String) additionalProperties.get(SOURCE_SUBFOLDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceFolder = "src" + File.separator + sourceSubFolder + File.separator + sourceFolder;
|
||||||
|
|
||||||
|
supportingFiles.add(new SupportingFile("types.mustache", modelFileFolderRelative(), "types.scala"));
|
||||||
|
supportingFiles.add(new SupportingFile("path.mustache", apiFileFolderRelative(), "path.scala"));
|
||||||
|
supportingFiles.add(new SupportingFile("query.mustache", apiFileFolderRelative(), "query.scala"));
|
||||||
|
|
||||||
|
supportingFiles.add(new SupportingFile("apis.mustache", packageFileFolderRelative(), "api.scala"));
|
||||||
|
|
||||||
|
apiTemplateFiles.put("api.mustache", ".scala");
|
||||||
|
|
||||||
|
if (!additionalProperties.containsKey(EXCLUDE_SBT) && !Boolean.parseBoolean((String)additionalProperties.get(EXCLUDE_SBT))) {
|
||||||
|
supportingFiles.add(new SupportingFile("build.sbt", "", "build.sbt"));
|
||||||
|
supportingFiles.add(new SupportingFile("build.properties", "project", "build.properties"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> inlineSchemaOption() {
|
||||||
|
return super.inlineSchemaOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnablePostProcessFile() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessFile(File file, String fileType) {
|
||||||
|
System.out.println("postprocess " + file.toString());
|
||||||
|
super.postProcessFile(file, fileType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
|
||||||
|
Map<String, ModelsMap> modelsMap = super.postProcessAllModels(objs);
|
||||||
|
|
||||||
|
for (ModelsMap mm : modelsMap.values()) {
|
||||||
|
for (ModelMap model : mm.getModels()) {
|
||||||
|
// model oneOf as sealed trait
|
||||||
|
|
||||||
|
CodegenModel cModel = model.getModel();
|
||||||
|
cModel.getVendorExtensions().put("x-isSealedTrait", !cModel.oneOf.isEmpty());
|
||||||
|
|
||||||
|
if (cModel.discriminator != null) {
|
||||||
|
cModel.getVendorExtensions().put("x-use-discr", true);
|
||||||
|
|
||||||
|
if (cModel.discriminator.getMapping() != null) {
|
||||||
|
cModel.getVendorExtensions().put("x-use-discr-mapping", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
List<String> exts = (List<String>) cModel.getVendorExtensions().get("x-implements");
|
||||||
|
if (exts != null) {
|
||||||
|
cModel.getVendorExtensions().put("x-extends", exts.subList(0, 1));
|
||||||
|
cModel.getVendorExtensions().put("x-extendsWith", exts.subList(1, exts.size()));
|
||||||
|
}
|
||||||
|
} catch (IndexOutOfBoundsException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// add refined constraints
|
||||||
|
|
||||||
|
for (CodegenProperty prop: cModel.vars) {
|
||||||
|
Set<String> imports = new TreeSet<>();
|
||||||
|
|
||||||
|
prop.getVendorExtensions().putAll(refineProp(prop, imports));
|
||||||
|
|
||||||
|
cModel.imports.addAll(imports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modelsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> makeRefiined(Set<String> imports, String dataType, ArrayList<String> refined) {
|
||||||
|
Map<String, Object> vendorExtensions = new HashMap<>();
|
||||||
|
if (!refined.isEmpty()) {
|
||||||
|
imports.add("And");
|
||||||
|
imports.add("Refined");
|
||||||
|
|
||||||
|
String refinedRgt = String.join(" And ", refined);
|
||||||
|
|
||||||
|
vendorExtensions.put("x-type", "Refined[" + dataType + ", " + refinedRgt + "]");
|
||||||
|
vendorExtensions.put("x-refined-lft", dataType);
|
||||||
|
vendorExtensions.put("x-refined-rgt", refinedRgt);
|
||||||
|
vendorExtensions.put("x-refined", true);
|
||||||
|
} else {
|
||||||
|
vendorExtensions.put("x-type", dataType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vendorExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> refineProp(IJsonSchemaValidationProperties prop, Set<String> imports) {
|
||||||
|
Map<String, Object> vendorExtensions = new HashMap<>();
|
||||||
|
|
||||||
|
vendorExtensions.put("x-type", prop.getDataType());
|
||||||
|
|
||||||
|
if (prop.getIsString()) {
|
||||||
|
ArrayList<String> refined = new ArrayList<>();
|
||||||
|
|
||||||
|
if (prop.getMinLength() != null) {
|
||||||
|
refined.add("MinSize[" + prop.getMinLength() + "]");
|
||||||
|
imports.add("MinSize");
|
||||||
|
}
|
||||||
|
if (prop.getMaxLength() != null) {
|
||||||
|
refined.add("MaxSize[" + prop.getMaxLength() + "]");
|
||||||
|
imports.add("MaxSize");
|
||||||
|
}
|
||||||
|
if (prop.getPattern() != null) {
|
||||||
|
try {
|
||||||
|
String fixedPattern = prop.getPattern().substring(1, prop.getPattern().length() - 1);
|
||||||
|
refined.add("MatchesRegex[\"" + fixedPattern + "\"]");
|
||||||
|
imports.add("MatchesRegex");
|
||||||
|
} catch (IndexOutOfBoundsException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vendorExtensions.putAll(makeRefiined(imports, prop.getDataType(), refined));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("Int".equals(prop.getDataType())
|
||||||
|
|| "Long".equals(prop.getDataType())
|
||||||
|
|| "Float".equals(prop.getDataType())
|
||||||
|
|| "Double".equals(prop.getDataType())
|
||||||
|
|| "BigDecimal".equals(prop.getDataType())
|
||||||
|
) {
|
||||||
|
ArrayList<String> refined = new ArrayList<>();
|
||||||
|
|
||||||
|
if (prop.getMinimum() != null) {
|
||||||
|
if (prop.getExclusiveMinimum()) {
|
||||||
|
refined.add("Greater[" + prop.getMinimum() + "]");
|
||||||
|
imports.add("Greater");
|
||||||
|
} else {
|
||||||
|
refined.add("GreaterEqual[" + prop.getMinimum() + "]");
|
||||||
|
imports.add("GreaterEqual");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prop.getMaximum() != null) {
|
||||||
|
if (prop.getExclusiveMaximum()) {
|
||||||
|
refined.add("Less[" + prop.getMaximum() + "]");
|
||||||
|
imports.add("Less");
|
||||||
|
} else {
|
||||||
|
refined.add("LessEqual[" + prop.getMaximum() + "]");
|
||||||
|
imports.add("LessEqual");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vendorExtensions.putAll(makeRefiined(imports, prop.getDataType(), refined));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.getIsUuid() || "Uuid".equals(prop.getDataType())) {
|
||||||
|
prop.setDataType("UUID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.getIsArray() && prop.getItems() != null) {
|
||||||
|
Map<String, Object> subVendorExtensions = refineProp(prop.getItems(), imports);
|
||||||
|
prop.getItems().getVendorExtensions().putAll(subVendorExtensions);
|
||||||
|
|
||||||
|
ArrayList<String> refined = new ArrayList<>();
|
||||||
|
if (prop.getMinItems() != null) {
|
||||||
|
refined.add("MinSize[" + prop.getMinItems() + "]");
|
||||||
|
imports.add("MinSize");
|
||||||
|
}
|
||||||
|
if (prop.getMaxItems() != null) {
|
||||||
|
refined.add("MaxSize[" + prop.getMaxItems() + "]");
|
||||||
|
imports.add("MaxSize");
|
||||||
|
}
|
||||||
|
|
||||||
|
vendorExtensions.putAll(makeRefiined(imports, prop.getDataType(), refined));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vendorExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
|
||||||
|
Map<String, Object> bundle = super.postProcessSupportingFileData(objs);
|
||||||
|
|
||||||
|
List<ModelMap> models = (List<ModelMap>) bundle.get("models");
|
||||||
|
TreeSet<String> allImports = new TreeSet<>();
|
||||||
|
for (ModelMap mm: models) {
|
||||||
|
for (String nextImport : mm.getModel().imports) {
|
||||||
|
String mapping = importMapping().get(nextImport);
|
||||||
|
if (mapping != null && !defaultIncludes().contains(mapping)) {
|
||||||
|
allImports.add(mapping);
|
||||||
|
}
|
||||||
|
// add instantiation types
|
||||||
|
mapping = instantiationTypes().get(nextImport);
|
||||||
|
if (mapping != null && !defaultIncludes().contains(mapping)) {
|
||||||
|
allImports.add(mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bundle.put("imports", allImports);
|
||||||
|
bundle.put("packageName", packageName);
|
||||||
|
|
||||||
|
|
||||||
|
ApiInfoMap apiInfoMap = (ApiInfoMap) bundle.get("apiInfo");
|
||||||
|
Map<String, List<String>> authToOperationMap = new TreeMap<>();
|
||||||
|
for (OperationsMap op: apiInfoMap.getApis()) {
|
||||||
|
List<HashMap<String, Object>> opsByAuth = (List<HashMap<String, Object>>) op.get("operationsByAuth");
|
||||||
|
for (HashMap<String, Object> auth: opsByAuth) {
|
||||||
|
String autName = (String) auth.get("auth");
|
||||||
|
String classname = (String) op.get("classname");
|
||||||
|
List<String> classnames = authToOperationMap.computeIfAbsent(autName, k -> new ArrayList<>());
|
||||||
|
classnames.add(classname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle.put("authToOperationMap",
|
||||||
|
authToOperationMap.entrySet().stream().map(ent -> {
|
||||||
|
Map<String, Object> tuple = new HashMap<>();
|
||||||
|
String auth = ent.getKey();
|
||||||
|
tuple.put("auth", auth);
|
||||||
|
tuple.put("ops", ent.getValue());
|
||||||
|
tuple.put("addMiddleware", !"".equals(auth));
|
||||||
|
return tuple;
|
||||||
|
}).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodegenType getTag() {
|
||||||
|
return CodegenType.SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "scala-http4s-server";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelp() {
|
||||||
|
return "Generates a Scala http4s server bindings.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escapeReservedWord(String name) {
|
||||||
|
return "_" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apiFileFolder() {
|
||||||
|
return outputFolder + File.separator + apiFileFolderRelative() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String apiFileFolderRelative() {
|
||||||
|
return sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String modelFileFolder() {
|
||||||
|
return outputFolder + File.separator + modelFileFolderRelative();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String modelFileFolderRelative() {
|
||||||
|
return sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String packageFileFolderRelative() {
|
||||||
|
return sourceFolder + File.separator + packageName.replace('.', File.separatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OperationsMap postProcessOperationsWithModels(OperationsMap objsI, List<ModelMap> allModels) {
|
||||||
|
OperationsMap objs = super.postProcessOperationsWithModels(objsI, allModels);
|
||||||
|
OperationMap operations = objs.getOperations();
|
||||||
|
|
||||||
|
List<CodegenOperation> operationList = operations.getOperation();
|
||||||
|
Set<String> allAuth = new HashSet<>();
|
||||||
|
Map<String, List<String>> opsByAuth = new HashMap<>();
|
||||||
|
|
||||||
|
for (CodegenOperation op : operationList) {
|
||||||
|
|
||||||
|
// Converts GET /foo/bar => GET / foo / bar =>
|
||||||
|
generateScalaPath(op);
|
||||||
|
|
||||||
|
// :? fooQueryParam
|
||||||
|
generateQueryParameters(op);
|
||||||
|
|
||||||
|
// decide wat methods do we need in delegate:
|
||||||
|
if (op.consumes == null || op.consumes.size() == 0) {
|
||||||
|
op.vendorExtensions.put("x-generic-body", true);
|
||||||
|
} else {
|
||||||
|
if (op.consumes.stream().anyMatch(x -> x.containsKey("isJson"))) {
|
||||||
|
op.vendorExtensions.put("x-json-body", true);
|
||||||
|
}
|
||||||
|
if (op.consumes.stream().anyMatch(x -> !x.containsKey("isJson"))) {
|
||||||
|
op.vendorExtensions.put("x-generic-body", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decide wat methods do we need in responses:
|
||||||
|
for (CodegenResponse resp: op.responses) {
|
||||||
|
if (resp.code.equals("0"))
|
||||||
|
resp.code = "200"; // 200 by default
|
||||||
|
|
||||||
|
String responseName;
|
||||||
|
|
||||||
|
responseName = locationStatusToResponse.get(resp.code);
|
||||||
|
if (responseName != null) {
|
||||||
|
resp.vendorExtensions.put("x-response-location", true);
|
||||||
|
} else {
|
||||||
|
responseName = wwwAuthStatusToResponse.get(resp.code);
|
||||||
|
if (responseName != null) {
|
||||||
|
resp.vendorExtensions.put("x-response-www-auth", true);
|
||||||
|
} else {
|
||||||
|
responseName = allowStatusToResponse.get(resp.code);
|
||||||
|
if (responseName != null) {
|
||||||
|
resp.vendorExtensions.put("x-response-allow", true);
|
||||||
|
} else {
|
||||||
|
responseName = proxyAuthStatusToResponse.get(resp.code);
|
||||||
|
if (responseName != null) {
|
||||||
|
resp.vendorExtensions.put("x-response-proxy-auth", true);
|
||||||
|
} else {
|
||||||
|
responseName = statusToResponse.get(resp.code);
|
||||||
|
if (responseName != null) {
|
||||||
|
resp.vendorExtensions.put("x-response-standard", true);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("unsupported status " + resp.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.vendorExtensions.put("x-response", responseName);
|
||||||
|
|
||||||
|
if (resp.getContent() == null) {
|
||||||
|
resp.vendorExtensions.put("x-generic-response", true); // non json resp
|
||||||
|
} else {
|
||||||
|
if (resp.getContent().containsKey("application/json")) {
|
||||||
|
resp.vendorExtensions.put("x-json-response", true); // json resp
|
||||||
|
} else {
|
||||||
|
resp.vendorExtensions.put("x-generic-response", true); // non json resp
|
||||||
|
}
|
||||||
|
if (resp.getContent().size() > 1) {
|
||||||
|
resp.vendorExtensions.put("x-generic-response", true); // non json resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op.authMethods != null) {
|
||||||
|
for (CodegenSecurity cs: op.authMethods) {
|
||||||
|
allAuth.add(cs.name);
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> authDup = new ArrayList<>();
|
||||||
|
for (CodegenSecurity authMeth: op.authMethods) {
|
||||||
|
Map<String, Object> vals = new HashMap<>();
|
||||||
|
vals.put("authName", authMeth.name);
|
||||||
|
vals.put("operation", op);
|
||||||
|
authDup.add(vals);
|
||||||
|
|
||||||
|
opsByAuth.computeIfAbsent(authMeth.name, k -> new ArrayList<>()).add(op.operationId);
|
||||||
|
}
|
||||||
|
op.vendorExtensions.put("x-authed", authDup);
|
||||||
|
} else {
|
||||||
|
opsByAuth.computeIfAbsent("", k -> new ArrayList<>()).add(op.operationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeSet<String> allImports = new TreeSet<>();
|
||||||
|
List<String> currentImports = objs.getImports().stream().flatMap(m -> m.values().stream()).collect(Collectors.toList());
|
||||||
|
for (CodegenOperation op : operationList) {
|
||||||
|
for (String nextImport : op.imports) {
|
||||||
|
String mapping = importMapping().get(nextImport);
|
||||||
|
if (mapping != null && !defaultIncludes().contains(mapping)) {
|
||||||
|
if (!currentImports.contains(mapping)) {
|
||||||
|
allImports.add(mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add instantiation types
|
||||||
|
mapping = instantiationTypes().get(nextImport);
|
||||||
|
if (mapping != null && !currentImports.contains(mapping)) {
|
||||||
|
if (!currentImports.contains(mapping)) {
|
||||||
|
allImports.add(mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
objs.put("operationsByAuth", opsByAuth.entrySet().stream().map(ent -> {
|
||||||
|
HashMap<String, Object> tuple = new HashMap<>();
|
||||||
|
tuple.put("auth", ent.getKey());
|
||||||
|
tuple.put("ops", ent.getValue());
|
||||||
|
return tuple;
|
||||||
|
}
|
||||||
|
).collect(Collectors.toList()));
|
||||||
|
objs.put("extraImports", allImports);
|
||||||
|
objs.put("allAuth", allAuth);
|
||||||
|
|
||||||
|
return objs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("Duplicates")
|
||||||
|
@Override
|
||||||
|
public String getTypeDeclaration(Schema p) {
|
||||||
|
if (ModelUtils.isArraySchema(p)) {
|
||||||
|
ArraySchema ap = (ArraySchema) p;
|
||||||
|
Schema inner = ap.getItems();
|
||||||
|
return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]";
|
||||||
|
} else if (ModelUtils.isMapSchema(p)) {
|
||||||
|
Schema inner = ModelUtils.getAdditionalProperties(p);
|
||||||
|
|
||||||
|
return getSchemaType(p) + "[String, " + getTypeDeclaration(inner) + "]";
|
||||||
|
}
|
||||||
|
return super.getTypeDeclaration(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSchemaType(Schema p) {
|
||||||
|
String schemaType = super.getSchemaType(p);
|
||||||
|
String type;
|
||||||
|
if (typeMapping.containsKey(schemaType)) {
|
||||||
|
type = typeMapping.get(schemaType);
|
||||||
|
if (languageSpecificPrimitives.contains(type)) {
|
||||||
|
return toModelName(type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type = schemaType;
|
||||||
|
}
|
||||||
|
return toModelName(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escapeQuotationMark(String input) {
|
||||||
|
// remove " to avoid code injection
|
||||||
|
return input.replace("\"", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escapeUnsafeCharacters(String input) {
|
||||||
|
return input.replace("*/", "*_/").replace("/*", "/_*");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateScalaPath(CodegenOperation op) {
|
||||||
|
Set<String> imports = new HashSet<>();
|
||||||
|
|
||||||
|
String path = op.path;
|
||||||
|
|
||||||
|
// remove first /
|
||||||
|
if (path.startsWith("/")) {
|
||||||
|
path = path.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove last /
|
||||||
|
if (path.endsWith("/")) {
|
||||||
|
path = path.substring(0, path.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] items = path.split("/", -1);
|
||||||
|
String scalaPath = "";
|
||||||
|
int pathParamIndex = 0;
|
||||||
|
|
||||||
|
for (String item : items) {
|
||||||
|
|
||||||
|
if (item.matches("^\\{(.*)}$")) { // wrap in {}
|
||||||
|
// find the datatype of the parameter
|
||||||
|
final CodegenParameter cp = op.pathParams.get(pathParamIndex);
|
||||||
|
|
||||||
|
// TODO: Handle non-primitives…
|
||||||
|
scalaPath = scalaPath + " / " + cpToPathParameter(cp, imports, cp.vendorExtensions);
|
||||||
|
|
||||||
|
pathParamIndex++;
|
||||||
|
} else {
|
||||||
|
scalaPath = scalaPath + " / " + "\"" + item + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
op.vendorExtensions.put("x-codegen-path", scalaPath);
|
||||||
|
op.imports.addAll(imports);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String cpToPathParameter(CodegenParameter cp, Set<String> imports, Map<String, Object> vendorExtensions) {
|
||||||
|
// don't support containers and arrays yet, reset to string
|
||||||
|
if (cp.isContainer || cp.isArray) {
|
||||||
|
cp.setDataType("String");
|
||||||
|
cp.setIsArray(false);
|
||||||
|
cp.setIsString(true);
|
||||||
|
cp.isContainer = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> _vendorExtensions = refineProp(cp, imports);
|
||||||
|
vendorExtensions.putAll(_vendorExtensions);
|
||||||
|
|
||||||
|
if (_vendorExtensions.size() == 1) { // only `x-type`
|
||||||
|
if ("String".equals(cp.getDataType())) {
|
||||||
|
return cp.baseName;
|
||||||
|
} else {
|
||||||
|
return cp.dataType + "Varr(" + cp.baseName + ")";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return cp.baseName + "Varr(" + cp.baseName + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateQueryParameters(CodegenOperation op) {
|
||||||
|
Set<String> imports = new HashSet<>();
|
||||||
|
String queryString = "";
|
||||||
|
|
||||||
|
for (CodegenParameter cp : op.queryParams) {
|
||||||
|
if (queryString.isEmpty()) {
|
||||||
|
queryString = queryString + " :? ";
|
||||||
|
} else {
|
||||||
|
queryString = queryString + " +& ";
|
||||||
|
}
|
||||||
|
|
||||||
|
queryString = queryString + cpToQueryParameter(cp, imports, cp.vendorExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
op.vendorExtensions.put("x-codegen-query", queryString);
|
||||||
|
op.imports.addAll(imports);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String cpToQueryParameter(CodegenParameter cp, Set<String> imports, Map<String, Object> vendorExtensions) {
|
||||||
|
// don't support containers and arrays yet, reset to string
|
||||||
|
if (cp.isContainer && !cp.isArray) {
|
||||||
|
cp.setDataType("String");
|
||||||
|
cp.setIsArray(false);
|
||||||
|
cp.setIsString(true);
|
||||||
|
cp.isContainer = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vendorExtensions.putAll(refineProp(cp, imports));
|
||||||
|
return cp.baseName + "QueryParam(" + cp.baseName + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcess() {
|
||||||
|
System.out.println("################################################################################");
|
||||||
|
System.out.println("# Thanks for using OpenAPI Generator. #");
|
||||||
|
System.out.println("# Please consider donation to help us maintain this project \uD83D\uDE4F #");
|
||||||
|
System.out.println("# https://opencollective.com/openapi_generator/donate #");
|
||||||
|
System.out.println("# #");
|
||||||
|
System.out.println("# This generator's contributed by Jim Schubert (https://github.com/jimschubert)#");
|
||||||
|
System.out.println("# Please support his work directly via https://patreon.com/jimschubert \uD83D\uDE4F #");
|
||||||
|
System.out.println("################################################################################");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeneratorLanguage generatorLanguage() { return GeneratorLanguage.SCALA; }
|
||||||
|
}
|
@ -118,6 +118,7 @@ org.openapitools.codegen.languages.ScalaPekkoClientCodegen
|
|||||||
org.openapitools.codegen.languages.ScalaAkkaHttpServerCodegen
|
org.openapitools.codegen.languages.ScalaAkkaHttpServerCodegen
|
||||||
org.openapitools.codegen.languages.ScalaFinchServerCodegen
|
org.openapitools.codegen.languages.ScalaFinchServerCodegen
|
||||||
org.openapitools.codegen.languages.ScalaGatlingCodegen
|
org.openapitools.codegen.languages.ScalaGatlingCodegen
|
||||||
|
org.openapitools.codegen.languages.ScalaHttp4sServerCodegen
|
||||||
org.openapitools.codegen.languages.ScalaLagomServerCodegen
|
org.openapitools.codegen.languages.ScalaLagomServerCodegen
|
||||||
org.openapitools.codegen.languages.ScalaPlayFrameworkServerCodegen
|
org.openapitools.codegen.languages.ScalaPlayFrameworkServerCodegen
|
||||||
org.openapitools.codegen.languages.ScalaSttpClientCodegen
|
org.openapitools.codegen.languages.ScalaSttpClientCodegen
|
||||||
|
255
modules/openapi-generator/src/main/resources/scala-http4s-server/api.mustache
vendored
Normal file
255
modules/openapi-generator/src/main/resources/scala-http4s-server/api.mustache
vendored
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
package {{apiPackage}}
|
||||||
|
|
||||||
|
import {{apiPackage}}.path._
|
||||||
|
import {{apiPackage}}.query._
|
||||||
|
|
||||||
|
{{#imports}}import {{import}}
|
||||||
|
{{/imports}}
|
||||||
|
|
||||||
|
{{#extraImports}}import {{.}}
|
||||||
|
{{/extraImports}}
|
||||||
|
|
||||||
|
import cats.Monad
|
||||||
|
import cats.syntax.all._
|
||||||
|
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.circe._
|
||||||
|
import org.http4s.server._
|
||||||
|
import org.http4s.headers._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
|
||||||
|
final case class {{classname}}Routes[
|
||||||
|
F[_]: JsonDecoder: Monad{{#allAuth}}, {{.}}{{/allAuth}}
|
||||||
|
](delegate: {{classname}}Delegate[F{{#allAuth}}, {{.}}{{/allAuth}}]) extends Http4sDsl[F] {
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
object {{operationId}} {
|
||||||
|
import {{classname}}Delegate.{{operationId}}Responses
|
||||||
|
|
||||||
|
{{#pathParams}}
|
||||||
|
{{#vendorExtensions.x-refined}}
|
||||||
|
object {{baseName}}Varr extends RefinedVarr[{{vendorExtensions.x-refined-lft}}, {{{vendorExtensions.x-refined-rgt}}}]
|
||||||
|
{{/vendorExtensions.x-refined}}
|
||||||
|
{{/pathParams}}
|
||||||
|
{{#queryParams}}
|
||||||
|
{{#isArray}}
|
||||||
|
{{#required}}
|
||||||
|
object {{baseName}}QueryParam extends QuerySeqParamDecoderMatcher[{{{items.vendorExtensions.x-type}}}]("{{baseName}}")
|
||||||
|
{{/required}}
|
||||||
|
{{^required}}
|
||||||
|
object {{baseName}}QueryParam extends OptionalQuerySeqParamDecoderMatcher[{{{items.vendorExtensions.x-type}}}]("{{baseName}}")
|
||||||
|
{{/required}}
|
||||||
|
{{/isArray}}
|
||||||
|
{{^isArray}}
|
||||||
|
{{#required}}
|
||||||
|
object {{baseName}}QueryParam extends QueryParamDecoderMatcher[{{{vendorExtensions.x-type}}}]("{{baseName}}")
|
||||||
|
{{/required}}
|
||||||
|
{{^required}}
|
||||||
|
object {{baseName}}QueryParam extends OptionalQueryParamDecoderMatcher[{{{vendorExtensions.x-type}}}]("{{baseName}}")
|
||||||
|
{{/required}}
|
||||||
|
{{/isArray}}
|
||||||
|
{{/queryParams}}
|
||||||
|
|
||||||
|
{{^vendorExtensions.x-authed}}
|
||||||
|
val route = HttpRoutes.of[F] {
|
||||||
|
case req @ {{{httpMethod}}} -> Root{{{vendorExtensions.x-codegen-path}}}{{{vendorExtensions.x-codegen-query}}} =>
|
||||||
|
{{#vendorExtensions.x-json-body}}
|
||||||
|
{{#vendorExtensions.x-generic-body}}
|
||||||
|
req.contentType match {
|
||||||
|
case Some(`Content-Type`(MediaType.application.json, _)) =>
|
||||||
|
{{>delegateCallJson}}
|
||||||
|
case _ =>
|
||||||
|
{{>delegateCallGeneric}}
|
||||||
|
}
|
||||||
|
{{/vendorExtensions.x-generic-body}}
|
||||||
|
{{^vendorExtensions.x-generic-body}}
|
||||||
|
{{>delegateCallJson}}
|
||||||
|
{{/vendorExtensions.x-generic-body}}
|
||||||
|
{{/vendorExtensions.x-json-body}}
|
||||||
|
{{^vendorExtensions.x-json-body}}
|
||||||
|
{{>delegateCallGeneric}}
|
||||||
|
{{/vendorExtensions.x-json-body}}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{/vendorExtensions.x-authed}}
|
||||||
|
{{#vendorExtensions.x-authed}}
|
||||||
|
val route{{authName}} = AuthedRoutes.of[{{authName}}, F] {
|
||||||
|
case (req @ {{{httpMethod}}} -> Root{{{vendorExtensions.x-codegen-path}}}{{{vendorExtensions.x-codegen-query}}}) as auth =>
|
||||||
|
{{#vendorExtensions.x-json-body}}
|
||||||
|
{{#vendorExtensions.x-generic-body}}
|
||||||
|
req.contentType match {
|
||||||
|
case Some(`Content-Type`(MediaType.application.json, _)) =>
|
||||||
|
{{>delegateCallJson}}
|
||||||
|
case _ =>
|
||||||
|
{{>delegateCallGeneric}}
|
||||||
|
}
|
||||||
|
{{/vendorExtensions.x-generic-body}}
|
||||||
|
{{^vendorExtensions.x-generic-body}}
|
||||||
|
{{>delegateCallJson}}
|
||||||
|
{{/vendorExtensions.x-generic-body}}
|
||||||
|
{{/vendorExtensions.x-json-body}}
|
||||||
|
{{^vendorExtensions.x-json-body}}
|
||||||
|
{{>delegateCallGeneric}}
|
||||||
|
{{/vendorExtensions.x-json-body}}
|
||||||
|
}
|
||||||
|
{{/vendorExtensions.x-authed}}
|
||||||
|
|
||||||
|
val responses: {{operationId}}Responses[F] = new {{operationId}}Responses[F] {
|
||||||
|
{{#responses}}
|
||||||
|
{{#vendorExtensions.x-response-location}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(location: Location, value: {{{dataType}}}): F[Response[F]] = {{vendorExtensions.x-response}}(location, value)
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(location: Location): F[Response[F]] = {{vendorExtensions.x-response}}(location)
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-location}}
|
||||||
|
{{#vendorExtensions.x-response-www-auth}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(authenticate: `WWW-Authenticate`, value: {{{dataType}}}): F[Response[F]] = {{vendorExtensions.x-response}}(authenticate, value)
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(authenticate: `WWW-Authenticate`): F[Response[F]] = {{vendorExtensions.x-response}}(authenticate)
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-www-auth}}
|
||||||
|
{{#vendorExtensions.x-response-allow}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(allow: Allow, value: {{{dataType}}}): F[Response[F]] = {{vendorExtensions.x-response}}(allow, value)
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(allow: Allow): F[Response[F]] = {{vendorExtensions.x-response}}(allow)
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-allow}}
|
||||||
|
{{#vendorExtensions.x-response-proxy-auth}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(authenticate: `Proxy-Authenticate`, value: {{{dataType}}}): F[Response[F]] = {{vendorExtensions.x-response}}(value, authenticate)
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(authenticate: `Proxy-Authenticate`): F[Response[F]] = {{vendorExtensions.x-response}}(authenticate)
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-proxy-auth}}
|
||||||
|
{{#vendorExtensions.x-response-standard}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(value: {{{dataType}}}): F[Response[F]] = {{vendorExtensions.x-response}}(value)
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(): F[Response[F]] = {{vendorExtensions.x-response}}()
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-standard}}
|
||||||
|
{{/responses}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
||||||
|
|
||||||
|
{{#operationsByAuth}}
|
||||||
|
val routes{{auth}} =
|
||||||
|
{{#ops}}
|
||||||
|
{{.}}.route{{auth}}{{^-last}} <+>{{/-last}}
|
||||||
|
{{/ops}}
|
||||||
|
{{/operationsByAuth}}
|
||||||
|
}
|
||||||
|
|
||||||
|
object {{classname}}Delegate {
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
trait {{operationId}}Responses[F[_]] {
|
||||||
|
{{#responses}}
|
||||||
|
{{#vendorExtensions.x-response-location}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(location: Location, value: {{{dataType}}}): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(location: Location): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-location}}
|
||||||
|
{{#vendorExtensions.x-response-www-auth}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(authenticate: `WWW-Authenticate`, value: {{{dataType}}}): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(authenticate: `WWW-Authenticate`): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-www-auth}}
|
||||||
|
{{#vendorExtensions.x-response-allow}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(allow: Allow, value: {{{dataType}}}): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(allow: Allow): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-allow}}
|
||||||
|
{{#vendorExtensions.x-response-proxy-auth}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(authenticate: `Proxy-Authenticate`, value: {{{dataType}}}): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(authenticate: `Proxy-Authenticate`): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-proxy-auth}}
|
||||||
|
{{#vendorExtensions.x-response-standard}}
|
||||||
|
{{#vendorExtensions.x-json-response}}
|
||||||
|
def resp{{code}}(value: {{{dataType}}}): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-json-response}}
|
||||||
|
{{#vendorExtensions.x-generic-response}}
|
||||||
|
def resp{{code}}(): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-generic-response}}
|
||||||
|
{{/vendorExtensions.x-response-standard}}
|
||||||
|
{{/responses}}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait {{classname}}Delegate[F[_]{{#allAuth}}, {{.}}{{/allAuth}}] {
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
|
||||||
|
trait {{operationId}} {
|
||||||
|
import {{classname}}Delegate.{{operationId}}Responses
|
||||||
|
{{#vendorExtensions.x-json-body}}
|
||||||
|
|
||||||
|
{{^vendorExtensions.x-authed}}
|
||||||
|
def handle(
|
||||||
|
req: Request[F],
|
||||||
|
{{operationId}}: F[{{{bodyParam.dataType}}}],
|
||||||
|
{{> delegateArgs}} responses: {{operationId}}Responses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-authed}}
|
||||||
|
|
||||||
|
{{#vendorExtensions.x-authed}}
|
||||||
|
def handle_{{authName}}(
|
||||||
|
auth: {{authName}},
|
||||||
|
req: Request[F],
|
||||||
|
{{operationId}}: F[{{{bodyParam.dataType}}}],
|
||||||
|
{{> delegateArgs}} responses: {{operationId}}Responses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
{{/vendorExtensions.x-authed}}
|
||||||
|
{{/vendorExtensions.x-json-body}}
|
||||||
|
|
||||||
|
{{#vendorExtensions.x-generic-body}}
|
||||||
|
{{^vendorExtensions.x-authed}}
|
||||||
|
def handle(
|
||||||
|
req: Request[F],
|
||||||
|
{{> delegateArgs}} responses: {{operationId}}Responses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
{{/vendorExtensions.x-authed}}
|
||||||
|
|
||||||
|
{{#vendorExtensions.x-authed}}
|
||||||
|
def handle_{{authName}}(
|
||||||
|
auth: {{authName}},
|
||||||
|
req: Request[F],
|
||||||
|
{{> delegateArgs}} responses: {{operationId}}Responses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
{{/vendorExtensions.x-authed}}
|
||||||
|
{{/vendorExtensions.x-generic-body}}
|
||||||
|
}
|
||||||
|
def {{operationId}}: {{operationId}}
|
||||||
|
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
||||||
|
}
|
53
modules/openapi-generator/src/main/resources/scala-http4s-server/apis.mustache
vendored
Normal file
53
modules/openapi-generator/src/main/resources/scala-http4s-server/apis.mustache
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package {{packageName}}
|
||||||
|
|
||||||
|
import org.http4s.circe._
|
||||||
|
import cats.Monad
|
||||||
|
import cats.syntax.all._
|
||||||
|
import cats.data.OptionT
|
||||||
|
import cats.data.Kleisli
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.server._
|
||||||
|
|
||||||
|
import {{apiPackage}}._
|
||||||
|
|
||||||
|
final case class API [
|
||||||
|
F[_]: JsonDecoder: Monad{{#authMethods}}, {{name}}{{/authMethods}}
|
||||||
|
](
|
||||||
|
{{#authMethods}}
|
||||||
|
{{#lambda.camelcase}}{{name}}{{/lambda.camelcase}}: Kleisli[OptionT[F, *], Request[F], {{name}}],
|
||||||
|
{{/authMethods}}
|
||||||
|
)(
|
||||||
|
{{#apiInfo}}
|
||||||
|
{{#apis}}
|
||||||
|
{{#operations}}
|
||||||
|
delegate{{classname}}: {{classname}}Delegate[F{{#allAuth}}, {{.}}{{/allAuth}}],
|
||||||
|
{{/operations}}
|
||||||
|
{{/apis}}
|
||||||
|
{{/apiInfo}}
|
||||||
|
){
|
||||||
|
{{#authToOperationMap}}
|
||||||
|
{{#addMiddleware}}
|
||||||
|
val {{#lambda.camelcase}}{{auth}}{{/lambda.camelcase}}Middleware = AuthMiddleware{{^-last}}.withFallThrough{{/-last}}({{#lambda.camelcase}}{{auth}}{{/lambda.camelcase}})
|
||||||
|
{{/addMiddleware}}
|
||||||
|
{{/authToOperationMap}}
|
||||||
|
|
||||||
|
{{#apiInfo}}
|
||||||
|
{{#apis}}
|
||||||
|
{{#operations}}
|
||||||
|
val {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}Routes = new {{classname}}Routes(delegate{{classname}})
|
||||||
|
{{/operations}}
|
||||||
|
{{/apis}}
|
||||||
|
{{/apiInfo}}
|
||||||
|
|
||||||
|
{{#authToOperationMap}}
|
||||||
|
val routes{{auth}} = {{#addMiddleware}}{{#lambda.camelcase}}{{auth}}{{/lambda.camelcase}}Middleware({{/addMiddleware}}
|
||||||
|
{{#ops}}
|
||||||
|
{{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}Routes.routes{{auth}}{{^-last}} <+>{{/-last}}
|
||||||
|
{{/ops}}{{#addMiddleware}}){{/addMiddleware}}
|
||||||
|
{{/authToOperationMap}}
|
||||||
|
|
||||||
|
val routesAll =
|
||||||
|
{{#authToOperationMap}}
|
||||||
|
routes{{auth}}{{^-last}} <+>{{/-last}}
|
||||||
|
{{/authToOperationMap}}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
sbt.version=1.8.2
|
@ -0,0 +1,27 @@
|
|||||||
|
scalaVersion := "2.13.11"
|
||||||
|
scalacOptions += "-Ymacro-annotations"
|
||||||
|
|
||||||
|
val circeVersion = "0.14.5"
|
||||||
|
def circe(artifact: String): ModuleID = "io.circe" %% s"circe-$artifact" % circeVersion
|
||||||
|
|
||||||
|
val http4sVersion = "0.23.23"
|
||||||
|
def http4s(artifact: String): ModuleID = "org.http4s" %% s"http4s-$artifact" % http4sVersion
|
||||||
|
|
||||||
|
val refinedVersion = "0.9.29"
|
||||||
|
val refined = Seq(
|
||||||
|
"eu.timepit" %% "refined" % refinedVersion,
|
||||||
|
"eu.timepit" %% "refined-cats" % refinedVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
val catsVersion = "2.10.0"
|
||||||
|
val cats = Seq("org.typelevel" %% "cats-core" % catsVersion)
|
||||||
|
|
||||||
|
lazy val compilerPlugins = Seq(
|
||||||
|
compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
|
||||||
|
compilerPlugin("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full)
|
||||||
|
)
|
||||||
|
|
||||||
|
libraryDependencies ++= (Seq(
|
||||||
|
http4s("core"), http4s("ember-server"), http4s("circe"), http4s("dsl"),
|
||||||
|
circe("core"), circe("generic"), circe("parser"), circe("refined")
|
||||||
|
) ++ refined ++ cats ++ compilerPlugins)
|
21
modules/openapi-generator/src/main/resources/scala-http4s-server/delegateArgs.mustache
vendored
Normal file
21
modules/openapi-generator/src/main/resources/scala-http4s-server/delegateArgs.mustache
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{{#pathParams}}
|
||||||
|
{{baseName}}: {{{vendorExtensions.x-type}}},
|
||||||
|
{{/pathParams}}
|
||||||
|
{{#queryParams}}
|
||||||
|
{{#isArray}}
|
||||||
|
{{#required}}
|
||||||
|
{{baseName}}: List[{{{items.vendorExtensions.x-type}}}],
|
||||||
|
{{/required}}
|
||||||
|
{{^required}}
|
||||||
|
{{baseName}}: Option[List[{{{items.vendorExtensions.x-type}}}]],
|
||||||
|
{{/required}}
|
||||||
|
{{/isArray}}
|
||||||
|
{{^isArray}}
|
||||||
|
{{#required}}
|
||||||
|
{{baseName}}: {{{vendorExtensions.x-type}}},
|
||||||
|
{{/required}}
|
||||||
|
{{^required}}
|
||||||
|
{{baseName}}: Option[{{{vendorExtensions.x-type}}}],
|
||||||
|
{{/required}}
|
||||||
|
{{/isArray}}
|
||||||
|
{{/queryParams}}
|
@ -0,0 +1,6 @@
|
|||||||
|
{{^authName}}
|
||||||
|
delegate.{{operationId}}.handle(req, {{#pathParams}}{{baseName}}, {{/pathParams}}{{#queryParams}}{{baseName}}, {{/queryParams}}responses)
|
||||||
|
{{/authName}}
|
||||||
|
{{#authName}}
|
||||||
|
delegate.{{operationId}}.handle_{{authName}}(auth, req, {{#pathParams}}{{baseName}}, {{/pathParams}}{{#queryParams}}{{baseName}}, {{/queryParams}}responses)
|
||||||
|
{{/authName}}
|
6
modules/openapi-generator/src/main/resources/scala-http4s-server/delegateCallJson.mustache
vendored
Normal file
6
modules/openapi-generator/src/main/resources/scala-http4s-server/delegateCallJson.mustache
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{{^authName}}
|
||||||
|
delegate.{{operationId}}.handle(req, req.asJsonDecode[{{{bodyParam.dataType}}}] , {{#pathParams}}{{baseName}}, {{/pathParams}}{{#queryParams}}{{baseName}}, {{/queryParams}}responses)
|
||||||
|
{{/authName}}
|
||||||
|
{{#authName}}
|
||||||
|
delegate.{{operationId}}.handle_{{authName}}(auth, req, req.asJsonDecode[{{{bodyParam.dataType}}}] , {{#pathParams}}{{baseName}}, {{/pathParams}}{{#queryParams}}{{baseName}}, {{/queryParams}}responses)
|
||||||
|
{{/authName}}
|
62
modules/openapi-generator/src/main/resources/scala-http4s-server/path.mustache
vendored
Normal file
62
modules/openapi-generator/src/main/resources/scala-http4s-server/path.mustache
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package {{apiPackage}}
|
||||||
|
|
||||||
|
import cats.syntax.all._
|
||||||
|
import cats.data.ValidatedNel
|
||||||
|
|
||||||
|
import eu.timepit.refined._
|
||||||
|
import eu.timepit.refined.api.Refined
|
||||||
|
import eu.timepit.refined.api.Validate
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
object path {
|
||||||
|
trait Varrr[T] {
|
||||||
|
def unapply(str: String): Option[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val LocalDateVarr: Varrr[LocalDate] = new Varrr[LocalDate] {
|
||||||
|
def unapply(str: String): Option[LocalDate] = Try(LocalDate.parse(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val LocalDateTimeVarr: Varrr[LocalDateTime] = new Varrr[LocalDateTime] {
|
||||||
|
def unapply(str: String): Option[LocalDateTime] = Try(LocalDateTime.parse(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val ZonedDateTimeVarr: Varrr[ZonedDateTime] = new Varrr[ZonedDateTime] {
|
||||||
|
def unapply(str: String): Option[ZonedDateTime] = Try(ZonedDateTime.parse(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val UUIDVarr: Varrr[UUID] = new Varrr[UUID] {
|
||||||
|
def unapply(str: String): Option[UUID] = Try(java.util.UUID.fromString(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val IntVarr: Varrr[Int] = new Varrr[Int] {
|
||||||
|
def unapply(str: String): Option[Int] = Try(str.toInt).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val LongVarr: Varrr[Long] = new Varrr[Long] {
|
||||||
|
def unapply(str: String): Option[Long] = Try(str.toLong).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val DoubleVarr: Varrr[Double] = new Varrr[Double] {
|
||||||
|
def unapply(str: String): Option[Double] = Try(str.toDouble).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val BigDecimalVarr: Varrr[BigDecimal] = new Varrr[BigDecimal] {
|
||||||
|
def unapply(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val StringVarr: Varrr[String] = new Varrr[String] {
|
||||||
|
def unapply(str: String): Option[String] = str.some
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class RefinedVarr[T, P](implicit varrr: Varrr[T], validate: Validate[T, P]) extends Varrr[Refined[T, P]] {
|
||||||
|
def unapply(str: String): Option[Refined[T, P]] =
|
||||||
|
varrr.unapply(str).flatMap(x => refineV(x).toOption)
|
||||||
|
}
|
||||||
|
}
|
59
modules/openapi-generator/src/main/resources/scala-http4s-server/query.mustache
vendored
Normal file
59
modules/openapi-generator/src/main/resources/scala-http4s-server/query.mustache
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package {{apiPackage}}
|
||||||
|
|
||||||
|
import cats.data.NonEmptyList
|
||||||
|
import cats.data.ValidatedNel
|
||||||
|
import cats.syntax.all._
|
||||||
|
|
||||||
|
import eu.timepit.refined._
|
||||||
|
import eu.timepit.refined.api.Refined
|
||||||
|
import eu.timepit.refined.api.Validate
|
||||||
|
|
||||||
|
import org.http4s.ParseFailure
|
||||||
|
import org.http4s.QueryParamDecoder
|
||||||
|
import org.http4s.QueryParameterValue
|
||||||
|
|
||||||
|
import java.time._
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object query {
|
||||||
|
implicit def rrrefinedQueryParamDecoder[T, P](
|
||||||
|
implicit tDecoder: QueryParamDecoder[T], validate: Validate[T, P]
|
||||||
|
): QueryParamDecoder[Refined[T, P]] = new QueryParamDecoder[Refined[T, P]] {
|
||||||
|
def decode(value: QueryParameterValue): ValidatedNel[ParseFailure,Refined[T, P]] =
|
||||||
|
tDecoder.decode(value).withEither(t => t.flatMap(x =>
|
||||||
|
refineV(x).leftMap(err => NonEmptyList.one(ParseFailure(err, err)))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class QuerySeqParamDecoderMatcher[T: QueryParamDecoder](name: String) {
|
||||||
|
def unapply(params: Map[String, Seq[String]]): Option[List[T]] =
|
||||||
|
params
|
||||||
|
.get(name)
|
||||||
|
.flatMap(values =>
|
||||||
|
values.toList.traverse(s => QueryParamDecoder[T].decode(QueryParameterValue(s)).toOption))
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class OptionalQuerySeqParamDecoderMatcher[T: QueryParamDecoder](name: String) {
|
||||||
|
def unapply(params: Map[String, List[String]]): Option[Option[List[T]]] =
|
||||||
|
params
|
||||||
|
.get(name)
|
||||||
|
.flatMap(values =>
|
||||||
|
values.toList.traverse(s => QueryParamDecoder[T].decode(QueryParameterValue(s)).toOption))
|
||||||
|
.fold(List.empty[T].some.some)(_.some.some)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit lazy val BigDecimalQueryParamDecoder: QueryParamDecoder[BigDecimal] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[BigDecimal](x => BigDecimal(x.value))("BigDecimal")
|
||||||
|
|
||||||
|
implicit lazy val LocalDateTimeQueryParamDecoder: QueryParamDecoder[LocalDateTime] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[LocalDateTime](x => LocalDateTime.parse(x.value))("LocalDateTime")
|
||||||
|
|
||||||
|
implicit lazy val LocalDateQueryParamDecoder: QueryParamDecoder[LocalDate] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[LocalDate](x => LocalDate.parse(x.value))("LocalDateTime")
|
||||||
|
|
||||||
|
implicit lazy val ZonedDateTimeQueryParamDecoder: QueryParamDecoder[ZonedDateTime] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[ZonedDateTime](x => ZonedDateTime.parse(x.value))("ZonedDateTime")
|
||||||
|
|
||||||
|
implicit lazy val UUIDQueryParamDecoder: QueryParamDecoder[UUID] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[UUID](x => UUID.fromString(x.value))("UUID")
|
||||||
|
}
|
98
modules/openapi-generator/src/main/resources/scala-http4s-server/types.mustache
vendored
Normal file
98
modules/openapi-generator/src/main/resources/scala-http4s-server/types.mustache
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package {{modelPackage}}
|
||||||
|
|
||||||
|
import java.time._
|
||||||
|
|
||||||
|
import io.circe.refined._
|
||||||
|
import io.circe.syntax._
|
||||||
|
import io.circe.{ Decoder, Encoder }
|
||||||
|
import io.circe.generic.semiauto.{ deriveDecoder, deriveEncoder }
|
||||||
|
|
||||||
|
{{#imports}}
|
||||||
|
import {{.}}
|
||||||
|
{{/imports}}
|
||||||
|
|
||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
/**
|
||||||
|
* {{{description}}}
|
||||||
|
{{#vars}}
|
||||||
|
* @param {{name}} {{{description}}}
|
||||||
|
{{/vars}}
|
||||||
|
*/
|
||||||
|
{{#vendorExtensions.x-isSealedTrait}}
|
||||||
|
sealed trait {{classname}}
|
||||||
|
object {{classname}} {
|
||||||
|
import io.circe.{ Decoder, Encoder }
|
||||||
|
import io.circe.syntax._
|
||||||
|
import cats.syntax.functor._
|
||||||
|
|
||||||
|
{{^vendorExtensions.x-use-discr}}
|
||||||
|
// no discriminator
|
||||||
|
implicit val {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}Encoder: Encoder[{{classname}}] = Encoder.instance {
|
||||||
|
{{#oneOf}}
|
||||||
|
case {{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}: {{.}} => {{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}.asJson
|
||||||
|
{{/oneOf}}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}Decoder: Decoder[{{classname}}] =
|
||||||
|
List[Decoder[{{classname}}]](
|
||||||
|
{{#oneOf}}
|
||||||
|
Decoder[{{.}}].widen,
|
||||||
|
{{/oneOf}}
|
||||||
|
).reduceLeft(_ or _)
|
||||||
|
{{/vendorExtensions.x-use-discr}}
|
||||||
|
{{#vendorExtensions.x-use-discr}}
|
||||||
|
{{^vendorExtensions.x-use-discr-mapping}}
|
||||||
|
// no discriminator mapping
|
||||||
|
implicit val {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}Encoder: Encoder[{{classname}}] = Encoder.instance {
|
||||||
|
{{#oneOf}}
|
||||||
|
case {{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}: {{.}} => {{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}.asJson.mapObject(("{{discriminator.propertyName}}" -> "{{.}}".asJson) +: _)
|
||||||
|
{{/oneOf}}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}Decoder: Decoder[{{classname}}] = Decoder.instance { cursor =>
|
||||||
|
cursor.downField("{{discriminator.propertyName}}").as[String].flatMap {
|
||||||
|
{{#oneOf}}
|
||||||
|
case "{{.}}" =>
|
||||||
|
cursor.as[{{.}}]
|
||||||
|
{{/oneOf}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/vendorExtensions.x-use-discr-mapping}}
|
||||||
|
{{#vendorExtensions.x-use-discr-mapping}}
|
||||||
|
// use discriminator mapping
|
||||||
|
{{#discriminator}}
|
||||||
|
implicit val {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}Encoder: Encoder[{{classname}}] = Encoder.instance {
|
||||||
|
{{#mappedModels}}
|
||||||
|
case {{#lambda.camelcase}}{{model.classname}}{{/lambda.camelcase}}: {{model.classname}} => {{#lambda.camelcase}}{{model.classname}}{{/lambda.camelcase}}.asJson.mapObject(("{{propertyName}}" -> "{{mappingName}}".asJson) +: _)
|
||||||
|
{{/mappedModels}}
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}Decoder: Decoder[{{classname}}] = Decoder.instance { cursor =>
|
||||||
|
cursor.downField("{{propertyName}}").as[String].flatMap {
|
||||||
|
{{#mappedModels}}
|
||||||
|
case "{{mappingName}}" =>
|
||||||
|
cursor.as[{{model.classname}}]
|
||||||
|
{{/mappedModels}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/discriminator}}
|
||||||
|
{{/vendorExtensions.x-use-discr-mapping}}
|
||||||
|
{{/vendorExtensions.x-use-discr}}
|
||||||
|
}
|
||||||
|
{{/vendorExtensions.x-isSealedTrait}}
|
||||||
|
|
||||||
|
{{^vendorExtensions.x-isSealedTrait}}
|
||||||
|
case class {{classname}}(
|
||||||
|
{{#vars}}
|
||||||
|
{{name}}: {{^required}}Option[{{{vendorExtensions.x-type}}}]{{/required}}{{#required}}{{{vendorExtensions.x-type}}}{{/required}}{{^-last}},{{/-last}}
|
||||||
|
{{/vars}}
|
||||||
|
){{#vendorExtensions.x-extends}} extends {{.}}{{/vendorExtensions.x-extends}}{{#vendorExtensions.x-extendsWith}} with {{.}}{{/vendorExtensions.x-extendsWith}}
|
||||||
|
object {{classname}} {
|
||||||
|
implicit val encoder{{classname}}: Encoder[{{classname}}] = deriveEncoder[{{classname}}].mapJson(_.dropNullValues)
|
||||||
|
implicit val decoder{{classname}}: Decoder[{{classname}}] = deriveDecoder[{{classname}}]
|
||||||
|
}
|
||||||
|
{{/vendorExtensions.x-isSealedTrait}}
|
||||||
|
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
@ -0,0 +1,23 @@
|
|||||||
|
# OpenAPI Generator Ignore
|
||||||
|
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
|
||||||
|
|
||||||
|
# Use this file to prevent files from being overwritten by the generator.
|
||||||
|
# The patterns follow closely to .gitignore or .dockerignore.
|
||||||
|
|
||||||
|
# As an example, the C# client generator defines ApiClient.cs.
|
||||||
|
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
|
||||||
|
#ApiClient.cs
|
||||||
|
|
||||||
|
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||||
|
#foo/*/qux
|
||||||
|
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||||
|
#foo/**/qux
|
||||||
|
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can also negate patterns with an exclamation (!).
|
||||||
|
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||||
|
#docs/*.md
|
||||||
|
# Then explicitly reverse the ignore rule for a single file:
|
||||||
|
#!docs/README.md
|
@ -0,0 +1,9 @@
|
|||||||
|
build.sbt
|
||||||
|
project/build.properties
|
||||||
|
src/main/scala/org/openapitools/api.scala
|
||||||
|
src/main/scala/org/openapitools/apis/PetApi.scala
|
||||||
|
src/main/scala/org/openapitools/apis/StoreApi.scala
|
||||||
|
src/main/scala/org/openapitools/apis/UserApi.scala
|
||||||
|
src/main/scala/org/openapitools/apis/path.scala
|
||||||
|
src/main/scala/org/openapitools/apis/query.scala
|
||||||
|
src/main/scala/org/openapitools/models/types.scala
|
@ -0,0 +1 @@
|
|||||||
|
unset
|
27
samples/server/petstore/scala-http4s-server/build.sbt
Normal file
27
samples/server/petstore/scala-http4s-server/build.sbt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
scalaVersion := "2.13.11"
|
||||||
|
scalacOptions += "-Ymacro-annotations"
|
||||||
|
|
||||||
|
val circeVersion = "0.14.5"
|
||||||
|
def circe(artifact: String): ModuleID = "io.circe" %% s"circe-$artifact" % circeVersion
|
||||||
|
|
||||||
|
val http4sVersion = "0.23.23"
|
||||||
|
def http4s(artifact: String): ModuleID = "org.http4s" %% s"http4s-$artifact" % http4sVersion
|
||||||
|
|
||||||
|
val refinedVersion = "0.9.29"
|
||||||
|
val refined = Seq(
|
||||||
|
"eu.timepit" %% "refined" % refinedVersion,
|
||||||
|
"eu.timepit" %% "refined-cats" % refinedVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
val catsVersion = "2.10.0"
|
||||||
|
val cats = Seq("org.typelevel" %% "cats-core" % catsVersion)
|
||||||
|
|
||||||
|
lazy val compilerPlugins = Seq(
|
||||||
|
compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
|
||||||
|
compilerPlugin("org.typelevel" %% "kind-projector" % "0.13.2" cross CrossVersion.full)
|
||||||
|
)
|
||||||
|
|
||||||
|
libraryDependencies ++= (Seq(
|
||||||
|
http4s("core"), http4s("ember-server"), http4s("circe"), http4s("dsl"),
|
||||||
|
circe("core"), circe("generic"), circe("parser"), circe("refined")
|
||||||
|
) ++ refined ++ cats ++ compilerPlugins)
|
34
samples/server/petstore/scala-http4s-server/pom.xml
Normal file
34
samples/server/petstore/scala-http4s-server/pom.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<project>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.openapitools</groupId>
|
||||||
|
<artifactId>scala-http4s-server</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<name>Scala http4s server</name>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>1.5.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>sbt-test</id>
|
||||||
|
<phase>integration-test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>exec</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<executable>sbt</executable>
|
||||||
|
<arguments>
|
||||||
|
<argument>-ivy</argument>
|
||||||
|
<argument>${user.home}/.ivy2</argument>
|
||||||
|
<argument>test</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1 @@
|
|||||||
|
sbt.version=1.8.2
|
@ -0,0 +1,47 @@
|
|||||||
|
package org.openapitools
|
||||||
|
|
||||||
|
import org.http4s.circe._
|
||||||
|
import cats.Monad
|
||||||
|
import cats.syntax.all._
|
||||||
|
import cats.data.OptionT
|
||||||
|
import cats.data.Kleisli
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.server._
|
||||||
|
|
||||||
|
import org.openapitools.apis._
|
||||||
|
|
||||||
|
final case class API [
|
||||||
|
F[_]: JsonDecoder: Monad, petstore_auth, api_key
|
||||||
|
](
|
||||||
|
petstoreAuth: Kleisli[OptionT[F, *], Request[F], petstore_auth],
|
||||||
|
apiKey: Kleisli[OptionT[F, *], Request[F], api_key],
|
||||||
|
)(
|
||||||
|
delegatePetApi: PetApiDelegate[F, petstore_auth, api_key],
|
||||||
|
delegateStoreApi: StoreApiDelegate[F, api_key],
|
||||||
|
delegateUserApi: UserApiDelegate[F, api_key],
|
||||||
|
){
|
||||||
|
val apiKeyMiddleware = AuthMiddleware.withFallThrough(apiKey)
|
||||||
|
val petstoreAuthMiddleware = AuthMiddleware(petstoreAuth)
|
||||||
|
|
||||||
|
val petApiRoutes = new PetApiRoutes(delegatePetApi)
|
||||||
|
val storeApiRoutes = new StoreApiRoutes(delegateStoreApi)
|
||||||
|
val userApiRoutes = new UserApiRoutes(delegateUserApi)
|
||||||
|
|
||||||
|
val routes =
|
||||||
|
storeApiRoutes.routes <+>
|
||||||
|
userApiRoutes.routes
|
||||||
|
|
||||||
|
val routesapi_key = apiKeyMiddleware(
|
||||||
|
petApiRoutes.routesapi_key <+>
|
||||||
|
storeApiRoutes.routesapi_key <+>
|
||||||
|
userApiRoutes.routesapi_key
|
||||||
|
)
|
||||||
|
val routespetstore_auth = petstoreAuthMiddleware(
|
||||||
|
petApiRoutes.routespetstore_auth
|
||||||
|
)
|
||||||
|
|
||||||
|
val routesAll =
|
||||||
|
routes <+>
|
||||||
|
routesapi_key <+>
|
||||||
|
routespetstore_auth
|
||||||
|
}
|
@ -0,0 +1,361 @@
|
|||||||
|
package org.openapitools.apis
|
||||||
|
|
||||||
|
import org.openapitools.apis.path._
|
||||||
|
import org.openapitools.apis.query._
|
||||||
|
|
||||||
|
import org.openapitools.models.ApiResponse
|
||||||
|
import java.io.File
|
||||||
|
import org.openapitools.models.Pet
|
||||||
|
|
||||||
|
|
||||||
|
import cats.Monad
|
||||||
|
import cats.syntax.all._
|
||||||
|
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.circe._
|
||||||
|
import org.http4s.server._
|
||||||
|
import org.http4s.headers._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
|
||||||
|
final case class PetApiRoutes[
|
||||||
|
F[_]: JsonDecoder: Monad, petstore_auth, api_key
|
||||||
|
](delegate: PetApiDelegate[F, petstore_auth, api_key]) extends Http4sDsl[F] {
|
||||||
|
object addPet {
|
||||||
|
import PetApiDelegate.addPetResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routepetstore_auth = AuthedRoutes.of[petstore_auth, F] {
|
||||||
|
case (req @ POST -> Root / "pet") as auth =>
|
||||||
|
req.contentType match {
|
||||||
|
case Some(`Content-Type`(MediaType.application.json, _)) =>
|
||||||
|
delegate.addPet.handle_petstore_auth(auth, req, req.asJsonDecode[Pet] , responses)
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
delegate.addPet.handle_petstore_auth(auth, req, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: addPetResponses[F] = new addPetResponses[F] {
|
||||||
|
def resp200(value: Pet): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp405(allow: Allow): F[Response[F]] = MethodNotAllowed(allow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object deletePet {
|
||||||
|
import PetApiDelegate.deletePetResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routepetstore_auth = AuthedRoutes.of[petstore_auth, F] {
|
||||||
|
case (req @ DELETE -> Root / "pet" / LongVarr(petId)) as auth =>
|
||||||
|
delegate.deletePet.handle_petstore_auth(auth, req, petId, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: deletePetResponses[F] = new deletePetResponses[F] {
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object findPetsByStatus {
|
||||||
|
import PetApiDelegate.findPetsByStatusResponses
|
||||||
|
|
||||||
|
object statusQueryParam extends QuerySeqParamDecoderMatcher[String]("status")
|
||||||
|
|
||||||
|
val routepetstore_auth = AuthedRoutes.of[petstore_auth, F] {
|
||||||
|
case (req @ GET -> Root / "pet" / "findByStatus" :? statusQueryParam(status)) as auth =>
|
||||||
|
delegate.findPetsByStatus.handle_petstore_auth(auth, req, status, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: findPetsByStatusResponses[F] = new findPetsByStatusResponses[F] {
|
||||||
|
def resp200(value: List[Pet]): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object findPetsByTags {
|
||||||
|
import PetApiDelegate.findPetsByTagsResponses
|
||||||
|
|
||||||
|
object tagsQueryParam extends QuerySeqParamDecoderMatcher[String]("tags")
|
||||||
|
|
||||||
|
val routepetstore_auth = AuthedRoutes.of[petstore_auth, F] {
|
||||||
|
case (req @ GET -> Root / "pet" / "findByTags" :? tagsQueryParam(tags)) as auth =>
|
||||||
|
delegate.findPetsByTags.handle_petstore_auth(auth, req, tags, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: findPetsByTagsResponses[F] = new findPetsByTagsResponses[F] {
|
||||||
|
def resp200(value: List[Pet]): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object getPetById {
|
||||||
|
import PetApiDelegate.getPetByIdResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ GET -> Root / "pet" / LongVarr(petId)) as auth =>
|
||||||
|
delegate.getPetById.handle_api_key(auth, req, petId, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: getPetByIdResponses[F] = new getPetByIdResponses[F] {
|
||||||
|
def resp200(value: Pet): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
def resp404(): F[Response[F]] = NotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object updatePet {
|
||||||
|
import PetApiDelegate.updatePetResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routepetstore_auth = AuthedRoutes.of[petstore_auth, F] {
|
||||||
|
case (req @ PUT -> Root / "pet") as auth =>
|
||||||
|
req.contentType match {
|
||||||
|
case Some(`Content-Type`(MediaType.application.json, _)) =>
|
||||||
|
delegate.updatePet.handle_petstore_auth(auth, req, req.asJsonDecode[Pet] , responses)
|
||||||
|
|
||||||
|
case _ =>
|
||||||
|
delegate.updatePet.handle_petstore_auth(auth, req, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: updatePetResponses[F] = new updatePetResponses[F] {
|
||||||
|
def resp200(value: Pet): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
def resp404(): F[Response[F]] = NotFound()
|
||||||
|
def resp405(allow: Allow): F[Response[F]] = MethodNotAllowed(allow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object updatePetWithForm {
|
||||||
|
import PetApiDelegate.updatePetWithFormResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routepetstore_auth = AuthedRoutes.of[petstore_auth, F] {
|
||||||
|
case (req @ POST -> Root / "pet" / LongVarr(petId)) as auth =>
|
||||||
|
delegate.updatePetWithForm.handle_petstore_auth(auth, req, petId, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: updatePetWithFormResponses[F] = new updatePetWithFormResponses[F] {
|
||||||
|
def resp405(allow: Allow): F[Response[F]] = MethodNotAllowed(allow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object uploadFile {
|
||||||
|
import PetApiDelegate.uploadFileResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routepetstore_auth = AuthedRoutes.of[petstore_auth, F] {
|
||||||
|
case (req @ POST -> Root / "pet" / LongVarr(petId) / "uploadImage") as auth =>
|
||||||
|
delegate.uploadFile.handle_petstore_auth(auth, req, petId, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: uploadFileResponses[F] = new uploadFileResponses[F] {
|
||||||
|
def resp200(value: ApiResponse): F[Response[F]] = Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val routespetstore_auth =
|
||||||
|
addPet.routepetstore_auth <+>
|
||||||
|
deletePet.routepetstore_auth <+>
|
||||||
|
findPetsByStatus.routepetstore_auth <+>
|
||||||
|
findPetsByTags.routepetstore_auth <+>
|
||||||
|
updatePet.routepetstore_auth <+>
|
||||||
|
updatePetWithForm.routepetstore_auth <+>
|
||||||
|
uploadFile.routepetstore_auth
|
||||||
|
val routesapi_key =
|
||||||
|
getPetById.routeapi_key
|
||||||
|
}
|
||||||
|
|
||||||
|
object PetApiDelegate {
|
||||||
|
trait addPetResponses[F[_]] {
|
||||||
|
def resp200(value: Pet): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp405(allow: Allow): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait deletePetResponses[F[_]] {
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait findPetsByStatusResponses[F[_]] {
|
||||||
|
def resp200(value: List[Pet]): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait findPetsByTagsResponses[F[_]] {
|
||||||
|
def resp200(value: List[Pet]): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait getPetByIdResponses[F[_]] {
|
||||||
|
def resp200(value: Pet): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
def resp404(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait updatePetResponses[F[_]] {
|
||||||
|
def resp200(value: Pet): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
def resp404(): F[Response[F]]
|
||||||
|
def resp405(allow: Allow): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait updatePetWithFormResponses[F[_]] {
|
||||||
|
def resp405(allow: Allow): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait uploadFileResponses[F[_]] {
|
||||||
|
def resp200(value: ApiResponse): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trait PetApiDelegate[F[_], petstore_auth, api_key] {
|
||||||
|
|
||||||
|
trait addPet {
|
||||||
|
import PetApiDelegate.addPetResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
addPet: F[Pet],
|
||||||
|
responses: addPetResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
responses: addPetResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def addPet: addPet
|
||||||
|
|
||||||
|
|
||||||
|
trait deletePet {
|
||||||
|
import PetApiDelegate.deletePetResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
petId: Long,
|
||||||
|
responses: deletePetResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def deletePet: deletePet
|
||||||
|
|
||||||
|
|
||||||
|
trait findPetsByStatus {
|
||||||
|
import PetApiDelegate.findPetsByStatusResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
status: List[String],
|
||||||
|
responses: findPetsByStatusResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def findPetsByStatus: findPetsByStatus
|
||||||
|
|
||||||
|
|
||||||
|
trait findPetsByTags {
|
||||||
|
import PetApiDelegate.findPetsByTagsResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
tags: List[String],
|
||||||
|
responses: findPetsByTagsResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def findPetsByTags: findPetsByTags
|
||||||
|
|
||||||
|
|
||||||
|
trait getPetById {
|
||||||
|
import PetApiDelegate.getPetByIdResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
petId: Long,
|
||||||
|
responses: getPetByIdResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def getPetById: getPetById
|
||||||
|
|
||||||
|
|
||||||
|
trait updatePet {
|
||||||
|
import PetApiDelegate.updatePetResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
updatePet: F[Pet],
|
||||||
|
responses: updatePetResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
responses: updatePetResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def updatePet: updatePet
|
||||||
|
|
||||||
|
|
||||||
|
trait updatePetWithForm {
|
||||||
|
import PetApiDelegate.updatePetWithFormResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
petId: Long,
|
||||||
|
responses: updatePetWithFormResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def updatePetWithForm: updatePetWithForm
|
||||||
|
|
||||||
|
|
||||||
|
trait uploadFile {
|
||||||
|
import PetApiDelegate.uploadFileResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_petstore_auth(
|
||||||
|
auth: petstore_auth,
|
||||||
|
req: Request[F],
|
||||||
|
petId: Long,
|
||||||
|
responses: uploadFileResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def uploadFile: uploadFile
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,181 @@
|
|||||||
|
package org.openapitools.apis
|
||||||
|
|
||||||
|
import org.openapitools.apis.path._
|
||||||
|
import org.openapitools.apis.query._
|
||||||
|
|
||||||
|
import org.openapitools.models.Order
|
||||||
|
|
||||||
|
import eu.timepit.refined.api.Refined
|
||||||
|
import eu.timepit.refined.boolean.And
|
||||||
|
import eu.timepit.refined.numeric.GreaterEqual
|
||||||
|
import eu.timepit.refined.numeric.LessEqual
|
||||||
|
|
||||||
|
import cats.Monad
|
||||||
|
import cats.syntax.all._
|
||||||
|
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.circe._
|
||||||
|
import org.http4s.server._
|
||||||
|
import org.http4s.headers._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
|
||||||
|
final case class StoreApiRoutes[
|
||||||
|
F[_]: JsonDecoder: Monad, api_key
|
||||||
|
](delegate: StoreApiDelegate[F, api_key]) extends Http4sDsl[F] {
|
||||||
|
object deleteOrder {
|
||||||
|
import StoreApiDelegate.deleteOrderResponses
|
||||||
|
|
||||||
|
|
||||||
|
val route = HttpRoutes.of[F] {
|
||||||
|
case req @ DELETE -> Root / "store" / "order" / orderId =>
|
||||||
|
delegate.deleteOrder.handle(req, orderId, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val responses: deleteOrderResponses[F] = new deleteOrderResponses[F] {
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
def resp404(): F[Response[F]] = NotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object getInventory {
|
||||||
|
import StoreApiDelegate.getInventoryResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ GET -> Root / "store" / "inventory") as auth =>
|
||||||
|
delegate.getInventory.handle_api_key(auth, req, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: getInventoryResponses[F] = new getInventoryResponses[F] {
|
||||||
|
def resp200(value: Map[String, Int]): F[Response[F]] = Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object getOrderById {
|
||||||
|
import StoreApiDelegate.getOrderByIdResponses
|
||||||
|
|
||||||
|
object orderIdVarr extends RefinedVarr[Long, GreaterEqual[1] And LessEqual[5]]
|
||||||
|
|
||||||
|
val route = HttpRoutes.of[F] {
|
||||||
|
case req @ GET -> Root / "store" / "order" / orderIdVarr(orderId) =>
|
||||||
|
delegate.getOrderById.handle(req, orderId, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val responses: getOrderByIdResponses[F] = new getOrderByIdResponses[F] {
|
||||||
|
def resp200(value: Order): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
def resp404(): F[Response[F]] = NotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object placeOrder {
|
||||||
|
import StoreApiDelegate.placeOrderResponses
|
||||||
|
|
||||||
|
|
||||||
|
val route = HttpRoutes.of[F] {
|
||||||
|
case req @ POST -> Root / "store" / "order" =>
|
||||||
|
delegate.placeOrder.handle(req, req.asJsonDecode[Order] , responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val responses: placeOrderResponses[F] = new placeOrderResponses[F] {
|
||||||
|
def resp200(value: Order): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val routes =
|
||||||
|
deleteOrder.route <+>
|
||||||
|
getOrderById.route <+>
|
||||||
|
placeOrder.route
|
||||||
|
val routesapi_key =
|
||||||
|
getInventory.routeapi_key
|
||||||
|
}
|
||||||
|
|
||||||
|
object StoreApiDelegate {
|
||||||
|
trait deleteOrderResponses[F[_]] {
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
def resp404(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait getInventoryResponses[F[_]] {
|
||||||
|
def resp200(value: Map[String, Int]): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait getOrderByIdResponses[F[_]] {
|
||||||
|
def resp200(value: Order): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
def resp404(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait placeOrderResponses[F[_]] {
|
||||||
|
def resp200(value: Order): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trait StoreApiDelegate[F[_], api_key] {
|
||||||
|
|
||||||
|
trait deleteOrder {
|
||||||
|
import StoreApiDelegate.deleteOrderResponses
|
||||||
|
|
||||||
|
def handle(
|
||||||
|
req: Request[F],
|
||||||
|
orderId: String,
|
||||||
|
responses: deleteOrderResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def deleteOrder: deleteOrder
|
||||||
|
|
||||||
|
|
||||||
|
trait getInventory {
|
||||||
|
import StoreApiDelegate.getInventoryResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
responses: getInventoryResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def getInventory: getInventory
|
||||||
|
|
||||||
|
|
||||||
|
trait getOrderById {
|
||||||
|
import StoreApiDelegate.getOrderByIdResponses
|
||||||
|
|
||||||
|
def handle(
|
||||||
|
req: Request[F],
|
||||||
|
orderId: Refined[Long, GreaterEqual[1] And LessEqual[5]],
|
||||||
|
responses: getOrderByIdResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def getOrderById: getOrderById
|
||||||
|
|
||||||
|
|
||||||
|
trait placeOrder {
|
||||||
|
import StoreApiDelegate.placeOrderResponses
|
||||||
|
|
||||||
|
def handle(
|
||||||
|
req: Request[F],
|
||||||
|
placeOrder: F[Order],
|
||||||
|
responses: placeOrderResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
def placeOrder: placeOrder
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,326 @@
|
|||||||
|
package org.openapitools.apis
|
||||||
|
|
||||||
|
import org.openapitools.apis.path._
|
||||||
|
import org.openapitools.apis.query._
|
||||||
|
|
||||||
|
import org.openapitools.models.User
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
import eu.timepit.refined.api.Refined
|
||||||
|
import eu.timepit.refined.boolean.And
|
||||||
|
import eu.timepit.refined.string.MatchesRegex
|
||||||
|
|
||||||
|
import cats.Monad
|
||||||
|
import cats.syntax.all._
|
||||||
|
|
||||||
|
import org.http4s._
|
||||||
|
import org.http4s.circe._
|
||||||
|
import org.http4s.server._
|
||||||
|
import org.http4s.headers._
|
||||||
|
import org.http4s.dsl.Http4sDsl
|
||||||
|
import org.http4s.circe.CirceEntityEncoder._
|
||||||
|
|
||||||
|
final case class UserApiRoutes[
|
||||||
|
F[_]: JsonDecoder: Monad, api_key
|
||||||
|
](delegate: UserApiDelegate[F, api_key]) extends Http4sDsl[F] {
|
||||||
|
object createUser {
|
||||||
|
import UserApiDelegate.createUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ POST -> Root / "user") as auth =>
|
||||||
|
delegate.createUser.handle_api_key(auth, req, req.asJsonDecode[User] , responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: createUserResponses[F] = new createUserResponses[F] {
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object createUsersWithArrayInput {
|
||||||
|
import UserApiDelegate.createUsersWithArrayInputResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ POST -> Root / "user" / "createWithArray") as auth =>
|
||||||
|
delegate.createUsersWithArrayInput.handle_api_key(auth, req, req.asJsonDecode[List[User]] , responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: createUsersWithArrayInputResponses[F] = new createUsersWithArrayInputResponses[F] {
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object createUsersWithListInput {
|
||||||
|
import UserApiDelegate.createUsersWithListInputResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ POST -> Root / "user" / "createWithList") as auth =>
|
||||||
|
delegate.createUsersWithListInput.handle_api_key(auth, req, req.asJsonDecode[List[User]] , responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: createUsersWithListInputResponses[F] = new createUsersWithListInputResponses[F] {
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object deleteUser {
|
||||||
|
import UserApiDelegate.deleteUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ DELETE -> Root / "user" / username) as auth =>
|
||||||
|
delegate.deleteUser.handle_api_key(auth, req, username, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: deleteUserResponses[F] = new deleteUserResponses[F] {
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
def resp404(): F[Response[F]] = NotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object getUserByName {
|
||||||
|
import UserApiDelegate.getUserByNameResponses
|
||||||
|
|
||||||
|
|
||||||
|
val route = HttpRoutes.of[F] {
|
||||||
|
case req @ GET -> Root / "user" / username =>
|
||||||
|
delegate.getUserByName.handle(req, username, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val responses: getUserByNameResponses[F] = new getUserByNameResponses[F] {
|
||||||
|
def resp200(value: User): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
def resp404(): F[Response[F]] = NotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object loginUser {
|
||||||
|
import UserApiDelegate.loginUserResponses
|
||||||
|
|
||||||
|
object usernameQueryParam extends QueryParamDecoderMatcher[Refined[String, MatchesRegex["^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$"]]]("username")
|
||||||
|
object passwordQueryParam extends QueryParamDecoderMatcher[String]("password")
|
||||||
|
|
||||||
|
val route = HttpRoutes.of[F] {
|
||||||
|
case req @ GET -> Root / "user" / "login" :? usernameQueryParam(username) +& passwordQueryParam(password) =>
|
||||||
|
delegate.loginUser.handle(req, username, password, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val responses: loginUserResponses[F] = new loginUserResponses[F] {
|
||||||
|
def resp200(value: String): F[Response[F]] = Ok(value)
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object logoutUser {
|
||||||
|
import UserApiDelegate.logoutUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ GET -> Root / "user" / "logout") as auth =>
|
||||||
|
delegate.logoutUser.handle_api_key(auth, req, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: logoutUserResponses[F] = new logoutUserResponses[F] {
|
||||||
|
def resp200(): F[Response[F]] = Ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object updateUser {
|
||||||
|
import UserApiDelegate.updateUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
val routeapi_key = AuthedRoutes.of[api_key, F] {
|
||||||
|
case (req @ PUT -> Root / "user" / username) as auth =>
|
||||||
|
delegate.updateUser.handle_api_key(auth, req, req.asJsonDecode[User] , username, responses)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val responses: updateUserResponses[F] = new updateUserResponses[F] {
|
||||||
|
def resp400(): F[Response[F]] = BadRequest()
|
||||||
|
def resp404(): F[Response[F]] = NotFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val routes =
|
||||||
|
getUserByName.route <+>
|
||||||
|
loginUser.route
|
||||||
|
val routesapi_key =
|
||||||
|
createUser.routeapi_key <+>
|
||||||
|
createUsersWithArrayInput.routeapi_key <+>
|
||||||
|
createUsersWithListInput.routeapi_key <+>
|
||||||
|
deleteUser.routeapi_key <+>
|
||||||
|
logoutUser.routeapi_key <+>
|
||||||
|
updateUser.routeapi_key
|
||||||
|
}
|
||||||
|
|
||||||
|
object UserApiDelegate {
|
||||||
|
trait createUserResponses[F[_]] {
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait createUsersWithArrayInputResponses[F[_]] {
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait createUsersWithListInputResponses[F[_]] {
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait deleteUserResponses[F[_]] {
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
def resp404(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait getUserByNameResponses[F[_]] {
|
||||||
|
def resp200(value: User): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
def resp404(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait loginUserResponses[F[_]] {
|
||||||
|
def resp200(value: String): F[Response[F]]
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait logoutUserResponses[F[_]] {
|
||||||
|
def resp200(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trait updateUserResponses[F[_]] {
|
||||||
|
def resp400(): F[Response[F]]
|
||||||
|
def resp404(): F[Response[F]]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
trait UserApiDelegate[F[_], api_key] {
|
||||||
|
|
||||||
|
trait createUser {
|
||||||
|
import UserApiDelegate.createUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
createUser: F[User],
|
||||||
|
responses: createUserResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
def createUser: createUser
|
||||||
|
|
||||||
|
|
||||||
|
trait createUsersWithArrayInput {
|
||||||
|
import UserApiDelegate.createUsersWithArrayInputResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
createUsersWithArrayInput: F[List[User]],
|
||||||
|
responses: createUsersWithArrayInputResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
def createUsersWithArrayInput: createUsersWithArrayInput
|
||||||
|
|
||||||
|
|
||||||
|
trait createUsersWithListInput {
|
||||||
|
import UserApiDelegate.createUsersWithListInputResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
createUsersWithListInput: F[List[User]],
|
||||||
|
responses: createUsersWithListInputResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
def createUsersWithListInput: createUsersWithListInput
|
||||||
|
|
||||||
|
|
||||||
|
trait deleteUser {
|
||||||
|
import UserApiDelegate.deleteUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
username: String,
|
||||||
|
responses: deleteUserResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def deleteUser: deleteUser
|
||||||
|
|
||||||
|
|
||||||
|
trait getUserByName {
|
||||||
|
import UserApiDelegate.getUserByNameResponses
|
||||||
|
|
||||||
|
def handle(
|
||||||
|
req: Request[F],
|
||||||
|
username: String,
|
||||||
|
responses: getUserByNameResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def getUserByName: getUserByName
|
||||||
|
|
||||||
|
|
||||||
|
trait loginUser {
|
||||||
|
import UserApiDelegate.loginUserResponses
|
||||||
|
|
||||||
|
def handle(
|
||||||
|
req: Request[F],
|
||||||
|
username: Refined[String, MatchesRegex["^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$"]],
|
||||||
|
password: String,
|
||||||
|
responses: loginUserResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def loginUser: loginUser
|
||||||
|
|
||||||
|
|
||||||
|
trait logoutUser {
|
||||||
|
import UserApiDelegate.logoutUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
responses: logoutUserResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
}
|
||||||
|
def logoutUser: logoutUser
|
||||||
|
|
||||||
|
|
||||||
|
trait updateUser {
|
||||||
|
import UserApiDelegate.updateUserResponses
|
||||||
|
|
||||||
|
|
||||||
|
def handle_api_key(
|
||||||
|
auth: api_key,
|
||||||
|
req: Request[F],
|
||||||
|
updateUser: F[User],
|
||||||
|
username: String,
|
||||||
|
responses: updateUserResponses[F]
|
||||||
|
): F[Response[F]]
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
def updateUser: updateUser
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package org.openapitools.apis
|
||||||
|
|
||||||
|
import cats.syntax.all._
|
||||||
|
import cats.data.ValidatedNel
|
||||||
|
|
||||||
|
import eu.timepit.refined._
|
||||||
|
import eu.timepit.refined.api.Refined
|
||||||
|
import eu.timepit.refined.api.Validate
|
||||||
|
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
|
object path {
|
||||||
|
trait Varrr[T] {
|
||||||
|
def unapply(str: String): Option[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val LocalDateVarr: Varrr[LocalDate] = new Varrr[LocalDate] {
|
||||||
|
def unapply(str: String): Option[LocalDate] = Try(LocalDate.parse(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val LocalDateTimeVarr: Varrr[LocalDateTime] = new Varrr[LocalDateTime] {
|
||||||
|
def unapply(str: String): Option[LocalDateTime] = Try(LocalDateTime.parse(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val ZonedDateTimeVarr: Varrr[ZonedDateTime] = new Varrr[ZonedDateTime] {
|
||||||
|
def unapply(str: String): Option[ZonedDateTime] = Try(ZonedDateTime.parse(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val UUIDVarr: Varrr[UUID] = new Varrr[UUID] {
|
||||||
|
def unapply(str: String): Option[UUID] = Try(java.util.UUID.fromString(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val IntVarr: Varrr[Int] = new Varrr[Int] {
|
||||||
|
def unapply(str: String): Option[Int] = Try(str.toInt).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val LongVarr: Varrr[Long] = new Varrr[Long] {
|
||||||
|
def unapply(str: String): Option[Long] = Try(str.toLong).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val DoubleVarr: Varrr[Double] = new Varrr[Double] {
|
||||||
|
def unapply(str: String): Option[Double] = Try(str.toDouble).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val BigDecimalVarr: Varrr[BigDecimal] = new Varrr[BigDecimal] {
|
||||||
|
def unapply(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit val StringVarr: Varrr[String] = new Varrr[String] {
|
||||||
|
def unapply(str: String): Option[String] = str.some
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class RefinedVarr[T, P](implicit varrr: Varrr[T], validate: Validate[T, P]) extends Varrr[Refined[T, P]] {
|
||||||
|
def unapply(str: String): Option[Refined[T, P]] =
|
||||||
|
varrr.unapply(str).flatMap(x => refineV(x).toOption)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package org.openapitools.apis
|
||||||
|
|
||||||
|
import cats.data.NonEmptyList
|
||||||
|
import cats.data.ValidatedNel
|
||||||
|
import cats.syntax.all._
|
||||||
|
|
||||||
|
import eu.timepit.refined._
|
||||||
|
import eu.timepit.refined.api.Refined
|
||||||
|
import eu.timepit.refined.api.Validate
|
||||||
|
|
||||||
|
import org.http4s.ParseFailure
|
||||||
|
import org.http4s.QueryParamDecoder
|
||||||
|
import org.http4s.QueryParameterValue
|
||||||
|
|
||||||
|
import java.time._
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object query {
|
||||||
|
implicit def rrrefinedQueryParamDecoder[T, P](
|
||||||
|
implicit tDecoder: QueryParamDecoder[T], validate: Validate[T, P]
|
||||||
|
): QueryParamDecoder[Refined[T, P]] = new QueryParamDecoder[Refined[T, P]] {
|
||||||
|
def decode(value: QueryParameterValue): ValidatedNel[ParseFailure,Refined[T, P]] =
|
||||||
|
tDecoder.decode(value).withEither(t => t.flatMap(x =>
|
||||||
|
refineV(x).leftMap(err => NonEmptyList.one(ParseFailure(err, err)))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class QuerySeqParamDecoderMatcher[T: QueryParamDecoder](name: String) {
|
||||||
|
def unapply(params: Map[String, Seq[String]]): Option[List[T]] =
|
||||||
|
params
|
||||||
|
.get(name)
|
||||||
|
.flatMap(values =>
|
||||||
|
values.toList.traverse(s => QueryParamDecoder[T].decode(QueryParameterValue(s)).toOption))
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class OptionalQuerySeqParamDecoderMatcher[T: QueryParamDecoder](name: String) {
|
||||||
|
def unapply(params: Map[String, List[String]]): Option[Option[List[T]]] =
|
||||||
|
params
|
||||||
|
.get(name)
|
||||||
|
.flatMap(values =>
|
||||||
|
values.toList.traverse(s => QueryParamDecoder[T].decode(QueryParameterValue(s)).toOption))
|
||||||
|
.fold(List.empty[T].some.some)(_.some.some)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicit lazy val BigDecimalQueryParamDecoder: QueryParamDecoder[BigDecimal] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[BigDecimal](x => BigDecimal(x.value))("BigDecimal")
|
||||||
|
|
||||||
|
implicit lazy val LocalDateTimeQueryParamDecoder: QueryParamDecoder[LocalDateTime] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[LocalDateTime](x => LocalDateTime.parse(x.value))("LocalDateTime")
|
||||||
|
|
||||||
|
implicit lazy val LocalDateQueryParamDecoder: QueryParamDecoder[LocalDate] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[LocalDate](x => LocalDate.parse(x.value))("LocalDateTime")
|
||||||
|
|
||||||
|
implicit lazy val ZonedDateTimeQueryParamDecoder: QueryParamDecoder[ZonedDateTime] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[ZonedDateTime](x => ZonedDateTime.parse(x.value))("ZonedDateTime")
|
||||||
|
|
||||||
|
implicit lazy val UUIDQueryParamDecoder: QueryParamDecoder[UUID] =
|
||||||
|
QueryParamDecoder.fromUnsafeCast[UUID](x => UUID.fromString(x.value))("UUID")
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
package org.openapitools.models
|
||||||
|
|
||||||
|
import java.time._
|
||||||
|
|
||||||
|
import io.circe.refined._
|
||||||
|
import io.circe.syntax._
|
||||||
|
import io.circe.{ Decoder, Encoder }
|
||||||
|
import io.circe.generic.semiauto.{ deriveDecoder, deriveEncoder }
|
||||||
|
|
||||||
|
import eu.timepit.refined.api.Refined
|
||||||
|
import eu.timepit.refined.boolean.And
|
||||||
|
import eu.timepit.refined.string.MatchesRegex
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the result of uploading an image resource
|
||||||
|
* @param code
|
||||||
|
* @param _type
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
|
||||||
|
case class ApiResponse(
|
||||||
|
code: Option[Int],
|
||||||
|
_type: Option[String],
|
||||||
|
message: Option[String]
|
||||||
|
)
|
||||||
|
object ApiResponse {
|
||||||
|
implicit val encoderApiResponse: Encoder[ApiResponse] = deriveEncoder[ApiResponse].mapJson(_.dropNullValues)
|
||||||
|
implicit val decoderApiResponse: Decoder[ApiResponse] = deriveDecoder[ApiResponse]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A category for a pet
|
||||||
|
* @param id
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
|
||||||
|
case class Category(
|
||||||
|
id: Option[Long],
|
||||||
|
name: Option[Refined[String, MatchesRegex["^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$"]]]
|
||||||
|
)
|
||||||
|
object Category {
|
||||||
|
implicit val encoderCategory: Encoder[Category] = deriveEncoder[Category].mapJson(_.dropNullValues)
|
||||||
|
implicit val decoderCategory: Decoder[Category] = deriveDecoder[Category]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An order for a pets from the pet store
|
||||||
|
* @param id
|
||||||
|
* @param petId
|
||||||
|
* @param quantity
|
||||||
|
* @param shipDate
|
||||||
|
* @param status Order Status
|
||||||
|
* @param complete
|
||||||
|
*/
|
||||||
|
|
||||||
|
case class Order(
|
||||||
|
id: Option[Long],
|
||||||
|
petId: Option[Long],
|
||||||
|
quantity: Option[Int],
|
||||||
|
shipDate: Option[ZonedDateTime],
|
||||||
|
status: Option[String],
|
||||||
|
complete: Option[Boolean]
|
||||||
|
)
|
||||||
|
object Order {
|
||||||
|
implicit val encoderOrder: Encoder[Order] = deriveEncoder[Order].mapJson(_.dropNullValues)
|
||||||
|
implicit val decoderOrder: Decoder[Order] = deriveDecoder[Order]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pet for sale in the pet store
|
||||||
|
* @param id
|
||||||
|
* @param category
|
||||||
|
* @param name
|
||||||
|
* @param photoUrls
|
||||||
|
* @param tags
|
||||||
|
* @param status pet status in the store
|
||||||
|
*/
|
||||||
|
|
||||||
|
case class Pet(
|
||||||
|
id: Option[Long],
|
||||||
|
category: Option[Category],
|
||||||
|
name: String,
|
||||||
|
photoUrls: List[String],
|
||||||
|
tags: Option[List[Tag]],
|
||||||
|
status: Option[String]
|
||||||
|
)
|
||||||
|
object Pet {
|
||||||
|
implicit val encoderPet: Encoder[Pet] = deriveEncoder[Pet].mapJson(_.dropNullValues)
|
||||||
|
implicit val decoderPet: Decoder[Pet] = deriveDecoder[Pet]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tag for a pet
|
||||||
|
* @param id
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
|
||||||
|
case class Tag(
|
||||||
|
id: Option[Long],
|
||||||
|
name: Option[String]
|
||||||
|
)
|
||||||
|
object Tag {
|
||||||
|
implicit val encoderTag: Encoder[Tag] = deriveEncoder[Tag].mapJson(_.dropNullValues)
|
||||||
|
implicit val decoderTag: Decoder[Tag] = deriveDecoder[Tag]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A User who is purchasing from the pet store
|
||||||
|
* @param id
|
||||||
|
* @param username
|
||||||
|
* @param firstName
|
||||||
|
* @param lastName
|
||||||
|
* @param email
|
||||||
|
* @param password
|
||||||
|
* @param phone
|
||||||
|
* @param userStatus User Status
|
||||||
|
*/
|
||||||
|
|
||||||
|
case class User(
|
||||||
|
id: Option[Long],
|
||||||
|
username: Option[String],
|
||||||
|
firstName: Option[String],
|
||||||
|
lastName: Option[String],
|
||||||
|
email: Option[String],
|
||||||
|
password: Option[String],
|
||||||
|
phone: Option[String],
|
||||||
|
userStatus: Option[Int]
|
||||||
|
)
|
||||||
|
object User {
|
||||||
|
implicit val encoderUser: Encoder[User] = deriveEncoder[User].mapJson(_.dropNullValues)
|
||||||
|
implicit val decoderUser: Decoder[User] = deriveDecoder[User]
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user