forked from loafle/openapi-generator-original
Improve sttpOpenApiClient generator (#6684)
Co-authored-by: eugeniyk <keatrance@gmail.com>
This commit is contained in:
parent
dae329d8e1
commit
323cd38b5c
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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}}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
76
modules/openapi-generator/src/main/resources/scala-sttp/dateSerializers.mustache
vendored
Normal file
76
modules/openapi-generator/src/main/resources/scala-sttp/dateSerializers.mustache
vendored
Normal 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}}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
53
modules/openapi-generator/src/main/resources/scala-sttp/jsonSupport.mustache
vendored
Normal file
53
modules/openapi-generator/src/main/resources/scala-sttp/jsonSupport.mustache
vendored
Normal 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}}
|
@ -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}}
|
||||
|
@ -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 {
|
||||
|
16
modules/openapi-generator/src/main/resources/scala-sttp/paramMultipartCreation.mustache
vendored
Normal file
16
modules/openapi-generator/src/main/resources/scala-sttp/paramMultipartCreation.mustache
vendored
Normal 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}}
|
1
modules/openapi-generator/src/main/resources/scala-sttp/project/build.properties.mustache
vendored
Normal file
1
modules/openapi-generator/src/main/resources/scala-sttp/project/build.properties.mustache
vendored
Normal file
@ -0,0 +1 @@
|
||||
sbt.version=1.3.12
|
@ -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
|
||||
|
||||
}
|
@ -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
|
||||
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
@ -0,0 +1 @@
|
||||
sbt.version=1.3.12
|
@ -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])
|
||||
|
||||
}
|
||||
|
||||
|
@ -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])
|
||||
|
||||
}
|
||||
|
||||
|
@ -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])
|
||||
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
||||
}
|
@ -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)
|
@ -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
|
||||
|
||||
}
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user