Improve sttpOpenApiClient generator (#6684)

Co-authored-by: eugeniyk <keatrance@gmail.com>
This commit is contained in:
Kasper Kondzielski 2020-07-01 03:14:34 +02:00 committed by GitHub
parent dae329d8e1
commit 323cd38b5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 901 additions and 502 deletions

View File

@ -7,17 +7,23 @@ sidebar_label: scala-sttp
| ------ | ----------- | ------ | ------- |
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|apiPackage|package for generated api classes| |null|
|circeVersion|The version of circe library| |0.13.0|
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (prefered for JDK 1.8+)</dd></dl>|java8|
|disallowAdditionalPropertiesIfNotPresent|Specify the behavior when the 'additionalProperties' keyword is not present in the OAS document. If false: the 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications. If true: when the 'additionalProperties' keyword is not present in a schema, the value of 'additionalProperties' is set to false, i.e. no additional properties are allowed. Note: this mode is not compliant with the JSON schema specification. This is the original openapi-generator behavior.This setting is currently ignored for OAS 2.0 documents: 1) When the 'additionalProperties' keyword is not present in a 2.0 schema, additional properties are NOT allowed. 2) Boolean values of the 'additionalProperties' keyword are ignored. It's as if additional properties are NOT allowed.Note: the root cause are issues #1369 and #1371, which must be resolved in the swagger-parser project.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>when the 'additionalProperties' keyword is not present in a schema, the value of 'additionalProperties' is automatically set to false, i.e. no additional properties are allowed. Note: this mode is not compliant with the JSON schema specification. This is the original openapi-generator behavior.</dd></dl>|true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|jodaTimeVersion|The version of joda-time library| |2.10.6|
|json4sVersion|The version of json4s library| |3.6.8|
|jsonLibrary|Json library to use. Possible values are: json4s and circe.| |json4s|
|legacyDiscriminatorBehavior|This flag is used by OpenAPITools codegen to influence the processing of the discriminator attribute in OpenAPI documents. This flag has no impact if the OAS document does not use the discriminator attribute. The default value of this flag is set in each language-specific code generator (e.g. Python, Java, go...)using the method toModelName. Note to developers supporting a language generator in OpenAPITools; to fully support the discriminator attribute as defined in the OAS specification 3.x, language generators should set this flag to true by default; however this requires updating the mustache templates to generate a language-specific discriminator lookup function that iterates over {{#mappedModels}} and does not iterate over {{children}}, {{#anyOf}}, or {{#oneOf}}.|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
|mainPackage|Top-level package name, which defines 'apiPackage', 'modelPackage', 'invokerPackage'| |org.openapitools.client|
|modelPackage|package for generated models| |null|
|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |camelCase|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|separateErrorChannel|Whether to return response as F[Either[ResponseError[ErrorType], ReturnType]]] or to flatten response's error raising them through enclosing monad (F[ReturnType]).| |true|
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|sourceFolder|source folder for generated code| |null|
|sttpClientVersion|The version of sttp client| |2.2.0|
## IMPORT MAPPING
@ -81,7 +87,7 @@ sidebar_label: scala-sttp
<li>final</li>
<li>finally</li>
<li>for</li>
<li>forsome</li>
<li>forSome</li>
<li>if</li>
<li>implicit</li>
<li>import</li>

View File

@ -16,50 +16,154 @@
package org.openapitools.codegen.languages;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.SupportingFile;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.meta.features.*;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.List;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.*;
import static org.openapitools.codegen.utils.StringUtils.camelize;
public class ScalaSttpClientCodegen extends AbstractScalaCodegen implements CodegenConfig {
private static final StringProperty STTP_CLIENT_VERSION = new StringProperty("sttpClientVersion", "The version of " +
"sttp client", "2.2.0");
private static final BooleanProperty USE_SEPARATE_ERROR_CHANNEL = new BooleanProperty("separateErrorChannel",
"Whether to return response as " +
"F[Either[ResponseError[ErrorType], ReturnType]]] or to flatten " +
"response's error raising them through enclosing monad (F[ReturnType]).", true);
private static final StringProperty JODA_TIME_VERSION = new StringProperty("jodaTimeVersion", "The version of " +
"joda-time library", "2.10.6");
private static final StringProperty JSON4S_VERSION = new StringProperty("json4sVersion", "The version of json4s " +
"library", "3.6.8");
private static final StringProperty CIRCE_VERSION = new StringProperty("circeVersion", "The version of circe " +
"library", "0.13.0");
private static final JsonLibraryProperty JSON_LIBRARY_PROPERTY = new JsonLibraryProperty();
public static final String DEFAULT_PACKAGE_NAME = "org.openapitools.client";
private static final PackageProperty PACKAGE_PROPERTY = new PackageProperty();
private static final List<Property<?>> properties = Arrays.asList(
STTP_CLIENT_VERSION, USE_SEPARATE_ERROR_CHANNEL, JODA_TIME_VERSION,
JSON4S_VERSION, CIRCE_VERSION, JSON_LIBRARY_PROPERTY, PACKAGE_PROPERTY);
private final Logger LOGGER = LoggerFactory.getLogger(ScalaSttpClientCodegen.class);
protected String groupId = "org.openapitools";
protected String artifactId = "openapi-client";
protected String artifactVersion = "1.0.0";
protected boolean registerNonStandardStatusCodes = true;
protected boolean renderJavadoc = true;
protected boolean removeOAuthSecurities = true;
public class ScalaSttpClientCodegen extends ScalaAkkaClientCodegen implements CodegenConfig {
public ScalaSttpClientCodegen() {
super();
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.BETA)
.build();
embeddedTemplateDir = templateDir = "scala-sttp";
modifyFeatureSet(features -> features
.includeDocumentationFeatures(DocumentationFeature.Readme)
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom))
.securityFeatures(EnumSet.of(
SecurityFeature.BasicAuth,
SecurityFeature.ApiKey,
SecurityFeature.BearerToken
))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling
)
.excludeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism
)
.excludeParameterFeatures(
ParameterFeature.Cookie
)
.includeClientModificationFeatures(
ClientModificationFeature.BasePath,
ClientModificationFeature.UserAgent
)
);
outputFolder = "generated-code/scala-sttp";
modelTemplateFiles.put("model.mustache", ".scala");
apiTemplateFiles.put("api.mustache", ".scala");
embeddedTemplateDir = templateDir = "scala-sttp";
additionalProperties.put(CodegenConstants.GROUP_ID, groupId);
additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId);
additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion);
if (renderJavadoc) {
additionalProperties.put("javadocRenderer", new JavadocLambda());
}
additionalProperties.put("fnCapitalize", new CapitalizeLambda());
additionalProperties.put("fnCamelize", new CamelizeLambda(false));
additionalProperties.put("fnEnumEntry", new EnumEntryLambda());
importMapping.remove("Seq");
importMapping.remove("List");
importMapping.remove("Set");
importMapping.remove("Map");
typeMapping = new HashMap<>();
typeMapping.put("array", "Seq");
typeMapping.put("set", "Set");
typeMapping.put("boolean", "Boolean");
typeMapping.put("string", "String");
typeMapping.put("int", "Int");
typeMapping.put("integer", "Int");
typeMapping.put("long", "Long");
typeMapping.put("float", "Float");
typeMapping.put("byte", "Byte");
typeMapping.put("short", "Short");
typeMapping.put("char", "Char");
typeMapping.put("double", "Double");
typeMapping.put("object", "Any");
typeMapping.put("file", "File");
typeMapping.put("binary", "File");
typeMapping.put("number", "Double");
instantiationTypes.put("array", "ListBuffer");
instantiationTypes.put("map", "Map");
properties.stream()
.map(Property::toCliOptions)
.flatMap(Collection::stream)
.forEach(option -> cliOptions.add(option));
}
@Override
public void processOpts() {
super.processOpts();
if (additionalProperties.containsKey("mainPackage")) {
setMainPackage((String) additionalProperties.get("mainPackage"));
additionalProperties.replace("configKeyPath", this.configKeyPath);
apiPackage = mainPackage + ".api";
modelPackage = mainPackage + ".model";
invokerPackage = mainPackage + ".core";
additionalProperties.put("apiPackage", apiPackage);
additionalProperties.put("modelPackage", modelPackage);
}
properties.forEach(p -> p.updateAdditionalProperties(additionalProperties));
invokerPackage = PACKAGE_PROPERTY.getInvokerPackage(additionalProperties);
apiPackage = PACKAGE_PROPERTY.getApiPackage(additionalProperties);
modelPackage = PACKAGE_PROPERTY.getModelPackage(additionalProperties);
supportingFiles.clear();
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt"));
final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator);
supportingFiles.add(new SupportingFile("requests.mustache", invokerFolder, "requests.scala"));
supportingFiles.add(new SupportingFile("apiInvoker.mustache", invokerFolder, "ApiInvoker.scala"));
final String apiFolder = (sourceFolder + File.separator + apiPackage).replace(".", File.separator);
supportingFiles.add(new SupportingFile("enumsSerializers.mustache", apiFolder, "EnumsSerializers.scala"));
supportingFiles.add(new SupportingFile("serializers.mustache", invokerFolder, "Serializers.scala"));
supportingFiles.add(new SupportingFile("jsonSupport.mustache", invokerFolder, "JsonSupport.scala"));
supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties"));
supportingFiles.add(new SupportingFile("dateSerializers.mustache", invokerFolder, "DateSerializers.scala"));
}
@Override
@ -87,4 +191,300 @@ public class ScalaSttpClientCodegen extends ScalaAkkaClientCodegen implements Co
op.path = encodePath(path);
return op;
}
@Override
public CodegenType getTag() {
return CodegenType.CLIENT;
}
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "`" + name + "`";
}
@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
if (registerNonStandardStatusCodes) {
try {
@SuppressWarnings("unchecked")
Map<String, ArrayList<CodegenOperation>> opsMap = (Map<String, ArrayList<CodegenOperation>>) objs.get("operations");
HashSet<Integer> unknownCodes = new HashSet<Integer>();
for (CodegenOperation operation : opsMap.get("operation")) {
for (CodegenResponse response : operation.responses) {
if ("default".equals(response.code)) {
continue;
}
try {
int code = Integer.parseInt(response.code);
if (code >= 600) {
unknownCodes.add(code);
}
} catch (NumberFormatException e) {
LOGGER.error("Status code is not an integer : response.code", e);
}
}
}
if (!unknownCodes.isEmpty()) {
additionalProperties.put("unknownStatusCodes", unknownCodes);
}
} catch (Exception e) {
LOGGER.error("Unable to find operations List", e);
}
}
return super.postProcessOperationsWithModels(objs, allModels);
}
@Override
public List<CodegenSecurity> fromSecurity(Map<String, SecurityScheme> schemes) {
final List<CodegenSecurity> codegenSecurities = super.fromSecurity(schemes);
if (!removeOAuthSecurities) {
return codegenSecurities;
}
// Remove OAuth securities
Iterator<CodegenSecurity> it = codegenSecurities.iterator();
while (it.hasNext()) {
final CodegenSecurity security = it.next();
if (security.isOAuth) {
it.remove();
}
}
// Adapt 'hasMore'
it = codegenSecurities.iterator();
while (it.hasNext()) {
final CodegenSecurity security = it.next();
security.hasMore = it.hasNext();
}
if (codegenSecurities.isEmpty()) {
return null;
}
return codegenSecurities;
}
@Override
public String toParamName(String name) {
return formatIdentifier(name, false);
}
@Override
public String toEnumName(CodegenProperty property) {
return formatIdentifier(property.baseName, true);
}
@Override
public String toDefaultValue(Schema p) {
if (p.getRequired() != null && p.getRequired().contains(p.getName())) {
return "None";
}
if (ModelUtils.isBooleanSchema(p)) {
return null;
} else if (ModelUtils.isDateSchema(p)) {
return null;
} else if (ModelUtils.isDateTimeSchema(p)) {
return null;
} else if (ModelUtils.isNumberSchema(p)) {
return null;
} else if (ModelUtils.isIntegerSchema(p)) {
return null;
} else if (ModelUtils.isMapSchema(p)) {
String inner = getSchemaType(getAdditionalProperties(p));
return "Map[String, " + inner + "].empty ";
} else if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
String inner = getSchemaType(ap.getItems());
if (ModelUtils.isSet(ap)) {
return "Set[" + inner + "].empty ";
}
return "Seq[" + inner + "].empty ";
} else if (ModelUtils.isStringSchema(p)) {
return null;
} else {
return null;
}
}
public static abstract class Property<T> {
final String name;
final String description;
final T defaultValue;
public Property(String name, String description, T defaultValue) {
this.name = name;
this.description = description;
this.defaultValue = defaultValue;
}
public abstract List<CliOption> toCliOptions();
public abstract void updateAdditionalProperties(Map<String, Object> additionalProperties);
public abstract T getValue(Map<String, Object> additionalProperties);
public void setValue(Map<String, Object> additionalProperties, T value) {
additionalProperties.put(name, value);
}
}
public static class StringProperty extends Property<String> {
public StringProperty(String name, String description, String defaultValue) {
super(name, description, defaultValue);
}
@Override
public List<CliOption> toCliOptions() {
return Collections.singletonList(CliOption.newString(name, description).defaultValue(defaultValue));
}
@Override
public void updateAdditionalProperties(Map<String, Object> additionalProperties) {
if (!additionalProperties.containsKey(name)) {
additionalProperties.put(name, defaultValue);
}
}
@Override
public String getValue(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(name, defaultValue).toString();
}
}
public static class BooleanProperty extends Property<Boolean> {
public BooleanProperty(String name, String description, Boolean defaultValue) {
super(name, description, defaultValue);
}
@Override
public List<CliOption> toCliOptions() {
return Collections.singletonList(CliOption.newBoolean(name, description, defaultValue));
}
@Override
public void updateAdditionalProperties(Map<String, Object> additionalProperties) {
Boolean value = getValue(additionalProperties);
additionalProperties.put(name, value);
}
@Override
public Boolean getValue(Map<String, Object> additionalProperties) {
return Boolean.valueOf(additionalProperties.getOrDefault(name, defaultValue.toString()).toString());
}
}
public static class JsonLibraryProperty extends StringProperty {
private static final String JSON4S = "json4s";
private static final String CIRCE = "circe";
public JsonLibraryProperty() {
super("jsonLibrary", "Json library to use. Possible values are: json4s and circe.", JSON4S);
}
@Override
public void updateAdditionalProperties(Map<String, Object> additionalProperties) {
String value = getValue(additionalProperties);
if (value.equals(CIRCE) || value.equals(JSON4S)) {
additionalProperties.put(CIRCE, value.equals(CIRCE));
additionalProperties.put(JSON4S, value.equals(JSON4S));
} else {
IllegalArgumentException exception =
new IllegalArgumentException("Invalid json library: " + value + ". Must be " + CIRCE + " " +
"or " + JSON4S);
throw exception;
}
}
}
public static class PackageProperty extends StringProperty {
public PackageProperty() {
super("mainPackage", "Top-level package name, which defines 'apiPackage', 'modelPackage', " +
"'invokerPackage'", DEFAULT_PACKAGE_NAME);
}
@Override
public void updateAdditionalProperties(Map<String, Object> additionalProperties) {
String mainPackage = getValue(additionalProperties);
if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
String apiPackage = mainPackage + ".api";
additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage);
}
if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
String modelPackage = mainPackage + ".model";
additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage);
}
if (!additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
String invokerPackage = mainPackage + ".core";
additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
}
}
public String getApiPackage(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(CodegenConstants.API_PACKAGE, DEFAULT_PACKAGE_NAME + ".api").toString();
}
public String getModelPackage(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(CodegenConstants.MODEL_PACKAGE, DEFAULT_PACKAGE_NAME + ".model").toString();
}
public String getInvokerPackage(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(CodegenConstants.INVOKER_PACKAGE, DEFAULT_PACKAGE_NAME + ".core").toString();
}
}
private static abstract class CustomLambda implements Mustache.Lambda {
@Override
public void execute(Template.Fragment frag, Writer out) throws IOException {
final StringWriter tempWriter = new StringWriter();
frag.execute(tempWriter);
out.write(formatFragment(tempWriter.toString()));
}
public abstract String formatFragment(String fragment);
}
private static class JavadocLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
final String[] lines = fragment.split("\\r?\\n");
final StringBuilder sb = new StringBuilder();
sb.append(" /**\n");
for (String line : lines) {
sb.append(" * ").append(line).append("\n");
}
sb.append(" */\n");
return sb.toString();
}
}
private static class CapitalizeLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
return StringUtils.capitalize(fragment);
}
}
private static class CamelizeLambda extends CustomLambda {
private final boolean capitalizeFirst;
public CamelizeLambda(boolean capitalizeFirst) {
this.capitalizeFirst = capitalizeFirst;
}
@Override
public String formatFragment(String fragment) {
return camelize(fragment, !capitalizeFirst);
}
}
private class EnumEntryLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
return formatIdentifier(fragment, true);
}
}
}

View File

@ -4,42 +4,40 @@ package {{package}}
{{#imports}}
import {{import}}
{{/imports}}
import {{invokerPackage}}._
import alias._
import {{invokerPackage}}.JsonSupport._
import sttp.client._
import sttp.model.Method
{{#operations}}
object {{classname}} {
def apply(baseUrl: String = "{{{basePath}}}")(implicit serializer: SttpSerializer) = new {{classname}}(baseUrl)
def apply(baseUrl: String = "{{{basePath}}}") = new {{classname}}(baseUrl)
}
class {{classname}}(baseUrl: String)(implicit serializer: SttpSerializer) {
import Helpers._
import serializer._
class {{classname}}(baseUrl: String) {
{{#operation}}
{{#javadocRenderer}}
{{>javadoc}}
{{/javadocRenderer}}
def {{operationId}}({{>methodParameters}}): ApiRequestT[{{>operationReturnType}}] =
def {{operationId}}({{>methodParameters}}): Request[{{#separateErrorChannel}}Either[ResponseError[Exception], {{>operationReturnType}}]{{/separateErrorChannel}}{{^separateErrorChannel}}{{>operationReturnType}}{{/separateErrorChannel}}, Nothing] =
basicRequest
.method(Method.{{httpMethod.toUpperCase}}, uri"$baseUrl{{{path}}}{{#queryParams.0}}?{{#queryParams}}{{baseName}}=${{{paramName}}}{{^-last}}&{{/-last}}{{/queryParams}}{{/queryParams.0}}{{#isApiKey}}{{#isKeyInQuery}}{{^queryParams.0}}?{{/queryParams.0}}{{#queryParams.0}}&{{/queryParams.0}}{{keyParamName}}=${apiKey.value}&{{/isKeyInQuery}}{{/isApiKey}}")
.contentType({{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{^consumes}}"application/json"{{/consumes}}){{#headerParams}}
.header({{>paramCreation}}){{/headerParams}}{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
.auth.withCredentials(basicAuth.user, basicAuth.password){{/isBasicBasic}}{{#isBasicBearer}}
.auth.bearer(bearerToken.token){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}}{{#isKeyInHeader}}
.header("{{keyParamName}}", apiKey.value){{/isKeyInHeader}}{{#isKeyInCookie}}
.cookie("{{keyParamName}}", apiKey.value){{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}}{{#formParams.0}}
.auth.withCredentials(username, password){{/isBasicBasic}}{{#isBasicBearer}}
.auth.bearer(bearerToken){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}}{{#isKeyInHeader}}
.header("{{keyParamName}}", apiKey){{/isKeyInHeader}}{{#isKeyInCookie}}
.cookie("{{keyParamName}}", apiKey){{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}}{{#formParams.0}}{{^isMultipart}}
.body(Map({{#formParams}}
{{>paramFormCreation}},{{/formParams}}
)){{/formParams.0}}{{#bodyParam}}
{{>paramFormCreation}}{{#hasMore}}, {{/hasMore}}{{/formParams}}
)){{/isMultipart}}{{#isMultipart}}
.multipartBody(Seq({{#formParams}}
{{>paramMultipartCreation}}{{#hasMore}}, {{/hasMore}}{{/formParams}}
).flatten){{/isMultipart}}{{/formParams.0}}{{#bodyParam}}
.body({{paramName}}){{/bodyParam}}
.response(asJson[{{>operationReturnType}}])
.response({{#separateErrorChannel}}asJson{{/separateErrorChannel}}{{^separateErrorChannel}}asJsonAlwaysUnsafe{{/separateErrorChannel}}[{{>operationReturnType}}])
{{/operation}}
}
{{/operations}}

View File

@ -1,50 +0,0 @@
{{>licenseInfo}}
package {{invokerPackage}}
import org.json4s._
import sttp.client._
import sttp.model.StatusCode
import {{{apiPackage}}}.EnumsSerializers
import sttp.client.json4s.SttpJson4sApi
import sttp.client.monad.MonadError
class SttpSerializer(implicit val format: Formats = DefaultFormats ++ EnumsSerializers.all ++ Serializers.all,
implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization) extends SttpJson4sApi
class HttpException(val statusCode: StatusCode, val statusText: String, val message: String) extends Exception(s"[$statusCode] $statusText: $message")
object Helpers {
// Helper to handle Optional header parameters
implicit class optionalParams(val request: RequestT[Identity, Either[String, String], Nothing]) extends AnyVal {
def header( header: String, optValue: Option[Any]): RequestT[Identity, Either[String, String], Nothing] = {
optValue.map( value => request.header(header, value.toString)).getOrElse(request)
}
}
}
object ApiInvoker {
/**
* Allows request execution without calling apiInvoker.execute(request)
* request.result can be used to get a monad wrapped content.
*
* @param request the apiRequest to be executed
*/
implicit class ApiRequestImprovements[R[_], T](request: RequestT[Identity, Either[ResponseError[Exception], T], Nothing]) {
def result(implicit backend: SttpBackend[R, Nothing, Nothing]): R[T] = {
val responseT = request.send()
val ME: MonadError[R] = backend.responseMonad
ME.flatMap(responseT) {
response =>
response.body match {
case Left(ex) => ME.error[T](new HttpException(response.code, response.statusText, ex.body))
case Right(value) => ME.unit(value)
}
}
}
}
}

View File

@ -2,20 +2,24 @@ version := "{{artifactVersion}}"
name := "{{artifactId}}"
organization := "{{groupId}}"
scalaVersion := "2.13.0"
crossScalaVersions := Seq(scalaVersion.value, "2.12.10", "2.11.12")
scalaVersion := "2.13.2"
crossScalaVersions := Seq(scalaVersion.value, "2.12.10")
libraryDependencies ++= Seq(
"com.softwaremill.sttp.client" %% "core" % "2.0.0",
"com.softwaremill.sttp.client" %% "json4s" % "2.0.0",
"com.softwaremill.sttp.client" %% "core" % "{{sttpClientVersion}}",
{{#joda}}
"joda-time" % "joda-time" % "2.10.1",
"joda-time" % "joda-time" % "{{jodaTimeVersion}}",
{{/joda}}
"org.json4s" %% "json4s-jackson" % "3.6.7",
// test dependencies
"org.scalatest" %% "scalatest" % "3.0.8" % Test,
"junit" % "junit" % "4.13" % "test"
{{#json4s}}
"com.softwaremill.sttp.client" %% "json4s" % "{{sttpClientVersion}}",
"org.json4s" %% "json4s-jackson" % "{{json4sVersion}}"
{{/json4s}}
{{#circe}}
"com.softwaremill.sttp.client" %% "circe" % "{{sttpClientVersion}}",
"io.circe" %% "circe-core" % "{{circeVersion}}",
"io.circe" %% "circe-generic" % "{{circeVersion}}",
"io.circe" %% "circe-parser" % "{{circeVersion}}"
{{/circe}}
)
scalacOptions := Seq(
@ -23,5 +27,3 @@ scalacOptions := Seq(
"-deprecation",
"-feature"
)
publishArtifact in (Compile, packageDoc) := false

View File

@ -0,0 +1,76 @@
package {{invokerPackage}}
{{#java8}}
import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId}
import java.time.format.DateTimeFormatter
import scala.util.Try
{{/java8}}
{{#joda}}
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
{{/joda}}
{{#json4s}}
object DateSerializers {
import org.json4s.{Serializer, CustomSerializer, JNull}
import org.json4s.JsonAST.JString
{{#java8}}
case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( {
case JString(s) =>
Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse
Try(LocalDateTime.parse(s).atZone(ZoneId.systemDefault()).toOffsetDateTime) getOrElse (null)
case JNull => null
}, {
case d: OffsetDateTime =>
JString(d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
}))
case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( {
case JString(s) => LocalDate.parse(s)
case JNull => null
}, {
case d: LocalDate =>
JString(d.format(DateTimeFormatter.ISO_LOCAL_DATE))
}))
{{/java8}}
{{#joda}}
case object DateTimeSerializer extends CustomSerializer[DateTime](_ => ( {
case JString(s) =>
ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(s)
case JNull => null
}, {
case d: org.joda.time.DateTime =>
JString(ISODateTimeFormat.dateTime().print(d))
})
)
case object LocalDateSerializer extends CustomSerializer[org.joda.time.LocalDate](_ => ( {
case JString(s) => org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate(s)
case JNull => null
}, {
case d: org.joda.time.LocalDate => JString(d.toString("yyyy-MM-dd"))
}))
{{/joda}}
def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ LocalDateSerializer :+ DateTimeSerializer
}
{{/json4s}}
{{#circe}}
trait DateSerializers {
import io.circe.{Decoder, Encoder}
{{#java8}}
implicit val isoOffsetDateTimeDecoder: Decoder[OffsetDateTime] = Decoder.decodeOffsetDateTimeWithFormatter(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
implicit val isoOffsetDateTimeEncoder: Encoder[OffsetDateTime] = Encoder.encodeOffsetDateTimeWithFormatter(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
implicit val localDateDecoder: Decoder[LocalDate] = Decoder.decodeLocalDateWithFormatter(DateTimeFormatter.ISO_LOCAL_DATE)
implicit val localDateEncoder: Encoder[LocalDate] = Encoder.encodeLocalDateWithFormatter(DateTimeFormatter.ISO_LOCAL_DATE)
{{/java8}}
{{#joda}}
implicit val dateTimeDecoder: Decoder[DateTime] = Decoder.decodeString.map(ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(_))
implicit val dateTimeEncoder: Encoder[DateTime] = Encoder.encodeString.contramap(ISODateTimeFormat.dateTime().print(_))
implicit val localDateDecoder: Decoder[org.joda.time.LocalDate] = Decoder.decodeString.map(org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate(_))
implicit val localDateEncoder: Encoder[org.joda.time.LocalDate] = Encoder.encodeString.contramap(_.toString("yyyy-MM-dd"))
{{/joda}}
}
{{/circe}}

View File

@ -1,42 +0,0 @@
{{>licenseInfo}}
package {{apiPackage}}
{{#models.0}}
import {{modelPackage}}._
{{/models.0}}
import org.json4s._
import scala.reflect.ClassTag
object EnumsSerializers {
def all: Seq[Serializer[_]] = Seq[Serializer[_]](){{#models}}{{#model}}{{#hasEnums}}{{#vars}}{{#isEnum}} :+
new EnumNameSerializer({{classname}}Enums.{{datatypeWithEnum}}){{/isEnum}}{{/vars}}{{/hasEnums}}{{/model}}{{/models}}
private class EnumNameSerializer[E <: Enumeration: ClassTag](enum: E)
extends Serializer[E#Value] {
import JsonDSL._
val EnumerationClass: Class[E#Value] = classOf[E#Value]
def deserialize(implicit format: Formats):
PartialFunction[(TypeInfo, JValue), E#Value] = {
case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) =>
json match {
case JString(value) =>
enum.withName(value)
case value =>
throw new MappingException(s"Can't convert $value to $EnumerationClass")
}
}
private[this] def isValid(json: JValue) = json match {
case JString(value) if enum.values.exists(_.toString == value) => true
case _ => false
}
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
case i: E#Value => i.toString
}
}
}

View File

@ -0,0 +1,53 @@
{{>licenseInfo}}
package {{invokerPackage}}
{{#models.0}}
import {{modelPackage}}._
{{/models.0}}
{{#json4s}}
import org.json4s._
import sttp.client.json4s.SttpJson4sApi
import scala.reflect.ClassTag
object JsonSupport extends SttpJson4sApi {
def enumSerializers: Seq[Serializer[_]] = Seq[Serializer[_]](){{#models}}{{#model}}{{#hasEnums}}{{#vars}}{{#isEnum}} :+
new EnumNameSerializer({{classname}}Enums.{{datatypeWithEnum}}){{/isEnum}}{{/vars}}{{/hasEnums}}{{/model}}{{/models}}
private class EnumNameSerializer[E <: Enumeration: ClassTag](enum: E) extends Serializer[E#Value] {
import JsonDSL._
val EnumerationClass: Class[E#Value] = classOf[E#Value]
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), E#Value] = {
case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) =>
json match {
case JString(value) => enum.withName(value)
case value => throw new MappingException(s"Can't convert $value to $EnumerationClass")
}
}
private[this] def isValid(json: JValue) = json match {
case JString(value) if enum.values.exists(_.toString == value) => true
case _ => false
}
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
case i: E#Value => i.toString
}
}
implicit val format: Formats = DefaultFormats ++ enumSerializers ++ DateSerializers.all
implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization
}
{{/json4s}}
{{#circe}}
import io.circe.{Decoder, Encoder}
import io.circe.generic.AutoDerivation
import sttp.client.circe.SttpCirceApi
object JsonSupport extends SttpCirceApi with AutoDerivation with DateSerializers {
{{#models}}{{#model}}{{#hasEnums}}{{#vars}}{{#isEnum}}
implicit val {{classname}}{{datatypeWithEnum}}Decoder: Decoder[{{classname}}Enums.{{datatypeWithEnum}}] = Decoder.decodeEnumeration({{classname}}Enums.{{datatypeWithEnum}})
implicit val {{classname}}{{datatypeWithEnum}}Encoder: Encoder[{{classname}}Enums.{{datatypeWithEnum}}] = Encoder.encodeEnumeration({{classname}}Enums.{{datatypeWithEnum}})
{{/isEnum}}{{/vars}}{{/hasEnums}}{{/model}}{{/models}}
}
{{/circe}}

View File

@ -1 +1 @@
{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}{{#isContainer}}{{dataType}}{{/isContainer}}{{^isContainer}}Option[{{dataType}}]{{/isContainer}}{{/required}}{{^defaultValue}}{{^required}}{{^isContainer}} = None{{/isContainer}}{{/required}}{{/defaultValue}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#authMethods.0}})(implicit {{#authMethods}}{{#isApiKey}}apiKey: ApiKeyValue{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}basicAuth: BasicCredentials{{/isBasicBasic}}{{#isBasicBearer}}bearerToken: BearerToken{{/isBasicBearer}}{{/isBasic}}{{#hasMore}}, {{/hasMore}}{{/authMethods}}{{/authMethods.0}}
{{#authMethods.0}}{{#authMethods}}{{#isApiKey}}apiKey: String{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}username: String, password: String{{/isBasicBasic}}{{#isBasicBearer}}bearerToken: String{{/isBasicBearer}}{{/isBasic}}{{#hasMore}}, {{/hasMore}}{{/authMethods}})({{/authMethods.0}}{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}{{#isContainer}}{{dataType}}{{/isContainer}}{{^isContainer}}Option[{{dataType}}]{{/isContainer}}{{/required}}{{^defaultValue}}{{^required}}{{^isContainer}} = None{{/isContainer}}{{/required}}{{/defaultValue}}{{#hasMore}}, {{/hasMore}}{{/allParams}}

View File

@ -4,7 +4,6 @@ package {{package}}
{{#imports}}
import {{import}}
{{/imports}}
import {{invokerPackage}}.ApiModel
{{#models}}
{{#model}}
@ -23,7 +22,7 @@ case class {{classname}}(
{{/description}}
{{{name}}}: {{^required}}Option[{{/required}}{{^isEnum}}{{dataType}}{{/isEnum}}{{#isEnum}}{{classname}}Enums.{{datatypeWithEnum}}{{/isEnum}}{{^required}}] = None{{/required}}{{#hasMore}},{{/hasMore}}
{{/vars}}
) extends ApiModel
)
{{#hasEnums}}
object {{classname}}Enums {

View File

@ -0,0 +1,16 @@
{{#required}}
{{#isFile}}
multipartFile("{{baseName}}", {{#isContainer}}ArrayValues({{{paramName}}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}{{{paramName}}}{{/isContainer}})
{{/isFile}}
{{^isFile}}
multipart("{{baseName}}", {{paramName}})
{{/isFile}}
{{/required}}
{{^required}}
{{#isFile}}
{{paramName}}.map(multipartFile("{{baseName}}", _))
{{/isFile}}
{{^isFile}}
{{paramName}}.map(multipart("{{baseName}}", {{#isContainer}}ArrayValues(_{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}_{{/isContainer}}))
{{/isFile}}
{{/required}}

View File

@ -0,0 +1 @@
sbt.version=1.3.12

View File

@ -1,48 +0,0 @@
{{>licenseInfo}}
package {{invokerPackage}}
import sttp.client.{Identity, RequestT, ResponseError}
/**
* This trait needs to be added to any model defined by the api.
*/
trait ApiModel
/**
* Sttp type aliases
*/
object alias {
type ApiRequestT[T] = RequestT[Identity, Either[ResponseError[Exception], T], Nothing]
}
/**
* Single trait defining a credential that can be transformed to a paramName / paramValue tupple
*/
sealed trait Credentials {
def asQueryParam: Option[(String, String)] = None
}
sealed case class BasicCredentials(user: String, password: String) extends Credentials
sealed case class BearerToken(token: String) extends Credentials
sealed case class ApiKeyCredentials(key: ApiKeyValue, keyName: String, location: ApiKeyLocation) extends Credentials {
override def asQueryParam: Option[(String, String)] = location match {
case ApiKeyLocations.QUERY => Some((keyName, key.value))
case _ => None
}
}
sealed case class ApiKeyValue(value: String)
sealed trait ApiKeyLocation
object ApiKeyLocations {
case object QUERY extends ApiKeyLocation
case object HEADER extends ApiKeyLocation
case object COOKIE extends ApiKeyLocation
}

View File

@ -1,57 +0,0 @@
package {{invokerPackage}}
{{#java8}}
import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId}
import java.time.format.DateTimeFormatter
import scala.util.Try
{{/java8}}
{{#joda}}
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
{{/joda}}
import org.json4s.{Serializer, CustomSerializer, JNull}
import org.json4s.JsonAST.JString
object Serializers {
{{#java8}}
case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( {
case JString(s) =>
Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse
Try(LocalDateTime.parse(s).atZone(ZoneId.systemDefault()).toOffsetDateTime) getOrElse (null)
case JNull => null
}, {
case d: OffsetDateTime =>
JString(d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
}))
case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( {
case JString(s) => LocalDate.parse(s)
case JNull => null
}, {
case d: LocalDate =>
JString(d.format(DateTimeFormatter.ISO_LOCAL_DATE))
}))
{{/java8}}
{{#joda}}
case object DateTimeSerializer extends CustomSerializer[DateTime](_ => ( {
case JString(s) =>
ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(s)
case JNull => null
}, {
case d: org.joda.time.DateTime =>
JString(ISODateTimeFormat.dateTime().print(d))
})
)
case object LocalDateSerializer extends CustomSerializer[org.joda.time.LocalDate](_ => ( {
case JString(s) => org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate(s)
case JNull => null
}, {
case d: org.joda.time.LocalDate => JString(d.toString("yyyy-MM-dd"))
}))
{{/joda}}
def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ LocalDateSerializer :+ DateTimeSerializer
}

View File

@ -0,0 +1,29 @@
package org.openapitools.codegen.scala;
import org.junit.Assert;
import org.junit.Test;
import org.openapitools.codegen.languages.ScalaSttpClientCodegen;
import java.util.HashMap;
import java.util.Map;
public class SttpBooleanPropertyTest {
@Test
public void shouldUseDefaultValueIfAdditionalPropertiesAreEmpty() {
ScalaSttpClientCodegen.BooleanProperty booleanProperty = new ScalaSttpClientCodegen.BooleanProperty("k1", "desc", false);
Map<String, Object> additionalProperties = new HashMap<>();
booleanProperty.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(false, additionalProperties.get("k1"));
}
@Test
public void shouldUseGivenValueIfProvided() {
ScalaSttpClientCodegen.BooleanProperty booleanProperty = new ScalaSttpClientCodegen.BooleanProperty("k1", "desc", false);
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put("k1", true);
booleanProperty.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(true, additionalProperties.get("k1"));
}
}

View File

@ -0,0 +1,39 @@
package org.openapitools.codegen.scala;
import org.junit.Assert;
import org.junit.Test;
import org.openapitools.codegen.languages.ScalaSttpClientCodegen;
import java.util.HashMap;
import java.util.Map;
public class SttpJsonLibraryPropertyTest {
@Test
public void shouldUseJson4sByDefault() {
ScalaSttpClientCodegen.JsonLibraryProperty property = new ScalaSttpClientCodegen.JsonLibraryProperty();
Map<String, Object> additionalProperties = new HashMap<>();
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(true, additionalProperties.get("json4s"));
Assert.assertEquals(false, additionalProperties.get("circe"));
}
@Test
public void shouldUseJson4sIfExplicitlyAskTo() {
ScalaSttpClientCodegen.JsonLibraryProperty property = new ScalaSttpClientCodegen.JsonLibraryProperty();
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put("jsonLibrary", "json4s");
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(true, additionalProperties.get("json4s"));
Assert.assertEquals(false, additionalProperties.get("circe"));
}
@Test
public void shouldUseCirceIfExplicitlyAskTo() {
ScalaSttpClientCodegen.JsonLibraryProperty property = new ScalaSttpClientCodegen.JsonLibraryProperty();
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put("jsonLibrary", "circe");
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(false, additionalProperties.get("json4s"));
Assert.assertEquals(true, additionalProperties.get("circe"));
}
}

View File

@ -0,0 +1,59 @@
package org.openapitools.codegen.scala;
import org.junit.Assert;
import org.junit.Test;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.languages.ScalaSttpClientCodegen;
import java.util.HashMap;
import java.util.Map;
public class SttpPackagePropertyTest {
@Test
public void shouldUseDefaultPackageNameIfAdditionalPropertiesAreEmpty(){
ScalaSttpClientCodegen.PackageProperty property = new ScalaSttpClientCodegen.PackageProperty();
Map<String, Object> additionalProperties = new HashMap<>();
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(ScalaSttpClientCodegen.DEFAULT_PACKAGE_NAME + ".api",
additionalProperties.get(CodegenConstants.API_PACKAGE));
Assert.assertEquals(ScalaSttpClientCodegen.DEFAULT_PACKAGE_NAME + ".model",
additionalProperties.get(CodegenConstants.MODEL_PACKAGE));
Assert.assertEquals(ScalaSttpClientCodegen.DEFAULT_PACKAGE_NAME + ".core",
additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
}
@Test
public void shouldUseCustomMainPackageNameIfProvided(){
ScalaSttpClientCodegen.PackageProperty property = new ScalaSttpClientCodegen.PackageProperty();
Map<String, Object> additionalProperties = new HashMap<>();
String customPackageName = "my.custom.pkg.name";
additionalProperties.put("mainPackage", customPackageName);
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(customPackageName + ".api",
additionalProperties.get(CodegenConstants.API_PACKAGE));
Assert.assertEquals(customPackageName + ".model",
additionalProperties.get(CodegenConstants.MODEL_PACKAGE));
Assert.assertEquals(customPackageName + ".core",
additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
}
@Test
public void shouldAllowToMixCustomPackages(){
ScalaSttpClientCodegen.PackageProperty property = new ScalaSttpClientCodegen.PackageProperty();
Map<String, Object> additionalProperties = new HashMap<>();
String customPackageName = "my.custom.pkg.name";
additionalProperties.put("mainPackage", customPackageName);
String otherCustomPackageName = "some.other.custom.pkg.api";
additionalProperties.put(CodegenConstants.API_PACKAGE, otherCustomPackageName);
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals(otherCustomPackageName,
additionalProperties.get(CodegenConstants.API_PACKAGE));
Assert.assertEquals(customPackageName + ".model",
additionalProperties.get(CodegenConstants.MODEL_PACKAGE));
Assert.assertEquals(customPackageName + ".core",
additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
}
}

View File

@ -0,0 +1,30 @@
package org.openapitools.codegen.scala;
import org.junit.Assert;
import org.junit.Test;
import org.openapitools.codegen.languages.ScalaSttpClientCodegen;
import java.util.HashMap;
import java.util.Map;
public class SttpStringPropertyTest {
@Test
public void shouldUseDefaultValueIfAdditionalPropertiesAreEmpty(){
ScalaSttpClientCodegen.StringProperty property = new ScalaSttpClientCodegen.StringProperty("k1", "desc", "default");
Map<String, Object> additionalProperties = new HashMap<>();
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals("default", additionalProperties.get("k1"));
}
@Test
public void shouldUseGivenValueIfProvided(){
ScalaSttpClientCodegen.StringProperty property = new ScalaSttpClientCodegen.StringProperty("k1", "desc", "default");
Map<String, Object> additionalProperties = new HashMap<>();
additionalProperties.put("k1", "custom");
property.updateAdditionalProperties(additionalProperties);
Assert.assertEquals("custom", additionalProperties.get("k1"));
}
}

View File

@ -1,12 +1,11 @@
README.md
build.sbt
src/main/scala/org/openapitools/client/api/EnumsSerializers.scala
project/build.properties
src/main/scala/org/openapitools/client/api/PetApi.scala
src/main/scala/org/openapitools/client/api/StoreApi.scala
src/main/scala/org/openapitools/client/api/UserApi.scala
src/main/scala/org/openapitools/client/core/ApiInvoker.scala
src/main/scala/org/openapitools/client/core/Serializers.scala
src/main/scala/org/openapitools/client/core/requests.scala
src/main/scala/org/openapitools/client/core/DateSerializers.scala
src/main/scala/org/openapitools/client/core/JsonSupport.scala
src/main/scala/org/openapitools/client/model/ApiResponse.scala
src/main/scala/org/openapitools/client/model/Category.scala
src/main/scala/org/openapitools/client/model/InlineObject.scala

View File

@ -2,17 +2,13 @@ version := "1.0.0"
name := "scala-sttp-petstore"
organization := "org.openapitools"
scalaVersion := "2.13.0"
crossScalaVersions := Seq(scalaVersion.value, "2.12.10", "2.11.12")
scalaVersion := "2.13.2"
crossScalaVersions := Seq(scalaVersion.value, "2.12.10")
libraryDependencies ++= Seq(
"com.softwaremill.sttp.client" %% "core" % "2.0.0",
"com.softwaremill.sttp.client" %% "json4s" % "2.0.0",
"org.json4s" %% "json4s-jackson" % "3.6.7",
// test dependencies
"org.scalatest" %% "scalatest" % "3.0.8" % Test,
"junit" % "junit" % "4.13" % "test"
"com.softwaremill.sttp.client" %% "core" % "2.2.0",
"com.softwaremill.sttp.client" %% "json4s" % "2.2.0",
"org.json4s" %% "json4s-jackson" % "3.6.8"
)
scalacOptions := Seq(
@ -20,5 +16,3 @@ scalacOptions := Seq(
"-deprecation",
"-feature"
)
publishArtifact in (Compile, packageDoc) := false

View File

@ -0,0 +1 @@
sbt.version=1.3.12

View File

@ -14,20 +14,16 @@ package org.openapitools.client.api
import org.openapitools.client.model.ApiResponse
import java.io.File
import org.openapitools.client.model.Pet
import org.openapitools.client.core._
import alias._
import org.openapitools.client.core.JsonSupport._
import sttp.client._
import sttp.model.Method
object PetApi {
def apply(baseUrl: String = "http://petstore.swagger.io/v2")(implicit serializer: SttpSerializer) = new PetApi(baseUrl)
def apply(baseUrl: String = "http://petstore.swagger.io/v2") = new PetApi(baseUrl)
}
class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
import Helpers._
import serializer._
class PetApi(baseUrl: String) {
/**
* Expected answers:
@ -36,7 +32,8 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param pet Pet object that needs to be added to the store
*/
def addPet(pet: Pet): ApiRequestT[Pet] =
def addPet(pet: Pet
): Request[Either[ResponseError[Exception], Pet], Nothing] =
basicRequest
.method(Method.POST, uri"$baseUrl/pet")
.contentType("application/json")
@ -50,7 +47,8 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
* @param petId Pet id to delete
* @param apiKey
*/
def deletePet(petId: Long, apiKey: Option[String] = None): ApiRequestT[Unit] =
def deletePet(petId: Long, apiKey: Option[String] = None
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.DELETE, uri"$baseUrl/pet/${petId}")
.contentType("application/json")
@ -66,7 +64,8 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param status Status values that need to be considered for filter
*/
def findPetsByStatus(status: Seq[String]): ApiRequestT[Seq[Pet]] =
def findPetsByStatus(status: Seq[String]
): Request[Either[ResponseError[Exception], Seq[Pet]], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/pet/findByStatus?status=$status")
.contentType("application/json")
@ -81,7 +80,8 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param tags Tags to filter by
*/
def findPetsByTags(tags: Seq[String]): ApiRequestT[Seq[Pet]] =
def findPetsByTags(tags: Seq[String]
): Request[Either[ResponseError[Exception], Seq[Pet]], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/pet/findByTags?tags=$tags")
.contentType("application/json")
@ -100,11 +100,12 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param petId ID of pet to return
*/
def getPetById(petId: Long)(implicit apiKey: ApiKeyValue): ApiRequestT[Pet] =
def getPetById(apiKey: String)(petId: Long
): Request[Either[ResponseError[Exception], Pet], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/pet/${petId}")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.response(asJson[Pet])
/**
@ -116,7 +117,8 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param pet Pet object that needs to be added to the store
*/
def updatePet(pet: Pet): ApiRequestT[Pet] =
def updatePet(pet: Pet
): Request[Either[ResponseError[Exception], Pet], Nothing] =
basicRequest
.method(Method.PUT, uri"$baseUrl/pet")
.contentType("application/json")
@ -131,13 +133,14 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
* @param name Updated name of the pet
* @param status Updated status of the pet
*/
def updatePetWithForm(petId: Long, name: Option[String] = None, status: Option[String] = None): ApiRequestT[Unit] =
def updatePetWithForm(petId: Long, name: Option[String] = None, status: Option[String] = None
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.POST, uri"$baseUrl/pet/${petId}")
.contentType("application/x-www-form-urlencoded")
.body(Map(
"name" -> name,
"status" -> status,
"name" -> name,
"status" -> status
))
.response(asJson[Unit])
@ -149,15 +152,17 @@ class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) {
* @param additionalMetadata Additional data to pass to server
* @param file file to upload
*/
def uploadFile(petId: Long, additionalMetadata: Option[String] = None, file: Option[File] = None): ApiRequestT[ApiResponse] =
def uploadFile(petId: Long, additionalMetadata: Option[String] = None, file: Option[File] = None
): Request[Either[ResponseError[Exception], ApiResponse], Nothing] =
basicRequest
.method(Method.POST, uri"$baseUrl/pet/${petId}/uploadImage")
.contentType("multipart/form-data")
.body(Map(
"additionalMetadata" -> additionalMetadata,
"file" -> file,
))
.multipartBody(Seq(
additionalMetadata.map(multipart("additionalMetadata", _))
,
file.map(multipartFile("file", _))
).flatten)
.response(asJson[ApiResponse])
}

View File

@ -12,20 +12,16 @@
package org.openapitools.client.api
import org.openapitools.client.model.Order
import org.openapitools.client.core._
import alias._
import org.openapitools.client.core.JsonSupport._
import sttp.client._
import sttp.model.Method
object StoreApi {
def apply(baseUrl: String = "http://petstore.swagger.io/v2")(implicit serializer: SttpSerializer) = new StoreApi(baseUrl)
def apply(baseUrl: String = "http://petstore.swagger.io/v2") = new StoreApi(baseUrl)
}
class StoreApi(baseUrl: String)(implicit serializer: SttpSerializer) {
import Helpers._
import serializer._
class StoreApi(baseUrl: String) {
/**
* For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
@ -36,7 +32,8 @@ class StoreApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param orderId ID of the order that needs to be deleted
*/
def deleteOrder(orderId: String): ApiRequestT[Unit] =
def deleteOrder(orderId: String
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.DELETE, uri"$baseUrl/store/order/${orderId}")
.contentType("application/json")
@ -51,11 +48,12 @@ class StoreApi(baseUrl: String)(implicit serializer: SttpSerializer) {
* Available security schemes:
* api_key (apiKey)
*/
def getInventory()(implicit apiKey: ApiKeyValue): ApiRequestT[Map[String, Int]] =
def getInventory(apiKey: String)(
): Request[Either[ResponseError[Exception], Map[String, Int]], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/store/inventory")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.response(asJson[Map[String, Int]])
/**
@ -68,7 +66,8 @@ class StoreApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param orderId ID of pet that needs to be fetched
*/
def getOrderById(orderId: Long): ApiRequestT[Order] =
def getOrderById(orderId: Long
): Request[Either[ResponseError[Exception], Order], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/store/order/${orderId}")
.contentType("application/json")
@ -81,7 +80,8 @@ class StoreApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param order order placed for purchasing the pet
*/
def placeOrder(order: Order): ApiRequestT[Order] =
def placeOrder(order: Order
): Request[Either[ResponseError[Exception], Order], Nothing] =
basicRequest
.method(Method.POST, uri"$baseUrl/store/order")
.contentType("application/json")
@ -89,4 +89,3 @@ class StoreApi(baseUrl: String)(implicit serializer: SttpSerializer) {
.response(asJson[Order])
}

View File

@ -12,20 +12,16 @@
package org.openapitools.client.api
import org.openapitools.client.model.User
import org.openapitools.client.core._
import alias._
import org.openapitools.client.core.JsonSupport._
import sttp.client._
import sttp.model.Method
object UserApi {
def apply(baseUrl: String = "http://petstore.swagger.io/v2")(implicit serializer: SttpSerializer) = new UserApi(baseUrl)
def apply(baseUrl: String = "http://petstore.swagger.io/v2") = new UserApi(baseUrl)
}
class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
import Helpers._
import serializer._
class UserApi(baseUrl: String) {
/**
* This can only be done by the logged in user.
@ -38,11 +34,12 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param user Created user object
*/
def createUser(user: User)(implicit apiKey: ApiKeyValue): ApiRequestT[Unit] =
def createUser(apiKey: String)(user: User
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.POST, uri"$baseUrl/user")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.body(user)
.response(asJson[Unit])
@ -55,11 +52,12 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param user List of user object
*/
def createUsersWithArrayInput(user: Seq[User])(implicit apiKey: ApiKeyValue): ApiRequestT[Unit] =
def createUsersWithArrayInput(apiKey: String)(user: Seq[User]
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.POST, uri"$baseUrl/user/createWithArray")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.body(user)
.response(asJson[Unit])
@ -72,11 +70,12 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param user List of user object
*/
def createUsersWithListInput(user: Seq[User])(implicit apiKey: ApiKeyValue): ApiRequestT[Unit] =
def createUsersWithListInput(apiKey: String)(user: Seq[User]
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.POST, uri"$baseUrl/user/createWithList")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.body(user)
.response(asJson[Unit])
@ -92,11 +91,12 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param username The name that needs to be deleted
*/
def deleteUser(username: String)(implicit apiKey: ApiKeyValue): ApiRequestT[Unit] =
def deleteUser(apiKey: String)(username: String
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.DELETE, uri"$baseUrl/user/${username}")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.response(asJson[Unit])
/**
@ -107,7 +107,8 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
*
* @param username The name that needs to be fetched. Use user1 for testing.
*/
def getUserByName(username: String): ApiRequestT[User] =
def getUserByName(username: String
): Request[Either[ResponseError[Exception], User], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/user/${username}")
.contentType("application/json")
@ -125,7 +126,8 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
* @param username The user name for login
* @param password The password for login in clear text
*/
def loginUser(username: String, password: String): ApiRequestT[String] =
def loginUser(username: String, password: String
): Request[Either[ResponseError[Exception], String], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/user/login?username=$username&password=$password")
.contentType("application/json")
@ -138,11 +140,12 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
* Available security schemes:
* api_key (apiKey)
*/
def logoutUser()(implicit apiKey: ApiKeyValue): ApiRequestT[Unit] =
def logoutUser(apiKey: String)(
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.GET, uri"$baseUrl/user/logout")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.response(asJson[Unit])
/**
@ -158,13 +161,13 @@ class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) {
* @param username name that need to be deleted
* @param user Updated user object
*/
def updateUser(username: String, user: User)(implicit apiKey: ApiKeyValue): ApiRequestT[Unit] =
def updateUser(apiKey: String)(username: String, user: User
): Request[Either[ResponseError[Exception], Unit], Nothing] =
basicRequest
.method(Method.PUT, uri"$baseUrl/user/${username}")
.contentType("application/json")
.header("api_key", apiKey.value)
.header("api_key", apiKey)
.body(user)
.response(asJson[Unit])
}

View File

@ -1,60 +0,0 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.core
import org.json4s._
import sttp.client._
import sttp.model.StatusCode
import org.openapitools.client.api.EnumsSerializers
import sttp.client.json4s.SttpJson4sApi
import sttp.client.monad.MonadError
class SttpSerializer(implicit val format: Formats = DefaultFormats ++ EnumsSerializers.all ++ Serializers.all,
implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization) extends SttpJson4sApi
class HttpException(val statusCode: StatusCode, val statusText: String, val message: String) extends Exception(s"[$statusCode] $statusText: $message")
object Helpers {
// Helper to handle Optional header parameters
implicit class optionalParams(val request: RequestT[Identity, Either[String, String], Nothing]) extends AnyVal {
def header( header: String, optValue: Option[Any]): RequestT[Identity, Either[String, String], Nothing] = {
optValue.map( value => request.header(header, value.toString)).getOrElse(request)
}
}
}
object ApiInvoker {
/**
* Allows request execution without calling apiInvoker.execute(request)
* request.result can be used to get a monad wrapped content.
*
* @param request the apiRequest to be executed
*/
implicit class ApiRequestImprovements[R[_], T](request: RequestT[Identity, Either[ResponseError[Exception], T], Nothing]) {
def result(implicit backend: SttpBackend[R, Nothing, Nothing]): R[T] = {
val responseT = request.send()
val ME: MonadError[R] = backend.responseMonad
ME.flatMap(responseT) {
response =>
response.body match {
case Left(ex) => ME.error[T](new HttpException(response.code, response.statusText, ex.body))
case Right(value) => ME.unit(value)
}
}
}
}
}

View File

@ -0,0 +1,29 @@
package org.openapitools.client.core
import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId}
import java.time.format.DateTimeFormatter
import scala.util.Try
object DateSerializers {
import org.json4s.{Serializer, CustomSerializer, JNull}
import org.json4s.JsonAST.JString
case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( {
case JString(s) =>
Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse
Try(LocalDateTime.parse(s).atZone(ZoneId.systemDefault()).toOffsetDateTime) getOrElse (null)
case JNull => null
}, {
case d: OffsetDateTime =>
JString(d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
}))
case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( {
case JString(s) => LocalDate.parse(s)
case JNull => null
}, {
case d: LocalDate =>
JString(d.format(DateTimeFormatter.ISO_LOCAL_DATE))
}))
def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ LocalDateSerializer :+ DateTimeSerializer
}

View File

@ -9,32 +9,27 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.api
package org.openapitools.client.core
import org.openapitools.client.model._
import org.json4s._
import sttp.client.json4s.SttpJson4sApi
import scala.reflect.ClassTag
object EnumsSerializers {
def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+
object JsonSupport extends SttpJson4sApi {
def enumSerializers: Seq[Serializer[_]] = Seq[Serializer[_]]() :+
new EnumNameSerializer(OrderEnums.Status) :+
new EnumNameSerializer(PetEnums.Status)
private class EnumNameSerializer[E <: Enumeration: ClassTag](enum: E)
extends Serializer[E#Value] {
private class EnumNameSerializer[E <: Enumeration: ClassTag](enum: E) extends Serializer[E#Value] {
import JsonDSL._
val EnumerationClass: Class[E#Value] = classOf[E#Value]
def deserialize(implicit format: Formats):
PartialFunction[(TypeInfo, JValue), E#Value] = {
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), E#Value] = {
case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) =>
json match {
case JString(value) =>
enum.withName(value)
case value =>
throw new MappingException(s"Can't convert $value to $EnumerationClass")
case JString(value) => enum.withName(value)
case value => throw new MappingException(s"Can't convert $value to $EnumerationClass")
}
}
@ -45,7 +40,9 @@ object EnumsSerializers {
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
case i: E#Value => i.toString
}
}
}
implicit val format: Formats = DefaultFormats ++ enumSerializers ++ DateSerializers.all
implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization
}

View File

@ -1,31 +0,0 @@
package org.openapitools.client.core
import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId}
import java.time.format.DateTimeFormatter
import scala.util.Try
import org.json4s.{Serializer, CustomSerializer, JNull}
import org.json4s.JsonAST.JString
object Serializers {
case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( {
case JString(s) =>
Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse
Try(LocalDateTime.parse(s).atZone(ZoneId.systemDefault()).toOffsetDateTime) getOrElse (null)
case JNull => null
}, {
case d: OffsetDateTime =>
JString(d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
}))
case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( {
case JString(s) => LocalDate.parse(s)
case JNull => null
}, {
case d: LocalDate =>
JString(d.format(DateTimeFormatter.ISO_LOCAL_DATE))
}))
def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ LocalDateSerializer :+ DateTimeSerializer
}

View File

@ -0,0 +1,18 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.core
final case class BasicCredentials(user: String, password: String)
final case class BearerToken(token: String)
final case class ApiKeyValue(value: String)

View File

@ -1,58 +0,0 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.core
import sttp.client.{Identity, RequestT, ResponseError}
/**
* This trait needs to be added to any model defined by the api.
*/
trait ApiModel
/**
* Sttp type aliases
*/
object alias {
type ApiRequestT[T] = RequestT[Identity, Either[ResponseError[Exception], T], Nothing]
}
/**
* Single trait defining a credential that can be transformed to a paramName / paramValue tupple
*/
sealed trait Credentials {
def asQueryParam: Option[(String, String)] = None
}
sealed case class BasicCredentials(user: String, password: String) extends Credentials
sealed case class BearerToken(token: String) extends Credentials
sealed case class ApiKeyCredentials(key: ApiKeyValue, keyName: String, location: ApiKeyLocation) extends Credentials {
override def asQueryParam: Option[(String, String)] = location match {
case ApiKeyLocations.QUERY => Some((keyName, key.value))
case _ => None
}
}
sealed case class ApiKeyValue(value: String)
sealed trait ApiKeyLocation
object ApiKeyLocations {
case object QUERY extends ApiKeyLocation
case object HEADER extends ApiKeyLocation
case object COOKIE extends ApiKeyLocation
}

View File

@ -11,7 +11,6 @@
*/
package org.openapitools.client.model
import org.openapitools.client.core.ApiModel
/**
* An uploaded response
@ -21,6 +20,6 @@ case class ApiResponse(
code: Option[Int] = None,
`type`: Option[String] = None,
message: Option[String] = None
) extends ApiModel
)

View File

@ -11,7 +11,6 @@
*/
package org.openapitools.client.model
import org.openapitools.client.core.ApiModel
/**
* Pet category
@ -20,6 +19,6 @@ import org.openapitools.client.core.ApiModel
case class Category(
id: Option[Long] = None,
name: Option[String] = None
) extends ApiModel
)

View File

@ -11,13 +11,12 @@
*/
package org.openapitools.client.model
import org.openapitools.client.core.ApiModel
case class InlineObject(
/* Updated name of the pet */
name: Option[String] = None,
/* Updated status of the pet */
status: Option[String] = None
) extends ApiModel
)

View File

@ -12,13 +12,12 @@
package org.openapitools.client.model
import java.io.File
import org.openapitools.client.core.ApiModel
case class InlineObject1(
/* Additional data to pass to server */
additionalMetadata: Option[String] = None,
/* file to upload */
file: Option[File] = None
) extends ApiModel
)

View File

@ -12,7 +12,6 @@
package org.openapitools.client.model
import java.time.OffsetDateTime
import org.openapitools.client.core.ApiModel
/**
* Pet Order
@ -26,7 +25,7 @@ case class Order(
/* Order Status */
status: Option[OrderEnums.Status] = None,
complete: Option[Boolean] = None
) extends ApiModel
)
object OrderEnums {

View File

@ -11,7 +11,6 @@
*/
package org.openapitools.client.model
import org.openapitools.client.core.ApiModel
/**
* a Pet
@ -25,7 +24,7 @@ case class Pet(
tags: Option[Seq[Tag]] = None,
/* pet status in the store */
status: Option[PetEnums.Status] = None
) extends ApiModel
)
object PetEnums {

View File

@ -11,7 +11,6 @@
*/
package org.openapitools.client.model
import org.openapitools.client.core.ApiModel
/**
* Pet Tag
@ -20,6 +19,6 @@ import org.openapitools.client.core.ApiModel
case class Tag(
id: Option[Long] = None,
name: Option[String] = None
) extends ApiModel
)

View File

@ -11,7 +11,6 @@
*/
package org.openapitools.client.model
import org.openapitools.client.core.ApiModel
/**
* a User
@ -27,6 +26,6 @@ case class User(
phone: Option[String] = None,
/* User Status */
userStatus: Option[Int] = None
) extends ApiModel
)