diff --git a/.github/workflows/samples-scala.yaml b/.github/workflows/samples-scala.yaml index 7e69173a529..584cb7ea3b1 100644 --- a/.github/workflows/samples-scala.yaml +++ b/.github/workflows/samples-scala.yaml @@ -22,6 +22,7 @@ jobs: # clients - 'samples/client/petstore/java/okhttp-gson' - samples/client/petstore/scalaz + - samples/client/petstore/scala-pekko #- samples/client/petstore/scala-sttp # won't pass while the same tests in circleci pass # servers - samples/server/petstore/scala-lagom-server diff --git a/bin/configs/scala-pekko.yaml b/bin/configs/scala-pekko.yaml new file mode 100644 index 00000000000..aeba7586533 --- /dev/null +++ b/bin/configs/scala-pekko.yaml @@ -0,0 +1,6 @@ +generatorName: scala-pekko +outputDir: samples/client/petstore/scala-pekko +inputSpec: modules/openapi-generator/src/test/resources/3_0/scala-pekko/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/scala-pekko-client +additionalProperties: + artifactId: scala-pekko-petstore-client diff --git a/docs/generators.md b/docs/generators.md index d07a0bf2abb..c9e07259a0e 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -59,6 +59,7 @@ The following generators are available: * [rust](generators/rust.md) * [scala-akka](generators/scala-akka.md) * [scala-gatling](generators/scala-gatling.md) +* [scala-pekko](generators/scala-pekko.md) * [scala-sttp](generators/scala-sttp.md) * [scala-sttp4 (beta)](generators/scala-sttp4.md) * [scalaz](generators/scalaz.md) diff --git a/docs/generators/README.md b/docs/generators/README.md index 648de9673e7..adf500bdace 100644 --- a/docs/generators/README.md +++ b/docs/generators/README.md @@ -45,6 +45,7 @@ The following generators are available: * [ruby](ruby.md) * [rust](rust.md) * [scala-akka](scala-akka.md) +* [scala-pekko](scala-pekko.md) * [scala-gatling](scala-gatling.md) * [scalaz](scalaz.md) * [swift4](swift4.md) diff --git a/docs/generators/scala-pekko.md b/docs/generators/scala-pekko.md new file mode 100644 index 00000000000..ebc33bb6987 --- /dev/null +++ b/docs/generators/scala-pekko.md @@ -0,0 +1,246 @@ +--- +title: Documentation for the scala-pekko Generator +--- + +## METADATA + +| Property | Value | Notes | +| -------- | ----- | ----- | +| generator name | scala-pekko | pass this to the generate command after -g | +| generator stability | STABLE | | +| generator type | CLIENT | | +| generator language | Scala | | +| generator default templating engine | mustache | | +| helpTxt | Generates a Scala client library (beta) base on pekko/Spray. | | + +## CONFIG OPTIONS +These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details. + +| Option | Description | Values | Default | +| ------ | ----------- | ------ | ------- | +|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false| +|apiPackage|package for generated api classes| |null| +|dateLibrary|Option. Date library to use|
**joda**
Joda (for legacy app)
**java8**
Java 8 native JSR310 (preferred for JDK 1.8+)
|java8| +|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true| +|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| +|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|
**false**
No changes to the enum's are made, this is the default option.
**true**
With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.
|false| +|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|
**true**
The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.
**false**
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.
|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| +|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| + +## IMPORT MAPPING + +| Type/Alias | Imports | +| ---------- | ------- | +|Array|java.util.List| +|ArrayList|java.util.ArrayList| +|BigDecimal|java.math.BigDecimal| +|Date|java.util.Date| +|DateTime|org.joda.time.*| +|File|java.io.File| +|HashMap|java.util.HashMap| +|ListBuffer|scala.collection.mutable.ListBuffer| +|ListSet|scala.collection.immutable.ListSet| +|LocalDate|org.joda.time.*| +|LocalDateTime|org.joda.time.*| +|LocalTime|org.joda.time.*| +|Timestamp|java.sql.Timestamp| +|URI|java.net.URI| +|UUID|java.util.UUID| + + +## INSTANTIATION TYPES + +| Type/Alias | Instantiated By | +| ---------- | --------------- | +|array|ListBuffer| +|map|Map| +|set|Set| + + +## LANGUAGE PRIMITIVES + + + +## RESERVED WORDS + + + +## FEATURE SET + + +### Client Modification Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasePath|✓|ToolingExtension +|Authorizations|✗|ToolingExtension +|UserAgent|✓|ToolingExtension +|MockServer|✗|ToolingExtension + +### Data Type Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Custom|✗|OAS2,OAS3 +|Int32|✓|OAS2,OAS3 +|Int64|✓|OAS2,OAS3 +|Float|✓|OAS2,OAS3 +|Double|✓|OAS2,OAS3 +|Decimal|✓|ToolingExtension +|String|✓|OAS2,OAS3 +|Byte|✓|OAS2,OAS3 +|Binary|✓|OAS2,OAS3 +|Boolean|✓|OAS2,OAS3 +|Date|✓|OAS2,OAS3 +|DateTime|✓|OAS2,OAS3 +|Password|✓|OAS2,OAS3 +|File|✓|OAS2 +|Uuid|✗| +|Array|✓|OAS2,OAS3 +|Null|✗|OAS3 +|AnyType|✗|OAS2,OAS3 +|Object|✓|OAS2,OAS3 +|Maps|✓|ToolingExtension +|CollectionFormat|✓|OAS2 +|CollectionFormatMulti|✓|OAS2 +|Enum|✓|OAS2,OAS3 +|ArrayOfEnum|✓|ToolingExtension +|ArrayOfModel|✓|ToolingExtension +|ArrayOfCollectionOfPrimitives|✓|ToolingExtension +|ArrayOfCollectionOfModel|✓|ToolingExtension +|ArrayOfCollectionOfEnum|✓|ToolingExtension +|MapOfEnum|✓|ToolingExtension +|MapOfModel|✓|ToolingExtension +|MapOfCollectionOfPrimitives|✓|ToolingExtension +|MapOfCollectionOfModel|✓|ToolingExtension +|MapOfCollectionOfEnum|✓|ToolingExtension + +### Documentation Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Readme|✓|ToolingExtension +|Model|✓|ToolingExtension +|Api|✓|ToolingExtension + +### Global Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Host|✓|OAS2,OAS3 +|BasePath|✓|OAS2,OAS3 +|Info|✓|OAS2,OAS3 +|Schemes|✗|OAS2,OAS3 +|PartialSchemes|✓|OAS2,OAS3 +|Consumes|✓|OAS2 +|Produces|✓|OAS2 +|ExternalDocumentation|✓|OAS2,OAS3 +|Examples|✓|OAS2,OAS3 +|XMLStructureDefinitions|✗|OAS2,OAS3 +|MultiServer|✗|OAS3 +|ParameterizedServer|✗|OAS3 +|ParameterStyling|✗|OAS3 +|Callbacks|✗|OAS3 +|LinkObjects|✗|OAS3 + +### Parameter Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Path|✓|OAS2,OAS3 +|Query|✓|OAS2,OAS3 +|Header|✓|OAS2,OAS3 +|Body|✓|OAS2 +|FormUnencoded|✓|OAS2 +|FormMultipart|✓|OAS2 +|Cookie|✗|OAS3 + +### Schema Support Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|Simple|✓|OAS2,OAS3 +|Composite|✓|OAS2,OAS3 +|Polymorphism|✗|OAS2,OAS3 +|Union|✗|OAS3 +|allOf|✗|OAS2,OAS3 +|anyOf|✗|OAS3 +|oneOf|✗|OAS3 +|not|✗|OAS3 + +### Security Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|BasicAuth|✓|OAS2,OAS3 +|ApiKey|✓|OAS2,OAS3 +|OpenIDConnect|✗|OAS3 +|BearerToken|✓|OAS3 +|OAuth2_Implicit|✗|OAS2,OAS3 +|OAuth2_Password|✗|OAS2,OAS3 +|OAuth2_ClientCredentials|✗|OAS2,OAS3 +|OAuth2_AuthorizationCode|✗|OAS2,OAS3 +|SignatureAuth|✗|OAS3 +|AWSV4Signature|✗|ToolingExtension + +### Wire Format Feature +| Name | Supported | Defined By | +| ---- | --------- | ---------- | +|JSON|✓|OAS2,OAS3 +|XML|✓|OAS2,OAS3 +|PROTOBUF|✗|ToolingExtension +|Custom|✓|OAS2,OAS3 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPekkoClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPekkoClientCodegen.java new file mode 100644 index 00000000000..f277ab617fb --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPekkoClientCodegen.java @@ -0,0 +1,359 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * Copyright 2018 SmartBear Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.languages; + +import com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Template; +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 org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.*; +import org.openapitools.codegen.meta.features.*; +import org.openapitools.codegen.model.ModelMap; +import org.openapitools.codegen.model.OperationMap; +import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.utils.ModelUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +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 ScalaPekkoClientCodegen extends AbstractScalaCodegen implements CodegenConfig { + protected String mainPackage = "org.openapitools.client"; + protected String groupId = "org.openapitools"; + protected String artifactId = "openapi-client"; + protected String artifactVersion = "1.0.0"; + protected String resourcesFolder = "src/main/resources"; + protected String apiDocPath = "docs/"; + protected String modelDocPath = "docs/"; + protected String configKey = "apiRequest"; + protected int defaultTimeoutInMs = 5000; + protected String configKeyPath = mainPackage; + protected boolean registerNonStandardStatusCodes = true; + protected boolean renderJavadoc = true; + protected boolean removeOAuthSecurities = true; + + @SuppressWarnings("hiding") + protected final Logger LOGGER = LoggerFactory.getLogger(ScalaPekkoClientCodegen.class); + + public ScalaPekkoClientCodegen() { + super(); + + 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-pekko"; + modelTemplateFiles.put("model.mustache", ".scala"); + apiTemplateFiles.put("api.mustache", ".scala"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + embeddedTemplateDir = templateDir = "scala-pekko-client"; + apiPackage = mainPackage + ".api"; + modelPackage = mainPackage + ".model"; + invokerPackage = mainPackage + ".core"; + + setReservedWordsLowerCase( + Arrays.asList( + "abstract", "case", "catch", "class", "def", "do", "else", "extends", + "false", "final", "finally", "for", "forSome", "if", "implicit", + "import", "lazy", "match", "new", "null", "object", "override", "package", + "private", "protected", "return", "sealed", "super", "this", "throw", + "trait", "try", "true", "type", "val", "var", "while", "with", "yield") + ); + + additionalProperties.put(CodegenConstants.GROUP_ID, groupId); + additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); + additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); + additionalProperties.put("configKey", configKey); + additionalProperties.put("configKeyPath", configKeyPath); + additionalProperties.put("defaultTimeout", defaultTimeoutInMs); + 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"); + importMapping.put("BigDecimal", "java.math.BigDecimal"); + + 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", "BigDecimal"); + typeMapping.put("decimal", "BigDecimal"); + + instantiationTypes.put("array", "ListBuffer"); + instantiationTypes.put("map", "Map"); + + cliOptions.add(new CliOption("mainPackage", "Top-level package name, which defines 'apiPackage', 'modelPackage', 'invokerPackage'").defaultValue("org.openapitools.client")); + } + + @Override + public void processOpts() { + super.processOpts(); + if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { + this.setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE)); + } + if (additionalProperties.containsKey("mainPackage")) { + setMainPackage((String) additionalProperties.get("mainPackage")); + additionalProperties.replace("configKeyPath", this.configKeyPath); + if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) { + apiPackage = mainPackage + ".api"; + additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage); + } + if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) { + modelPackage = mainPackage + ".model"; + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage); + } + if (!additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { + invokerPackage = mainPackage + ".core"; + } + } + additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage); + // make api and model doc path available in mustache template + additionalProperties.put("apiDocPath", apiDocPath); + additionalProperties.put("modelDocPath", modelDocPath); + + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); + supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); + supportingFiles.add(new SupportingFile("reference.mustache", resourcesFolder, "reference.conf")); + final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator); + supportingFiles.add(new SupportingFile("apiRequest.mustache", invokerFolder, "ApiRequest.scala")); + supportingFiles.add(new SupportingFile("apiInvoker.mustache", invokerFolder, "ApiInvoker.scala")); + supportingFiles.add(new SupportingFile("requests.mustache", invokerFolder, "requests.scala")); + supportingFiles.add(new SupportingFile("apiSettings.mustache", invokerFolder, "ApiSettings.scala")); + final String apiFolder = (sourceFolder + File.separator + apiPackage).replace(".", File.separator); + supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); + supportingFiles.add(new SupportingFile("enumsSerializers.mustache", apiFolder, "EnumsSerializers.scala")); + supportingFiles.add(new SupportingFile("serializers.mustache", invokerFolder, "Serializers.scala")); + } + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public String getName() { + return "scala-pekko"; + } + + @Override + public String getHelp() { + return "Generates a Scala client library (beta) base on pekko/Spray."; + } + + @Override + public String escapeReservedWord(String name) { + if (this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return "`" + name + "`"; + } + + @Override + public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { + if (registerNonStandardStatusCodes) { + try { + OperationMap opsMap = objs.getOperations(); + HashSet unknownCodes = new HashSet<>(); + for (CodegenOperation operation : opsMap.getOperation()) { + 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 fromSecurity(Map schemes) { + final List codegenSecurities = super.fromSecurity(schemes); + if (!removeOAuthSecurities) { + return codegenSecurities; + } + + // Remove OAuth securities + codegenSecurities.removeIf(security -> security.isOAuth); + if (codegenSecurities.isEmpty()) { + return null; + } + return codegenSecurities; + } + + @Override + public String toParamName(String name) { + // obtain the name from parameterNameMapping directly if provided + if (parameterNameMapping.containsKey(name)) { + return parameterNameMapping.get(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(ModelUtils.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; + } + } + + + 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 class EnumEntryLambda extends CustomLambda { + @Override + public String formatFragment(String fragment) { + return formatIdentifier(fragment, true); + } + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + public void setMainPackage(String mainPackage) { + this.configKeyPath = this.mainPackage = mainPackage; + } + + @Override + public String apiDocFileFolder() { + return (outputFolder + File.separator + apiDocPath).replace('/', File.separatorChar); + } + + @Override + public String modelDocFileFolder() { + return (outputFolder + File.separator + modelDocPath).replace('/', File.separatorChar); + } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index c51d01c26ec..ffb879c5794 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -114,6 +114,7 @@ org.openapitools.codegen.languages.RustClientCodegen org.openapitools.codegen.languages.RustServerCodegen org.openapitools.codegen.languages.ScalatraServerCodegen org.openapitools.codegen.languages.ScalaAkkaClientCodegen +org.openapitools.codegen.languages.ScalaPekkoClientCodegen org.openapitools.codegen.languages.ScalaAkkaHttpServerCodegen org.openapitools.codegen.languages.ScalaFinchServerCodegen org.openapitools.codegen.languages.ScalaGatlingCodegen diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/README.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/README.mustache new file mode 100644 index 00000000000..577f7188ca3 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/README.mustache @@ -0,0 +1,171 @@ +# {{artifactId}} + +{{appName}} +- API version: {{appVersion}} +{{^hideGenerationTimestamp}} + - Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} + +{{{appDescriptionWithNewLines}}} + +{{#infoUrl}} + For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)* + +## Requirements + +Building the API client library requires: +1. Java 1.7+ +2. Maven/Gradle/SBT + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +mvn clean install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn clean deploy +``` + +Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + {{{groupId}}} + {{{artifactId}}} + {{{artifactVersion}}} + compile + +``` + +### Gradle users + +Add this dependency to your project's build file: + +```groovy +compile "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}" +``` + +### SBT users + +```scala +libraryDependencies += "{{{groupId}}}" % "{{{artifactId}}}" % "{{{artifactVersion}}}" +``` + +## Getting Started + +Please follow the [installation](#installation) instruction and execute the following Java code: + +```scala +{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}} +import {{invokerPackage}}._ +import {{modelPackage}}._ +import {{{package}}}.{{{classname}}} + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object {{{classname}}}Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher{{#hasAuthMethods}} + {{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + // Configure HTTP basic authorization: {{{name}}} + implicit val {{{name}}}: BasicCredentials = BasicCredentials("YOUR USERNAME", "YOUR PASSWORD"){{/isBasicBasic}}{{#isBasicBearer}} + // Configure HTTP bearer authorization: {{{name}}} + implicit val {{{name}}}: BearerToken = BearerToken("BEARER TOKEN"){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + implicit val {{{name}}}: ApiKeyValue = ApiKeyValue("YOUR API KEY"){{/isApiKey}} + {{/authMethods}} + {{/hasAuthMethods}} + + // Create invoker to execute requests + val apiInvoker = ApiInvoker() + val apiInstance = {{{classname}}}("{{{basePath}}}"){{#allParams}} + val {{{paramName}}}: {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} + {{/allParams}} + + val request = apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(org.openapitools.client.core.ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}"){{#returnType}} + System.out.println(s"Response body: $content"){{/returnType}} + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}") + exception.printStackTrace(); + } + +} +{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}} +``` + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}{{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation for Models + +{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md) +{{/model}}{{/models}} + + +## Documentation for Authorization + +{{^authMethods}}Endpoints do not require authorization.{{/authMethods}} +{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}} +{{#authMethods}} + +### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasicBasic}}- **Type**: HTTP basic authentication +{{/isBasicBasic}} +{{#isBasicBearer}}- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}} +{{/isBasicBearer}} +{{#isHttpSignature}}- **Type**: HTTP signature authentication +{{/isHttpSignature}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} + +## Author + +{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}} +{{/-last}}{{/apis}}{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/api.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/api.mustache new file mode 100644 index 00000000000..6da068e37a5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/api.mustache @@ -0,0 +1,52 @@ +{{>licenseInfo}} +package {{package}} + +{{#imports}} +import {{import}} +{{/imports}} +import {{invokerPackage}}._ +import {{invokerPackage}}.CollectionFormats._ +import {{invokerPackage}}.ApiKeyLocations._ + +{{#operations}} +object {{classname}} { + + def apply(baseUrl: String = "{{{basePath}}}") = new {{classname}}(baseUrl) +} + +class {{classname}}(baseUrl: String) { + +{{#operation}} +{{#javadocRenderer}} +{{>javadoc}} +{{/javadocRenderer}} + def {{operationId}}({{>methodParameters}}): ApiRequest[{{>operationReturnType}}] = + ApiRequest[{{>operationReturnType}}](ApiMethods.{{httpMethod.toUpperCase}}, baseUrl, "{{{path}}}", {{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{^consumes}}"application/json"{{/consumes}}) + {{#authMethods}}{{#isApiKey}}.withApiKey(apiKey, "{{keyParamName}}", {{#isKeyInQuery}}QUERY{{/isKeyInQuery}}{{#isKeyInHeader}}HEADER{{/isKeyInHeader}}{{#isKeyInCookie}}COOKIE{{/isKeyInCookie}}) + {{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}.withCredentials(basicAuth){{/isBasicBasic}}{{#isBasicBearer}}.withCredentials(bearerToken){{/isBasicBearer}}{{/isBasic}}{{/authMethods}}{{#bodyParam}}.withBody({{paramName}}) + {{/bodyParam}}{{#formParams}}.withFormParam({{>paramCreation}}) + {{/formParams}}{{#queryParams}}.withQueryParam({{>paramCreation}}) + {{/queryParams}}{{#pathParams}}.withPathParam({{>paramCreation}}) + {{/pathParams}}{{#headerParams}}.withHeaderParam({{>paramCreation}}) + {{/headerParams}}{{#responses}}{{^isWildcard}}{{#dataType}}.with{{>responseState}}Response[{{dataType}}]({{code}}) + {{/dataType}}{{^dataType}}.with{{>responseState}}Response[Unit]({{code}}) + {{/dataType}}{{/isWildcard}}{{/responses}}{{#responses}}{{#isWildcard}}{{#dataType}}.withDefault{{>responseState}}Response[{{dataType}}] + {{/dataType}}{{^dataType}}.withDefault{{>responseState}}Response[Unit] + {{/dataType}}{{/isWildcard}}{{/responses}} + {{^responseHeaders.isEmpty}} + object {{#fnCapitalize}}{{operationId}}{{/fnCapitalize}}Headers { + {{#responseHeaders}} + def {{{name}}}(r: ApiReturnWithHeaders) = r.get{{^isContainer}}{{baseType}}{{/isContainer}}{{#isContainer}}String{{/isContainer}}Header("{{baseName}}") + {{/responseHeaders}} + } + {{/responseHeaders.isEmpty}} + +{{/operation}} + + {{#unknownStatusCodes}} + ApiInvoker.addCustomStatusCode({{{value}}}, isSuccess = false) + {{/unknownStatusCodes}} + +} + +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/apiInvoker.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/apiInvoker.mustache new file mode 100644 index 00000000000..065e2296d0a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/apiInvoker.mustache @@ -0,0 +1,285 @@ +{{>licenseInfo}} +package {{invokerPackage}} + +import java.io.File + +import org.apache.pekko.actor.ActorSystem +import org.apache.pekko.http.scaladsl.Http +import org.apache.pekko.http.scaladsl.coding._ +import org.apache.pekko.http.scaladsl.model.Multipart.FormData.BodyPart +import org.apache.pekko.http.scaladsl.model.Uri.Query +import org.apache.pekko.http.scaladsl.model._ +import org.apache.pekko.http.scaladsl.model.headers._ +import org.apache.pekko.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } +import org.apache.pekko.stream.Materializer +import org.apache.pekko.stream.scaladsl.Source +import org.apache.pekko.util.{ ByteString, Timeout } +import com.github.pjfanning.pekkohttpjson4s.Json4sSupport +import org.json4s._ +import org.json4s.jackson.JsonMethods._ +import org.json4s.jackson.Serialization + +import scala.collection.immutable +import scala.concurrent.{ ExecutionContext, ExecutionContextExecutor, Future } +import scala.reflect.ClassTag + +object ApiInvoker { + + def apply()(implicit system: ActorSystem): ApiInvoker = + apply(DefaultFormats ++ Serializers.all) + + def apply(serializers: Iterable[Serializer[_]])(implicit system: ActorSystem): ApiInvoker = + apply(DefaultFormats ++ Serializers.all ++ serializers) + + def apply(formats: Formats)(implicit system: ActorSystem): ApiInvoker = new ApiInvoker(formats) + + + /** + * Allows request execution without calling apiInvoker.execute(request) + * request.response can be used to get a future of the ApiResponse generated. + * request.result can be used to get a future of the expected ApiResponse content. If content doesn't match, a + * Future will fail with a ClassCastException + * + * @param request the apiRequest to be executed + */ + implicit class ApiRequestImprovements[T: Manifest](request: ApiRequest[T]) { + + def response(invoker: ApiInvoker)(implicit ec: ExecutionContext, system: ActorSystem): Future[ApiResponse[T]] = + response(ec, system, invoker) + + def response(implicit ec: ExecutionContext, system: ActorSystem, invoker: ApiInvoker): Future[ApiResponse[T]] = + invoker.execute(request) + + def result[U <: T](implicit c: ClassTag[U], ec: ExecutionContext, system: ActorSystem, invoker: ApiInvoker): Future[U] = + invoker.execute(request).map(_.content).mapTo[U] + + } + + /** + * Allows transformation from ApiMethod to spray HttpMethods + * + * @param method the ApiMethod to be converted + */ + implicit class ApiMethodExtensions(val method: ApiMethod) { + def toAkkaHttpMethod: HttpMethod = HttpMethods.getForKey(method.value).getOrElse(HttpMethods.GET) + } + +} + +trait UnitJSONSupport { + +} + +class ApiInvoker(formats: Formats)(implicit system: ActorSystem) extends CustomContentTypes with Json4sSupport { + + import {{{invokerPackage}}}.ApiInvoker._ + import {{{invokerPackage}}}.ParametersMap._ + + implicit val ec: ExecutionContextExecutor = system.dispatcher + implicit val jsonFormats: Formats = formats + + protected val settings: ApiSettings = ApiSettings(system) + + private implicit val materializer: Materializer = Materializer(system) + private implicit val serialization: Serialization = jackson.Serialization + + + private val http = Http() + + val CompressionFilter: HttpMessage => Boolean = (msg: HttpMessage) => + Seq( + { _: HttpMessage => settings.compressionEnabled }, + Encoder.DefaultFilter, + (message: HttpMessage) => { + val long = message.entity().getContentLengthOption() + if (long.isPresent) long.getAsLong > settings.compressionSizeThreshold else true + } + ) + .map(f => f(msg)) + .forall(identity) + + + private def addAuthentication(credentialsSeq: Seq[Credentials]) = { + request: HttpRequest => + credentialsSeq.foldLeft(request) { + case (req, BasicCredentials(login, password)) => + req.addHeader(Authorization(BasicHttpCredentials(login, password))) + case (req, ApiKeyCredentials(keyValue, keyName, ApiKeyLocations.HEADER)) => + req.addHeader(RawHeader(keyName, keyValue.value)) + case (req, BearerToken(token)) => + req.addHeader(RawHeader("Authorization", s"Bearer $token")) + case (req, _) => req + } + } + + private def headers(headers: Map[String, Any]): immutable.Seq[HttpHeader] = + headers.asFormattedParams + .map { case (name, value) => RawHeader(name, value.toString) } + .to(immutable.Seq) + + + private def bodyPart(name: String, value: Any): BodyPart = { + value match { + case f: File => + BodyPart.fromFile( + name, + ContentType(MediaTypes.`application/octet-stream`), + f, + f.length().toInt + ) + case v: String => + BodyPart.Strict(name, v.toString) + case NumericValue(v) => + BodyPart.Strict(name, v.toString) + case m: ApiModel => + BodyPart.Strict(name, Serialization.write(m)) + } + } + + + private def formDataContent(request: ApiRequest[_]) = { + val params = request.formParams.asFormattedParams + if (params.isEmpty) + None + else + Some( + normalizedContentType(request.contentType).mediaType match { + case MediaTypes.`multipart/form-data` => + Multipart.FormData(Source(params.toList.map { case (name, value) => bodyPart(name, value) })) + case MediaTypes.`application/x-www-form-urlencoded` => + FormData(params.view.mapValues(_.toString).toMap) + case _: MediaType => // Default : application/x-www-form-urlencoded. + FormData(params.view.mapValues(_.toString).toMap) + } + ) + } + + private def bodyContent(request: ApiRequest[_]): Option[Any] = request + .bodyParam + .map(Extraction.decompose) + .map(compact) + + private def createRequest(uri: Uri, request: ApiRequest[_]): HttpRequest = { + val httpRequest = request.method.toAkkaHttpMethod match { + case m@(HttpMethods.GET | HttpMethods.DELETE) => HttpRequest(m, uri) + case m@(HttpMethods.POST | HttpMethods.PUT | HttpMethods.PATCH) => + formDataContent(request) orElse bodyContent(request) match { + case Some(c: FormData) => + HttpRequest(m, uri, entity = c.toEntity) + case Some(c: Multipart.FormData) => + HttpRequest(m, uri, entity = c.toEntity) + case Some(c: String) => + HttpRequest(m, uri, entity = HttpEntity(normalizedContentType(request.contentType), ByteString(c))) + case _ => + HttpRequest(m, uri, entity = HttpEntity(normalizedContentType(request.contentType), ByteString(""))) + } + case m: HttpMethod => HttpRequest(m, uri) + } + + addAuthentication(request.credentials)( + httpRequest.withHeaders(headers(request.headerParams)) + ) + } + + def makeQuery(r: ApiRequest[_]): Query = { + r.credentials.foldLeft(r.queryParams) { + case (params, ApiKeyCredentials(key, keyName, ApiKeyLocations.QUERY)) => + params + (keyName -> key.value) + case (params, _) => params + }.asFormattedParams + .view + .mapValues(_.toString) + .toMap + .foldRight[Query](Uri.Query.Empty) { + case ((name, value), acc) => acc.+:(name, value) + } + } + + def makeUri(r: ApiRequest[_]): Uri = { + val opPath = r.operationPath.replaceAll("\\{format\\}", "json") + val opPathWithParams = r.pathParams.asFormattedParams + .view + .mapValues(_.toString) + .toMap + .foldLeft(opPath) { + case (path, (name, value)) => path.replaceAll(s"\\{$name\\}", value) + } + val query = makeQuery(r) + + Uri(r.basePath + opPathWithParams).withQuery(query) + } + + def execute[T: Manifest](r: ApiRequest[T]): Future[ApiResponse[T]] = { + implicit val timeout: Timeout = settings.connectionTimeout + + val request = createRequest(makeUri(r), r) + + http + .singleRequest(request) + .map { response => + val decoder: Decoder with Decoder = response.encoding match { + case HttpEncodings.gzip => + Coders.Gzip + case HttpEncodings.deflate => + Coders.Deflate + case HttpEncodings.identity => + Coders.NoCoding + case HttpEncoding(encoding) => + throw new IllegalArgumentException(s"Unsupported encoding: $encoding") + } + + decoder.decodeMessage(response) + } + .flatMap(unmarshallApiResponse(r)) + } + + def unmarshallApiResponse[T: Manifest](request: ApiRequest[T])(response: HttpResponse): Future[ApiResponse[T]] = { + def responseForState[V](state: ResponseState, value: V): ApiResponse[V] = { + state match { + case ResponseState.Success => + ApiResponse(response.status.intValue, value, response.headers.map(header => (header.name, header.value)).toMap) + case ResponseState.Error => + throw ApiError( + response.status.intValue, + "Error response received", + Some(value), + headers = response.headers.map(header => (header.name, header.value)).toMap + ) + } + } + val mf = implicitly(manifest[T]) + request + .responseForCode(response.status.intValue) match { + case Some((Manifest.Unit, state: ResponseState)) => + Future(responseForState(state, ()).asInstanceOf[ApiResponse[T]]) + case Some((manifest, state: ResponseState)) if manifest == mf => + implicit val m: Unmarshaller[HttpEntity, T] = unmarshaller[T](mf, serialization, formats) + Unmarshal(response.entity) + .to[T] + .recoverWith { + case e => throw ApiError(response.status.intValue, s"Unable to unmarshall content to [$manifest]", Some(response.entity.toString), e) + } + .map(value => responseForState(state, value)) + case None | Some(_) => + Future.failed(ApiError(response.status.intValue, "Unexpected response code", Some(response.entity.toString))) + } + } +} + +sealed trait CustomContentTypes { + + protected def normalizedContentType(original: String): ContentType = + ContentType(parseContentType(original).mediaType, () => HttpCharsets.`UTF-8`) + + protected def parseContentType(contentType: String): ContentType = { + + ContentType.parse(contentType) match { + case Right(ct: ContentType) => + ct + case Left(error: List[ErrorInfo]) => + throw new IllegalArgumentException( + s"Error converting '$contentType' to a ContentType header: '${error.map(_.summary).mkString(", ")}'" + ) + } + } +} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/apiRequest.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/apiRequest.mustache new file mode 100644 index 00000000000..10be69afa95 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/apiRequest.mustache @@ -0,0 +1,57 @@ +{{>licenseInfo}} +package {{invokerPackage}} + +sealed trait ResponseState + +object ResponseState { + + case object Success extends ResponseState + + case object Error extends ResponseState + +} + +case class ApiRequest[U]( + // required fields + method: ApiMethod, + basePath: String, + operationPath: String, + contentType: String, + + // optional fields + responses: Map[Int, (Manifest[_], ResponseState)] = Map.empty, + bodyParam: Option[Any] = None, + formParams: Map[String, Any] = Map.empty, + pathParams: Map[String, Any] = Map.empty, + queryParams: Map[String, Any] = Map.empty, + headerParams: Map[String, Any] = Map.empty, + credentials: Seq[Credentials] = List.empty) { + + def withCredentials(cred: Credentials): ApiRequest[U] = copy[U](credentials = credentials :+ cred) + + def withApiKey(key: ApiKeyValue, keyName: String, location: ApiKeyLocation): ApiRequest[U] = withCredentials(ApiKeyCredentials(key, keyName, location)) + + def withSuccessResponse[T](code: Int)(implicit m: Manifest[T]): ApiRequest[U] = copy[U](responses = responses + (code -> (m, ResponseState.Success))) + + def withErrorResponse[T](code: Int)(implicit m: Manifest[T]): ApiRequest[U] = copy[U](responses = responses + (code -> (m, ResponseState.Error))) + + def withDefaultSuccessResponse[T](implicit m: Manifest[T]): ApiRequest[U] = withSuccessResponse[T](0) + + def withDefaultErrorResponse[T](implicit m: Manifest[T]): ApiRequest[U] = withErrorResponse[T](0) + + def responseForCode(statusCode: Int): Option[(Manifest[_], ResponseState)] = responses.get(statusCode) orElse responses.get(0) + + def withoutBody(): ApiRequest[U] = copy[U](bodyParam = None) + + def withBody(body: Any): ApiRequest[U] = copy[U](bodyParam = Some(body)) + + def withFormParam(name: String, value: Any): ApiRequest[U] = copy[U](formParams = formParams + (name -> value)) + + def withPathParam(name: String, value: Any): ApiRequest[U] = copy[U](pathParams = pathParams + (name -> value)) + + def withQueryParam(values: Map[String, Any]): ApiRequest[U] = copy[U](queryParams = queryParams ++ values) + + def withQueryParam(name: String, value: Any): ApiRequest[U] = copy[U](queryParams = queryParams + (name -> value)) + + def withHeaderParam(name: String, value: Any): ApiRequest[U] = copy[U](headerParams = headerParams + (name -> value)) +} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/apiSettings.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/apiSettings.mustache new file mode 100644 index 00000000000..9da1da56cdf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/apiSettings.mustache @@ -0,0 +1,44 @@ +{{>licenseInfo}} +package {{invokerPackage}} + +import java.util.concurrent.TimeUnit + +import org.apache.pekko.actor.{ActorSystem, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider} +import org.apache.pekko.http.scaladsl.model.{StatusCode, StatusCodes} +import org.apache.pekko.http.scaladsl.model.headers.RawHeader +import com.typesafe.config.Config + +import scala.jdk.CollectionConverters._ +import scala.concurrent.duration.FiniteDuration + +class ApiSettings(config: Config) extends Extension { + def this(system: ExtendedActorSystem) = this(system.settings.config) + + private def cfg = config.getConfig("{{configKeyPath}}.{{configKey}}") + + val alwaysTrustCertificates: Boolean = cfg.getBoolean("trust-certificates") + val defaultHeaders: List[RawHeader] = cfg.getConfig("default-headers").entrySet.asScala.toList.map(c => RawHeader(c.getKey, c.getValue.render)) + val connectionTimeout = FiniteDuration(cfg.getDuration("connection-timeout", TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS) + val compressionEnabled: Boolean = cfg.getBoolean("compression.enabled") + val compressionSizeThreshold: Int = cfg.getBytes("compression.size-threshold").toInt + val customCodes: List[StatusCode]= cfg.getConfigList("custom-codes").asScala.toList.map { c => + StatusCodes.custom( + c.getInt("code"), + c.getString("reason"), + if (c.hasPath("defaultMessage")) c.getString("defaultMessage") else c.getString("reason"), + c.getBoolean("success"), + if (c.hasPath("allowsEntity")) c.getBoolean("allowsEntity") else true + ) + } +} + +object ApiSettings extends ExtensionId[ApiSettings] with ExtensionIdProvider { + + override def lookup = ApiSettings + + override def createExtension(system: ExtendedActorSystem): ApiSettings = + new ApiSettings(system) + + // needed to get the type right when used from Java + override def get(system: ActorSystem): ApiSettings = super.get(system) +} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/api_doc.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/api_doc.mustache new file mode 100644 index 00000000000..236e83ca36a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/api_doc.mustache @@ -0,0 +1,114 @@ +# {{classname}}{{#description}} + +{{.}}{{/description}} + +All URIs are relative to *{{basePath}}* + +Method | HTTP request | Description +------------- | ------------- | ------------- +{{#operations}}{{#operation}}[**{{operationId}}**]({{classname}}.md#{{operationId}}) | **{{httpMethod}}** {{path}} | {{summary}} +[**{{operationId}}WithHttpInfo**]({{classname}}.md#{{operationId}}WithHttpInfo) | **{{httpMethod}}** {{path}} | {{summary}} +{{/operation}}{{/operations}} + +{{#operations}} +{{#operation}} + +## {{operationId}} + +> {{operationId}}({{#hasParams}}{{operationId}}Request{{/hasParams}}): ApiRequest[{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] + +{{summary}}{{#notes}} + +{{.}}{{/notes}} + +### Example + +```scala +// Import classes: +{{#imports}} +import {{import}} +{{/imports}} +import {{invokerPackage}}._ +import {{invokerPackage}}.CollectionFormats._ +import {{invokerPackage}}.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + {{#hasAuthMethods}} + {{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + // Configure HTTP basic authorization: {{{name}}} + implicit val {{{name}}}: BasicCredentials = BasicCredentials("YOUR USERNAME", "YOUR PASSWORD"){{/isBasicBasic}}{{#isBasicBearer}} + // Configure HTTP bearer authorization: {{{name}}} + implicit val {{{name}}}: BearerToken = BearerToken("BEARER TOKEN"){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}} + // Configure API key authorization: {{{name}}} + implicit val {{{name}}}: ApiKeyValue = ApiKeyValue("YOUR API KEY"){{/isApiKey}} + {{/authMethods}} + {{/hasAuthMethods}} + + val apiInvoker = ApiInvoker() + val apiInstance = {{{classname}}}("{{{basePath}}}"){{#allParams}} + val {{{paramName}}}: {{{dataType}}} = {{{example}}} // {{{dataType}}} | {{{description}}} + {{/allParams}} + + val request = apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}"){{#returnType}} + System.out.println(s"Response body: $content"){{/returnType}} + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}") + exception.printStackTrace(); + } +} +``` + +### Parameters + +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +Name | Type | Description | Notes +------------- | ------------- | ------------- | -------------{{/-last}}{{/allParams}} +{{#allParams}} **{{paramName}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isFile}}**{{dataType}}**{{/isFile}}{{^isFile}}[**{{dataType}}**]({{baseType}}.md){{/isFile}}{{/isPrimitiveType}}| {{description}} |{{^required}} [optional]{{/required}}{{^isContainer}}{{#defaultValue}} [default to {{.}}]{{/defaultValue}}{{/isContainer}}{{#allowableValues}} [enum: {{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} +{{/allParams}} + +### Return type + +{{#returnType}}ApiRequest[{{#returnTypeIsPrimitive}}**{{returnType}}**{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[**{{returnType}}**]({{returnBaseType}}.md){{/returnTypeIsPrimitive}}]{{/returnType}} +{{^returnType}}ApiRequest[Unit] (empty response body){{/returnType}} + +### Authorization + +{{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}[{{name}}](../README.md#{{name}}){{^-last}}, {{/-last}}{{/authMethods}} + +### HTTP request headers + +- **Content-Type**: {{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}{{^consumes}}Not defined{{/consumes}} +- **Accept**: {{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}{{^produces}}Not defined{{/produces}} + +{{#responses.0}} +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +{{#responses}} +| **{{code}}** | {{message}} | {{#headers}} * {{baseName}} - {{description}}
{{/headers}}{{^headers.0}} - {{/headers.0}} | +{{/responses}} +{{/responses.0}} + +{{/operation}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/build.sbt.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/build.sbt.mustache new file mode 100644 index 00000000000..478f24c993e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/build.sbt.mustache @@ -0,0 +1,29 @@ +version := "{{artifactVersion}}" +name := "{{artifactId}}" +organization := "{{groupId}}" + +scalaVersion := "2.13.12" +val PekkoVersion = "1.0.2" + +libraryDependencies ++= Seq( + "com.typesafe" % "config" % "1.4.3", + "org.apache.pekko" %% "pekko-actor" % PekkoVersion, + "org.apache.pekko" %% "pekko-stream" % PekkoVersion, + "org.apache.pekko" %% "pekko-stream" % PekkoVersion, + "com.github.pjfanning" %% "pekko-http-json4s" % "2.3.2", + "org.json4s" %% "json4s-jackson" % "4.0.7", + "org.json4s" %% "json4s-ext" % "4.0.7", + // test dependencies + "org.scalatest" %% "scalatest" % "3.2.17" % "test", + "org.scalatestplus" %% "junit-4-13" % "3.2.17.0" % "test" +) + +resolvers ++= Seq(Resolver.mavenLocal) + +scalacOptions := Seq( + "-unchecked", + "-deprecation", + "-feature" +) + +publishArtifact in (Compile, packageDoc) := false diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/class_doc.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/class_doc.mustache new file mode 100644 index 00000000000..0cb62ce8fe5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/class_doc.mustache @@ -0,0 +1,32 @@ +# {{#vendorExtensions.x-is-one-of-interface}}Trait {{/vendorExtensions.x-is-one-of-interface}}{{classname}} + +{{#description}}{{&description}} +{{/description}} + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +{{#vars}}**{{name}}** | {{#isEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} +{{/vars}} +{{#vars}}{{#isEnum}} + +## Enum: {{datatypeWithEnum}} +Allowed values: {{#allowableValues}}[{{#values}}{{{.}}}{{^-last}}, {{/-last}}{{/values}}]{{/allowableValues}} + +{{/isEnum}}{{/vars}} +{{#vendorExtensions.x-implements.0}} + +## Implemented Interfaces + +{{#vendorExtensions.x-implements}} +* {{{.}}} +{{/vendorExtensions.x-implements}} +{{/vendorExtensions.x-implements.0}} +{{#vendorExtensions.x-is-one-of-interface}} +## Implementing Classes + +{{#oneOf}} +* {{{.}}} +{{/oneOf}} +{{/vendorExtensions.x-is-one-of-interface}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/enum_doc.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/enum_doc.mustache new file mode 100644 index 00000000000..20c512aaeae --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/enum_doc.mustache @@ -0,0 +1,7 @@ +# {{classname}} + +## Enum + +{{#allowableValues}}{{#enumVars}} +* `{{name}}` (value: `{{{value}}}`) +{{/enumVars}}{{/allowableValues}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/enumsSerializers.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/enumsSerializers.mustache new file mode 100644 index 00000000000..8c7e6f2e41e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/enumsSerializers.mustache @@ -0,0 +1,42 @@ +{{>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 + } + } + +} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/javadoc.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/javadoc.mustache new file mode 100644 index 00000000000..6acaca14ac3 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/javadoc.mustache @@ -0,0 +1,25 @@ +{{#notes}} +{{{.}}} + +{{/notes}} +Expected answers: +{{#responses}} + code {{code}} : {{{dataType}}} {{#message}}({{{.}}}){{/message}} + {{#headers}} + {{#-first}} + Headers : + {{/-first}} + {{{baseName}}} - {{{description}}} + {{/headers}} +{{/responses}} +{{#authMethods.0}} + +Available security schemes: +{{#authMethods}} + {{name}} ({{type}}) +{{/authMethods}} +{{/authMethods.0}} + +{{#allParams}} +@param {{{paramName}}} {{{description}}} +{{/allParams}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/licenseInfo.mustache new file mode 100644 index 00000000000..16ba21d203d --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/licenseInfo.mustache @@ -0,0 +1,11 @@ +/** + * {{{appName}}} + * {{{appDescription}}} + * + * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} + * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/methodParameters.mustache new file mode 100644 index 00000000000..9a1b04d7747 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/methodParameters.mustache @@ -0,0 +1 @@ +{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}{{#isContainer}}{{dataType}}{{/isContainer}}{{^isContainer}}Option[{{dataType}}]{{/isContainer}}{{/required}}{{^defaultValue}}{{^required}}{{^isContainer}} = None{{/isContainer}}{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#authMethods.0}})(implicit {{#authMethods}}{{#isApiKey}}apiKey: ApiKeyValue{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}basicAuth: BasicCredentials{{/isBasicBasic}}{{#isBasicBearer}}bearerToken: BearerToken{{/isBasicBearer}}{{/isBasic}}{{^-last}}, {{/-last}}{{/authMethods}}{{/authMethods.0}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/model.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/model.mustache new file mode 100644 index 00000000000..832606e1d86 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/model.mustache @@ -0,0 +1,41 @@ +{{>licenseInfo}} +package {{package}} + +{{#imports}} +import {{import}} +{{/imports}} +import {{invokerPackage}}.ApiModel + +{{#models}} +{{#model}} +case class {{classname}} ( + {{#vars}} + {{#description}} + /* {{{.}}} */ + {{/description}} + {{{name}}}: {{^required}}Option[{{/required}}{{^isEnum}}{{dataType}}{{/isEnum}}{{#isEnum}}{{classname}}Enums.{{datatypeWithEnum}}{{/isEnum}}{{^required}}] = None{{/required}}{{^-last}},{{/-last}} + {{/vars}} +) extends ApiModel + +{{#hasEnums}} +object {{classname}}Enums { + + {{#vars}} + {{#isEnum}} + type {{datatypeWithEnum}} = {{datatypeWithEnum}}.Value + {{/isEnum}} + {{/vars}} + {{#vars}} + {{#isEnum}} + object {{datatypeWithEnum}} extends Enumeration { +{{#_enum}} + val {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}} = Value("{{.}}") +{{/_enum}} + } + + {{/isEnum}} + {{/vars}} +} +{{/hasEnums}} +{{/model}} +{{/models}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/model_doc.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/model_doc.mustache new file mode 100644 index 00000000000..e1025787793 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/model_doc.mustache @@ -0,0 +1,9 @@ +{{#models}}{{#model}} + +{{#isEnum}} +{{>enum_doc}} +{{/isEnum}} +{{^isEnum}} +{{>class_doc}} +{{/isEnum}} +{{/model}}{{/models}} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/operationReturnType.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/operationReturnType.mustache new file mode 100644 index 00000000000..170935cad63 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/operationReturnType.mustache @@ -0,0 +1 @@ +{{{returnType}}}{{^returnType}}Unit{{/returnType}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/paramCreation.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/paramCreation.mustache new file mode 100644 index 00000000000..dc7bbc9f434 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/paramCreation.mustache @@ -0,0 +1 @@ +{{#isMap}}{{baseName}}{{/isMap}}{{^isMap}}"{{baseName}}", {{#isContainer}}ArrayValues({{{paramName}}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}{{{paramName}}}{{/isContainer}}{{/isMap}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/pom.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/pom.mustache new file mode 100644 index 00000000000..d8165ad3fa1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/pom.mustache @@ -0,0 +1,254 @@ + + 4.0.0 + + {{artifactId}} + + {{groupId}} + {{artifactId}} + {{artifactVersion}} + + jar + + + UTF-8 + UTF-8 + + 1.8 + 2.13.12 + 4.0.7 + 4.0.7 + 1.0.2 + 1.0.0 + 1.4.3 + 2.3.2 + 3.2.17 + 3.2.17.0 + + 3.3.1 + + + + + org.scala-lang + scala-library + ${scala.version} + provided + + + com.typesafe + config + ${typesafeconfig.version} + + + org.apache.pekko + pekko-actor_2.13 + ${pekko.version} + + + org.apache.pekko + pekko-stream_2.13 + ${pekko.version} + + + org.apache.pekko + pekko-http_2.13 + ${pekko.http.version} + + + org.json4s + json4s-jackson_2.13 + ${json4s.jackson.version} + + + org.json4s + json4s-ext_2.13 + ${json4s.jackson.version} + + + com.github.pjfanning + pekko-http-json4s_2.13 + ${pekko.http.json4s.version} + + + + + org.scalatest + scalatest_2.13 + ${scala.test.version} + test + + + org.scalatestplus + junit-4-13_2.13 + ${scala.test.plus.version} + test + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.2 + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M4 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + 4 + pertest + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.2 + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + jar + test-jar + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${java.version} + ${java.version} + + + + net.alchim31.maven + scala-maven-plugin + ${scala.maven.plugin.version} + + + scala-compile-first + process-resources + + add-source + compile + + + + scala-test-compile + process-test-resources + + testCompile + + + + + + -feature + + + -Xms128m + -Xmx1500m + + + + + + diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/project/build.properties.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/project/build.properties.mustache new file mode 100644 index 00000000000..8cf07b7c254 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/project/build.properties.mustache @@ -0,0 +1 @@ +sbt.version=1.9.8 \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/reference.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/reference.mustache new file mode 100644 index 00000000000..1a28a8962ed --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/reference.mustache @@ -0,0 +1,24 @@ +{{configKeyPath}} { + + {{configKey}} { + + compression { + enabled: false + size-threshold: 0 + } + + trust-certificates: true + + connection-timeout: {{defaultTimeout}}ms + + default-headers { + "userAgent": "{{artifactId}}_{{artifactVersion}}" + } + + // let you define custom http status code, as in : + // { code: 601, reason: "some custom http status code", success: false } + custom-codes : [] + } +} + +spray.can.host-connector.max-redirects = 10 \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/requests.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/requests.mustache new file mode 100644 index 00000000000..56177e11c30 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/requests.mustache @@ -0,0 +1,192 @@ +{{>licenseInfo}} +package {{invokerPackage}} + +import java.io.File +import java.net.URLEncoder +import java.util.UUID +import java.time.OffsetDateTime + +import scala.util.Try + +sealed trait ApiReturnWithHeaders { + def headers: Map[String, String] + + def header(name: String): Option[String] = headers.get(name) + + def getStringHeader(name: String): Option[String] = header(name) + + // workaround: return date time header in string instead of datetime object + def getDateTimeHeader(name: String): Option[String] = header(name) + + def getIntHeader(name: String): Option[Int] = castedHeader(name, java.lang.Integer.parseInt) + + def getLongHeader(name: String): Option[Long] = castedHeader(name, java.lang.Long.parseLong) + + def getFloatHeader(name: String): Option[Float] = castedHeader(name, java.lang.Float.parseFloat) + + def getDoubleHeader(name: String): Option[Double] = castedHeader(name, java.lang.Double.parseDouble) + + def getBooleanHeader(name: String): Option[Boolean] = castedHeader(name, java.lang.Boolean.parseBoolean) + + def getOffsetDateTimeHeader(name: String): Option[OffsetDateTime] = castedHeader(name, java.time.OffsetDateTime.parse) + + private def castedHeader[U](name: String, conversion: String => U): Option[U] = { + Try { + header(name).map(conversion) + }.get + } +} + +sealed case class ApiResponse[T](code: Int, content: T, headers: Map[String, String] = Map.empty) + extends ApiReturnWithHeaders + +sealed case class ApiError[T](code: Int, message: String, responseContent: Option[T], cause: Throwable = null, headers: Map[String, String] = Map.empty) + extends Throwable(s"($code) $message.${responseContent.map(s => s" Content : $s").getOrElse("")}", cause) + with ApiReturnWithHeaders + +sealed case class ApiMethod(value: String) + +object ApiMethods { + val CONNECT = ApiMethod("CONNECT") + val DELETE = ApiMethod("DELETE") + val GET = ApiMethod("GET") + val HEAD = ApiMethod("HEAD") + val OPTIONS = ApiMethod("OPTIONS") + val PATCH = ApiMethod("PATCH") + val POST = ApiMethod("POST") + val PUT = ApiMethod("PUT") + val TRACE = ApiMethod("TRACE") +} + +/** + * This trait needs to be added to any model defined by the api. + */ +trait ApiModel + +/** + * Single trait defining a credential that can be transformed to a paramName / paramValue tuple + */ +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 + +} + + +/** + * Case class used to unapply numeric values only in pattern matching + * + * @param value the string representation of the numeric value + */ +sealed case class NumericValue(value: String) { + override def toString: String = value +} + +object NumericValue { + def unapply(n: Any): Option[NumericValue] = n match { + case (_: Int | _: Long | _: Float | _: Double | _: Boolean | _: Byte) => Some(NumericValue(String.valueOf(n))) + case _ => None + } +} + +/** + * Used for params being arrays + */ +sealed case class ArrayValues(values: Seq[Any], format: CollectionFormat = CollectionFormats.CSV) + +object ArrayValues { + def apply(values: Option[Seq[Any]], format: CollectionFormat): ArrayValues = + ArrayValues(values.getOrElse(Seq.empty), format) + + def apply(values: Option[Seq[Any]]): ArrayValues = ArrayValues(values, CollectionFormats.CSV) +} + + +/** + * Defines how arrays should be rendered in query strings. + */ +sealed trait CollectionFormat + +trait MergedArrayFormat extends CollectionFormat { + def separator: String +} + +object CollectionFormats { + + case object CSV extends MergedArrayFormat { + override val separator = "," + } + + case object TSV extends MergedArrayFormat { + override val separator = "\t" + } + + case object SSV extends MergedArrayFormat { + override val separator = " " + } + + case object PIPES extends MergedArrayFormat { + override val separator = "|" + } + + case object MULTI extends CollectionFormat + +} + +object ParametersMap { + + /** + * Pimp parameters maps (Map[String, Any]) in order to transform them in a sequence of String -> Any tuples, + * with valid url-encoding, arrays handling, files preservation, ... + */ + implicit class ParametersMapImprovements(val m: Map[String, Any]) { + + def asFormattedParamsList: List[(String, Any)] = m.toList.flatMap(formattedParams) + + def asFormattedParams: Map[String, Any] = m.flatMap(formattedParams) + + private def urlEncode(v: Any) = URLEncoder.encode(String.valueOf(v), "utf-8").replaceAll("\\+", "%20") + + private def formattedParams(tuple: (String, Any)): Seq[(String, Any)] = formattedParams(tuple._1, tuple._2) + + private def formattedParams(name: String, value: Any): Seq[(String, Any)] = value match { + case arr: ArrayValues => + arr.format match { + case CollectionFormats.MULTI => arr.values.flatMap(formattedParams(name, _)) + case format: MergedArrayFormat => Seq((name, arr.values.mkString(format.separator))) + } + case None => Seq.empty + case Some(opt) => formattedParams(name, opt) + case s: Seq[Any] => formattedParams(name, ArrayValues(s)) + case v: String => Seq((name, urlEncode(v))) + case v: UUID => formattedParams(name, v.toString) + case NumericValue(v) => Seq((name, urlEncode(v))) + case f: File => Seq((name, f)) + case m: ApiModel => Seq((name, m)) + } + } + +} diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/responseState.mustache new file mode 100644 index 00000000000..5b428552346 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/responseState.mustache @@ -0,0 +1 @@ +{{#is2xx}}Success{{/is2xx}}{{#is3xx}}Success{{/is3xx}}{{#is4xx}}Error{{/is4xx}}{{#is5xx}}Error{{/is5xx}}{{#isDefault}}Error{{/isDefault}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-pekko-client/serializers.mustache b/modules/openapi-generator/src/main/resources/scala-pekko-client/serializers.mustache new file mode 100644 index 00000000000..e66999529d9 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-pekko-client/serializers.mustache @@ -0,0 +1,53 @@ +package {{invokerPackage}} + +{{#java8}} +import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId} +import java.time.format.DateTimeFormatter +{{/java8}} +{{#joda}} +import org.joda.time.format.ISODateTimeFormat +import org.joda.time.{LocalDate, DateTime} +{{/joda}} +import org.json4s.{Serializer, CustomSerializer, JNull} +import org.json4s.ext.JavaTypesSerializers +import org.json4s.JsonAST.JString + +import scala.util.Try + +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 d: OffsetDateTime => + JString(d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)) + })) + + case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( { + case JString(s) => LocalDate.parse(s) + }, { + 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 d: DateTime => JString(ISODateTimeFormat.dateTime().print(d)) + })) + + case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( { + case JString(s) => ISODateTimeFormat.localDateParser().parseLocalDate(s) + }, { + case d: LocalDate => JString(ISODateTimeFormat.date().print(d)) + })) +{{/joda}} + + def all: Seq[Serializer[_]] = JavaTypesSerializers.all :+ DateTimeSerializer :+ LocalDateSerializer + +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaPekkoClientOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaPekkoClientOptionsProvider.java new file mode 100644 index 00000000000..b04087101c0 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaPekkoClientOptionsProvider.java @@ -0,0 +1,69 @@ +/* + * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * Copyright 2018 SmartBear Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.options; + +import com.google.common.collect.ImmutableMap; +import org.openapitools.codegen.CodegenConstants; + +import java.util.Map; + +public class ScalaPekkoClientOptionsProvider implements OptionsProvider { + public static final String SOURCE_FOLDER_VALUE = "sourceFolder"; + public static final String MODEL_PACKAGE_VALUE = "package"; + public static final String API_PACKAGE_VALUE = "apiPackage"; + public static final String SORT_PARAMS_VALUE = "false"; + public static final String SORT_MODEL_PROPERTIES_VALUE = "false"; + public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true"; + public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false"; + public static final String PREPEND_FORM_OR_BODY_PARAMETERS_VALUE = "true"; + public static final String MAIN_PACKAGE_VALUE = "net.test"; + public static final String MODEL_PROPERTY_NAMING = "camelCase"; + public static final String DATE_LIBRARY = "joda"; + public static final String ENUM_UNKNOWN_DEFAULT_CASE_VALUE = "false"; + + + @Override + public String getLanguage() { + return "scala-pekko"; + } + + @Override + public Map createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return builder.put(CodegenConstants.MODEL_PACKAGE, MODEL_PACKAGE_VALUE) + .put(CodegenConstants.API_PACKAGE, API_PACKAGE_VALUE) + .put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_VALUE) + .put(CodegenConstants.SORT_MODEL_PROPERTIES_BY_REQUIRED_FLAG, SORT_MODEL_PROPERTIES_VALUE) + .put(CodegenConstants.ENSURE_UNIQUE_PARAMS, ENSURE_UNIQUE_PARAMS_VALUE) + .put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_VALUE) + .put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE) + .put(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS, PREPEND_FORM_OR_BODY_PARAMETERS_VALUE) + .put("mainPackage", MAIN_PACKAGE_VALUE) + .put(CodegenConstants.MODEL_PROPERTY_NAMING, MODEL_PROPERTY_NAMING) + .put("dateLibrary", DATE_LIBRARY) + .put(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "true") + .put(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT, "true") + .put(CodegenConstants.ENUM_UNKNOWN_DEFAULT_CASE, ENUM_UNKNOWN_DEFAULT_CASE_VALUE) + .build(); + } + + @Override + public boolean isServer() { + return false; + } +} diff --git a/modules/openapi-generator/src/test/resources/3_0/scala-pekko/petstore.yaml b/modules/openapi-generator/src/test/resources/3_0/scala-pekko/petstore.yaml new file mode 100644 index 00000000000..947376fc008 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/scala-pekko/petstore.yaml @@ -0,0 +1,742 @@ +openapi: 3.0.0 +servers: + - url: 'https://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +tags: + - name: pet + description: Everything about your Pets + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user +paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + description: '' + operationId: addPet + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + $ref: '#/components/requestBodies/Pet' + put: + tags: + - pet + summary: Update an existing pet + description: '' + operationId: updatePet + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + $ref: '#/components/requestBodies/Pet' + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + style: form + explode: false + schema: + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid status value + security: + - petstore_auth: + - 'read:pets' + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: >- + Multiple tags can be provided with comma separated strings. Use tag1, + tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + style: form + explode: false + schema: + type: array + items: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid tag value + security: + - petstore_auth: + - 'read:pets' + deprecated: true + '/pet/{petId}': + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + name: + description: Updated name of the pet + type: string + status: + description: Updated status of the pet + type: string + delete: + tags: + - pet + summary: Deletes a pet + description: '' + operationId: deletePet + parameters: + - name: api_key + in: header + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + '/pet/{petId}/uploadImage': + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + file: + description: file to upload + type: string + format: binary + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: '' + operationId: placeOrder + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid Order + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + description: order placed for purchasing the pet + required: true + '/store/order/{orderId}': + get: + tags: + - store + summary: Find purchase order by ID + description: >- + For valid response try integer IDs with value <= 5 or > 10. Other values + will generate exceptions + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + type: integer + format: int64 + minimum: 1 + maximum: 5 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: >- + For valid response try integer IDs with value < 1000. Anything above + 1000 or nonintegers will generate API errors + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + responses: + default: + description: successful operation + security: + - auth_cookie: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Created user object + required: true + /user/createWithArray: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithArrayInput + responses: + default: + description: successful operation + security: + - auth_cookie: [] + requestBody: + $ref: '#/components/requestBodies/UserArray' + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithListInput + responses: + default: + description: successful operation + security: + - auth_cookie: [] + requestBody: + $ref: '#/components/requestBodies/UserArray' + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: true + schema: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + - name: password + in: query + description: The password for login in clear text + required: true + schema: + type: string + responses: + '200': + description: successful operation + headers: + Set-Cookie: + description: >- + Cookie authentication key for use with the `auth_cookie` + apiKey authentication. + schema: + type: string + example: AUTH_KEY=abcde12345; Path=/; HttpOnly + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when token expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + responses: + default: + description: successful operation + security: + - auth_cookie: [] + '/user/{username}': + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + parameters: + - name: username + in: path + description: The name that needs to be fetched. Use user1 for testing. + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid user supplied + '404': + description: User not found + security: + - auth_cookie: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Updated user object + required: true + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found + security: + - auth_cookie: [] +externalDocs: + description: Find out more about Swagger + url: 'http://swagger.io' +components: + requestBodies: + UserArray: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + description: List of user object + required: true + Pet: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + api_key: + type: apiKey + name: api_key + in: header + auth_cookie: + type: apiKey + name: AUTH_KEY + in: cookie + schemas: + Order: + title: Pet Order + description: An order for a pets from the pet store + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + xml: + name: Order + Category: + title: Pet category + description: A category for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + xml: + name: Category + User: + title: a User + description: A User who is purchasing from the pet store + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + format: int32 + description: User Status + xml: + name: User + Tag: + title: Pet Tag + description: A tag for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + title: a Pet + description: A pet for sale in the pet store + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/components/schemas/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + $ref: '#/components/schemas/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + title: An uploaded response + description: Describes the result of uploading an image resource + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string diff --git a/samples/client/petstore/scala-pekko/.openapi-generator-ignore b/samples/client/petstore/scala-pekko/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/client/petstore/scala-pekko/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/samples/client/petstore/scala-pekko/.openapi-generator/FILES b/samples/client/petstore/scala-pekko/.openapi-generator/FILES new file mode 100644 index 00000000000..d5d327c2759 --- /dev/null +++ b/samples/client/petstore/scala-pekko/.openapi-generator/FILES @@ -0,0 +1,29 @@ +README.md +build.sbt +docs/ApiResponse.md +docs/Category.md +docs/Order.md +docs/Pet.md +docs/PetApi.md +docs/StoreApi.md +docs/Tag.md +docs/User.md +docs/UserApi.md +pom.xml +project/build.properties +src/main/resources/reference.conf +src/main/scala/org/openapitools/client/api/EnumsSerializers.scala +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/ApiRequest.scala +src/main/scala/org/openapitools/client/core/ApiSettings.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/model/ApiResponse.scala +src/main/scala/org/openapitools/client/model/Category.scala +src/main/scala/org/openapitools/client/model/Order.scala +src/main/scala/org/openapitools/client/model/Pet.scala +src/main/scala/org/openapitools/client/model/Tag.scala +src/main/scala/org/openapitools/client/model/User.scala diff --git a/samples/client/petstore/scala-pekko/.openapi-generator/VERSION b/samples/client/petstore/scala-pekko/.openapi-generator/VERSION new file mode 100644 index 00000000000..0f78c31cdc7 --- /dev/null +++ b/samples/client/petstore/scala-pekko/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.2.0-SNAPSHOT \ No newline at end of file diff --git a/samples/client/petstore/scala-pekko/README.md b/samples/client/petstore/scala-pekko/README.md new file mode 100644 index 00000000000..65a0f09699c --- /dev/null +++ b/samples/client/petstore/scala-pekko/README.md @@ -0,0 +1,170 @@ +# scala-pekko-petstore-client + +OpenAPI Petstore +- API version: 1.0.0 + +This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + + +*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)* + +## Requirements + +Building the API client library requires: +1. Java 1.7+ +2. Maven/Gradle/SBT + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +mvn clean install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn clean deploy +``` + +Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information. + +### Maven users + +Add this dependency to your project's POM: + +```xml + + org.openapitools + scala-pekko-petstore-client + 1.0.0 + compile + +``` + +### Gradle users + +Add this dependency to your project's build file: + +```groovy +compile "org.openapitools:scala-pekko-petstore-client:1.0.0" +``` + +### SBT users + +```scala +libraryDependencies += "org.openapitools" % "scala-pekko-petstore-client" % "1.0.0" +``` + +## Getting Started + +Please follow the [installation](#installation) instruction and execute the following Java code: + +```scala + +import org.openapitools.client.core._ +import org.openapitools.client.model._ +import org.openapitools.client.api.PetApi + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object PetApiExample extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Create invoker to execute requests + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val pet: Pet = // Pet | Pet object that needs to be added to the store + + val request = apiInstance.addPet(pet) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(org.openapitools.client.core.ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#addPet") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#addPet") + exception.printStackTrace(); + } + +} + +``` + +## Documentation for API Endpoints + +All URIs are relative to *https://petstore.swagger.io/v2* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*PetApi* | [**addPet**](docs/PetApi.md#addPet) | **POST** /pet | Add a new pet to the store +*PetApi* | [**deletePet**](docs/PetApi.md#deletePet) | **DELETE** /pet/{petId} | Deletes a pet +*PetApi* | [**findPetsByStatus**](docs/PetApi.md#findPetsByStatus) | **GET** /pet/findByStatus | Finds Pets by status +*PetApi* | [**findPetsByTags**](docs/PetApi.md#findPetsByTags) | **GET** /pet/findByTags | Finds Pets by tags +*PetApi* | [**getPetById**](docs/PetApi.md#getPetById) | **GET** /pet/{petId} | Find pet by ID +*PetApi* | [**updatePet**](docs/PetApi.md#updatePet) | **PUT** /pet | Update an existing pet +*PetApi* | [**updatePetWithForm**](docs/PetApi.md#updatePetWithForm) | **POST** /pet/{petId} | Updates a pet in the store with form data +*PetApi* | [**uploadFile**](docs/PetApi.md#uploadFile) | **POST** /pet/{petId}/uploadImage | uploads an image +*StoreApi* | [**deleteOrder**](docs/StoreApi.md#deleteOrder) | **DELETE** /store/order/{orderId} | Delete purchase order by ID +*StoreApi* | [**getInventory**](docs/StoreApi.md#getInventory) | **GET** /store/inventory | Returns pet inventories by status +*StoreApi* | [**getOrderById**](docs/StoreApi.md#getOrderById) | **GET** /store/order/{orderId} | Find purchase order by ID +*StoreApi* | [**placeOrder**](docs/StoreApi.md#placeOrder) | **POST** /store/order | Place an order for a pet +*UserApi* | [**createUser**](docs/UserApi.md#createUser) | **POST** /user | Create user +*UserApi* | [**createUsersWithArrayInput**](docs/UserApi.md#createUsersWithArrayInput) | **POST** /user/createWithArray | Creates list of users with given input array +*UserApi* | [**createUsersWithListInput**](docs/UserApi.md#createUsersWithListInput) | **POST** /user/createWithList | Creates list of users with given input array +*UserApi* | [**deleteUser**](docs/UserApi.md#deleteUser) | **DELETE** /user/{username} | Delete user +*UserApi* | [**getUserByName**](docs/UserApi.md#getUserByName) | **GET** /user/{username} | Get user by user name +*UserApi* | [**loginUser**](docs/UserApi.md#loginUser) | **GET** /user/login | Logs user into the system +*UserApi* | [**logoutUser**](docs/UserApi.md#logoutUser) | **GET** /user/logout | Logs out current logged in user session +*UserApi* | [**updateUser**](docs/UserApi.md#updateUser) | **PUT** /user/{username} | Updated user + + +## Documentation for Models + + - [ApiResponse](docs/ApiResponse.md) + - [Category](docs/Category.md) + - [Order](docs/Order.md) + - [Pet](docs/Pet.md) + - [Tag](docs/Tag.md) + - [User](docs/User.md) + + + +## Documentation for Authorization + + +Authentication schemes defined for the API: + +### api_key + +- **Type**: API key +- **API key parameter name**: api_key +- **Location**: HTTP header + + +### auth_cookie + +- **Type**: API key +- **API key parameter name**: AUTH_KEY +- **Location**: + + +## Author + + + diff --git a/samples/client/petstore/scala-pekko/build.sbt b/samples/client/petstore/scala-pekko/build.sbt new file mode 100644 index 00000000000..9a66e588069 --- /dev/null +++ b/samples/client/petstore/scala-pekko/build.sbt @@ -0,0 +1,29 @@ +version := "1.0.0" +name := "scala-pekko-petstore-client" +organization := "org.openapitools" + +scalaVersion := "2.13.12" +val PekkoVersion = "1.0.2" + +libraryDependencies ++= Seq( + "com.typesafe" % "config" % "1.4.3", + "org.apache.pekko" %% "pekko-actor" % PekkoVersion, + "org.apache.pekko" %% "pekko-stream" % PekkoVersion, + "org.apache.pekko" %% "pekko-stream" % PekkoVersion, + "com.github.pjfanning" %% "pekko-http-json4s" % "2.3.2", + "org.json4s" %% "json4s-jackson" % "4.0.7", + "org.json4s" %% "json4s-ext" % "4.0.7", + // test dependencies + "org.scalatest" %% "scalatest" % "3.2.17" % "test", + "org.scalatestplus" %% "junit-4-13" % "3.2.17.0" % "test" +) + +resolvers ++= Seq(Resolver.mavenLocal) + +scalacOptions := Seq( + "-unchecked", + "-deprecation", + "-feature" +) + +publishArtifact in (Compile, packageDoc) := false diff --git a/samples/client/petstore/scala-pekko/docs/ApiResponse.md b/samples/client/petstore/scala-pekko/docs/ApiResponse.md new file mode 100644 index 00000000000..bd174989f6f --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/ApiResponse.md @@ -0,0 +1,16 @@ + + +# ApiResponse + +Describes the result of uploading an image resource + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**code** | **Int** | | [optional] +**`type`** | **String** | | [optional] +**message** | **String** | | [optional] + + + diff --git a/samples/client/petstore/scala-pekko/docs/Category.md b/samples/client/petstore/scala-pekko/docs/Category.md new file mode 100644 index 00000000000..6f5421307d5 --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/Category.md @@ -0,0 +1,15 @@ + + +# Category + +A category for a pet + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **Long** | | [optional] +**name** | **String** | | [optional] + + + diff --git a/samples/client/petstore/scala-pekko/docs/Order.md b/samples/client/petstore/scala-pekko/docs/Order.md new file mode 100644 index 00000000000..2b9b4929753 --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/Order.md @@ -0,0 +1,24 @@ + + +# Order + +An order for a pets from the pet store + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **Long** | | [optional] +**petId** | **Long** | | [optional] +**quantity** | **Int** | | [optional] +**shipDate** | **OffsetDateTime** | | [optional] +**status** | [**Status**](#Status) | Order Status | [optional] +**complete** | **Boolean** | | [optional] + + +## Enum: Status +Allowed values: [placed, approved, delivered] + + + + diff --git a/samples/client/petstore/scala-pekko/docs/Pet.md b/samples/client/petstore/scala-pekko/docs/Pet.md new file mode 100644 index 00000000000..2c65c5440df --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/Pet.md @@ -0,0 +1,24 @@ + + +# Pet + +A pet for sale in the pet store + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **Long** | | [optional] +**category** | [**Category**](Category.md) | | [optional] +**name** | **String** | | +**photoUrls** | **Seq<String>** | | +**tags** | [**Seq<Tag>**](Tag.md) | | [optional] +**status** | [**Status**](#Status) | pet status in the store | [optional] + + +## Enum: Status +Allowed values: [available, pending, sold] + + + + diff --git a/samples/client/petstore/scala-pekko/docs/PetApi.md b/samples/client/petstore/scala-pekko/docs/PetApi.md new file mode 100644 index 00000000000..1b24b9a6198 --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/PetApi.md @@ -0,0 +1,688 @@ +# PetApi + +All URIs are relative to *https://petstore.swagger.io/v2* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**addPet**](PetApi.md#addPet) | **POST** /pet | Add a new pet to the store +[**addPetWithHttpInfo**](PetApi.md#addPetWithHttpInfo) | **POST** /pet | Add a new pet to the store +[**deletePet**](PetApi.md#deletePet) | **DELETE** /pet/{petId} | Deletes a pet +[**deletePetWithHttpInfo**](PetApi.md#deletePetWithHttpInfo) | **DELETE** /pet/{petId} | Deletes a pet +[**findPetsByStatus**](PetApi.md#findPetsByStatus) | **GET** /pet/findByStatus | Finds Pets by status +[**findPetsByStatusWithHttpInfo**](PetApi.md#findPetsByStatusWithHttpInfo) | **GET** /pet/findByStatus | Finds Pets by status +[**findPetsByTags**](PetApi.md#findPetsByTags) | **GET** /pet/findByTags | Finds Pets by tags +[**findPetsByTagsWithHttpInfo**](PetApi.md#findPetsByTagsWithHttpInfo) | **GET** /pet/findByTags | Finds Pets by tags +[**getPetById**](PetApi.md#getPetById) | **GET** /pet/{petId} | Find pet by ID +[**getPetByIdWithHttpInfo**](PetApi.md#getPetByIdWithHttpInfo) | **GET** /pet/{petId} | Find pet by ID +[**updatePet**](PetApi.md#updatePet) | **PUT** /pet | Update an existing pet +[**updatePetWithHttpInfo**](PetApi.md#updatePetWithHttpInfo) | **PUT** /pet | Update an existing pet +[**updatePetWithForm**](PetApi.md#updatePetWithForm) | **POST** /pet/{petId} | Updates a pet in the store with form data +[**updatePetWithFormWithHttpInfo**](PetApi.md#updatePetWithFormWithHttpInfo) | **POST** /pet/{petId} | Updates a pet in the store with form data +[**uploadFile**](PetApi.md#uploadFile) | **POST** /pet/{petId}/uploadImage | uploads an image +[**uploadFileWithHttpInfo**](PetApi.md#uploadFileWithHttpInfo) | **POST** /pet/{petId}/uploadImage | uploads an image + + + +## addPet + +> addPet(addPetRequest): ApiRequest[Pet] + +Add a new pet to the store + + + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val pet: Pet = // Pet | Pet object that needs to be added to the store + + val request = apiInstance.addPet(pet) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#addPet") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#addPet") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **pet** | [**Pet**](Pet.md)| Pet object that needs to be added to the store | + +### Return type + +ApiRequest[[**Pet**](Pet.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: application/json, application/xml +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **405** | Invalid input | - | + + +## deletePet + +> deletePet(deletePetRequest): ApiRequest[Unit] + +Deletes a pet + + + +### Example + +```scala +// Import classes: +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val petId: Long = 789 // Long | Pet id to delete + + val apiKey: String = apiKey_example // String | + + val request = apiInstance.deletePet(petId, apiKey) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#deletePet") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#deletePet") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **petId** | **Long**| Pet id to delete | + **apiKey** | **String**| | [optional] + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **400** | Invalid pet value | - | + + +## findPetsByStatus + +> findPetsByStatus(findPetsByStatusRequest): ApiRequest[Seq[Pet]] + +Finds Pets by status + +Multiple status values can be provided with comma separated strings + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val status: Seq[String] = // Seq[String] | Status values that need to be considered for filter + + val request = apiInstance.findPetsByStatus(status) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#findPetsByStatus") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#findPetsByStatus") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **status** | [**Seq[String]**](String.md)| Status values that need to be considered for filter | [enum: available, pending, sold] + +### Return type + +ApiRequest[[**Seq[Pet]**](Pet.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **400** | Invalid status value | - | + + +## findPetsByTags + +> findPetsByTags(findPetsByTagsRequest): ApiRequest[Seq[Pet]] + +Finds Pets by tags + +Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val tags: Seq[String] = // Seq[String] | Tags to filter by + + val request = apiInstance.findPetsByTags(tags) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#findPetsByTags") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#findPetsByTags") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **tags** | [**Seq[String]**](String.md)| Tags to filter by | + +### Return type + +ApiRequest[[**Seq[Pet]**](Pet.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **400** | Invalid tag value | - | + + +## getPetById + +> getPetById(getPetByIdRequest): ApiRequest[Pet] + +Find pet by ID + +Returns a single pet + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: api_key + implicit val api_key: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val petId: Long = 789 // Long | ID of pet to return + + val request = apiInstance.getPetById(petId) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#getPetById") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#getPetById") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **petId** | **Long**| ID of pet to return | + +### Return type + +ApiRequest[[**Pet**](Pet.md)] + + +### Authorization + +[api_key](../README.md#api_key) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **400** | Invalid ID supplied | - | +| **404** | Pet not found | - | + + +## updatePet + +> updatePet(updatePetRequest): ApiRequest[Pet] + +Update an existing pet + + + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val pet: Pet = // Pet | Pet object that needs to be added to the store + + val request = apiInstance.updatePet(pet) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#updatePet") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#updatePet") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **pet** | [**Pet**](Pet.md)| Pet object that needs to be added to the store | + +### Return type + +ApiRequest[[**Pet**](Pet.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: application/json, application/xml +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **400** | Invalid ID supplied | - | +| **404** | Pet not found | - | +| **405** | Validation exception | - | + + +## updatePetWithForm + +> updatePetWithForm(updatePetWithFormRequest): ApiRequest[Unit] + +Updates a pet in the store with form data + + + +### Example + +```scala +// Import classes: +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val petId: Long = 789 // Long | ID of pet that needs to be updated + + val name: String = name_example // String | Updated name of the pet + + val status: String = status_example // String | Updated status of the pet + + val request = apiInstance.updatePetWithForm(petId, name, status) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#updatePetWithForm") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#updatePetWithForm") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **petId** | **Long**| ID of pet that needs to be updated | + **name** | **String**| Updated name of the pet | [optional] + **status** | **String**| Updated status of the pet | [optional] + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: application/x-www-form-urlencoded +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **405** | Invalid input | - | + + +## uploadFile + +> uploadFile(uploadFileRequest): ApiRequest[ApiResponse] + +uploads an image + + + +### Example + +```scala +// Import classes: +import +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = PetApi("https://petstore.swagger.io/v2") + val petId: Long = 789 // Long | ID of pet to update + + val additionalMetadata: String = additionalMetadata_example // String | Additional data to pass to server + + val file: File = BINARY_DATA_HERE // File | file to upload + + val request = apiInstance.uploadFile(petId, additionalMetadata, file) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling PetApi#uploadFile") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling PetApi#uploadFile") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **petId** | **Long**| ID of pet to update | + **additionalMetadata** | **String**| Additional data to pass to server | [optional] + **file** | **File**| file to upload | [optional] + +### Return type + +ApiRequest[[**ApiResponse**](ApiResponse.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: multipart/form-data +- **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | + diff --git a/samples/client/petstore/scala-pekko/docs/StoreApi.md b/samples/client/petstore/scala-pekko/docs/StoreApi.md new file mode 100644 index 00000000000..d9a7378bf4e --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/StoreApi.md @@ -0,0 +1,335 @@ +# StoreApi + +All URIs are relative to *https://petstore.swagger.io/v2* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**deleteOrder**](StoreApi.md#deleteOrder) | **DELETE** /store/order/{orderId} | Delete purchase order by ID +[**deleteOrderWithHttpInfo**](StoreApi.md#deleteOrderWithHttpInfo) | **DELETE** /store/order/{orderId} | Delete purchase order by ID +[**getInventory**](StoreApi.md#getInventory) | **GET** /store/inventory | Returns pet inventories by status +[**getInventoryWithHttpInfo**](StoreApi.md#getInventoryWithHttpInfo) | **GET** /store/inventory | Returns pet inventories by status +[**getOrderById**](StoreApi.md#getOrderById) | **GET** /store/order/{orderId} | Find purchase order by ID +[**getOrderByIdWithHttpInfo**](StoreApi.md#getOrderByIdWithHttpInfo) | **GET** /store/order/{orderId} | Find purchase order by ID +[**placeOrder**](StoreApi.md#placeOrder) | **POST** /store/order | Place an order for a pet +[**placeOrderWithHttpInfo**](StoreApi.md#placeOrderWithHttpInfo) | **POST** /store/order | Place an order for a pet + + + +## deleteOrder + +> deleteOrder(deleteOrderRequest): ApiRequest[Unit] + +Delete purchase order by ID + +For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + +### Example + +```scala +// Import classes: +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = StoreApi("https://petstore.swagger.io/v2") + val orderId: String = orderId_example // String | ID of the order that needs to be deleted + + val request = apiInstance.deleteOrder(orderId) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling StoreApi#deleteOrder") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling StoreApi#deleteOrder") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **orderId** | **String**| ID of the order that needs to be deleted | + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **400** | Invalid ID supplied | - | +| **404** | Order not found | - | + + +## getInventory + +> getInventory(): ApiRequest[Map[String, Int]] + +Returns pet inventories by status + +Returns a map of status codes to quantities + +### Example + +```scala +// Import classes: +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: api_key + implicit val api_key: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = StoreApi("https://petstore.swagger.io/v2") + val request = apiInstance.getInventory() + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling StoreApi#getInventory") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling StoreApi#getInventory") + exception.printStackTrace(); + } +} +``` + +### Parameters + +This endpoint does not need any parameter. + +### Return type + +ApiRequest[**Map[String, Int]**] + + +### Authorization + +[api_key](../README.md#api_key) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | + + +## getOrderById + +> getOrderById(getOrderByIdRequest): ApiRequest[Order] + +Find purchase order by ID + +For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = StoreApi("https://petstore.swagger.io/v2") + val orderId: Long = 789 // Long | ID of pet that needs to be fetched + + val request = apiInstance.getOrderById(orderId) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling StoreApi#getOrderById") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling StoreApi#getOrderById") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **orderId** | **Long**| ID of pet that needs to be fetched | + +### Return type + +ApiRequest[[**Order**](Order.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **400** | Invalid ID supplied | - | +| **404** | Order not found | - | + + +## placeOrder + +> placeOrder(placeOrderRequest): ApiRequest[Order] + +Place an order for a pet + + + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = StoreApi("https://petstore.swagger.io/v2") + val order: Order = // Order | order placed for purchasing the pet + + val request = apiInstance.placeOrder(order) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling StoreApi#placeOrder") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling StoreApi#placeOrder") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **order** | [**Order**](Order.md)| order placed for purchasing the pet | + +### Return type + +ApiRequest[[**Order**](Order.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **400** | Invalid Order | - | + diff --git a/samples/client/petstore/scala-pekko/docs/Tag.md b/samples/client/petstore/scala-pekko/docs/Tag.md new file mode 100644 index 00000000000..ae6756ef393 --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/Tag.md @@ -0,0 +1,15 @@ + + +# Tag + +A tag for a pet + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **Long** | | [optional] +**name** | **String** | | [optional] + + + diff --git a/samples/client/petstore/scala-pekko/docs/User.md b/samples/client/petstore/scala-pekko/docs/User.md new file mode 100644 index 00000000000..44bea4103d0 --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/User.md @@ -0,0 +1,21 @@ + + +# User + +A User who is purchasing from the pet store + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **Long** | | [optional] +**username** | **String** | | [optional] +**firstName** | **String** | | [optional] +**lastName** | **String** | | [optional] +**email** | **String** | | [optional] +**password** | **String** | | [optional] +**phone** | **String** | | [optional] +**userStatus** | **Int** | User Status | [optional] + + + diff --git a/samples/client/petstore/scala-pekko/docs/UserApi.md b/samples/client/petstore/scala-pekko/docs/UserApi.md new file mode 100644 index 00000000000..097714ce9a7 --- /dev/null +++ b/samples/client/petstore/scala-pekko/docs/UserApi.md @@ -0,0 +1,680 @@ +# UserApi + +All URIs are relative to *https://petstore.swagger.io/v2* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**createUser**](UserApi.md#createUser) | **POST** /user | Create user +[**createUserWithHttpInfo**](UserApi.md#createUserWithHttpInfo) | **POST** /user | Create user +[**createUsersWithArrayInput**](UserApi.md#createUsersWithArrayInput) | **POST** /user/createWithArray | Creates list of users with given input array +[**createUsersWithArrayInputWithHttpInfo**](UserApi.md#createUsersWithArrayInputWithHttpInfo) | **POST** /user/createWithArray | Creates list of users with given input array +[**createUsersWithListInput**](UserApi.md#createUsersWithListInput) | **POST** /user/createWithList | Creates list of users with given input array +[**createUsersWithListInputWithHttpInfo**](UserApi.md#createUsersWithListInputWithHttpInfo) | **POST** /user/createWithList | Creates list of users with given input array +[**deleteUser**](UserApi.md#deleteUser) | **DELETE** /user/{username} | Delete user +[**deleteUserWithHttpInfo**](UserApi.md#deleteUserWithHttpInfo) | **DELETE** /user/{username} | Delete user +[**getUserByName**](UserApi.md#getUserByName) | **GET** /user/{username} | Get user by user name +[**getUserByNameWithHttpInfo**](UserApi.md#getUserByNameWithHttpInfo) | **GET** /user/{username} | Get user by user name +[**loginUser**](UserApi.md#loginUser) | **GET** /user/login | Logs user into the system +[**loginUserWithHttpInfo**](UserApi.md#loginUserWithHttpInfo) | **GET** /user/login | Logs user into the system +[**logoutUser**](UserApi.md#logoutUser) | **GET** /user/logout | Logs out current logged in user session +[**logoutUserWithHttpInfo**](UserApi.md#logoutUserWithHttpInfo) | **GET** /user/logout | Logs out current logged in user session +[**updateUser**](UserApi.md#updateUser) | **PUT** /user/{username} | Updated user +[**updateUserWithHttpInfo**](UserApi.md#updateUserWithHttpInfo) | **PUT** /user/{username} | Updated user + + + +## createUser + +> createUser(createUserRequest): ApiRequest[Unit] + +Create user + +This can only be done by the logged in user. + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: auth_cookie + implicit val auth_cookie: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val user: User = // User | Created user object + + val request = apiInstance.createUser(user) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#createUser") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#createUser") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **user** | [**User**](User.md)| Created user object | + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +[auth_cookie](../README.md#auth_cookie) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **0** | successful operation | - | + + +## createUsersWithArrayInput + +> createUsersWithArrayInput(createUsersWithArrayInputRequest): ApiRequest[Unit] + +Creates list of users with given input array + + + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: auth_cookie + implicit val auth_cookie: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val user: Seq[User] = // Seq[User] | List of user object + + val request = apiInstance.createUsersWithArrayInput(user) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#createUsersWithArrayInput") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#createUsersWithArrayInput") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **user** | [**Seq[User]**](User.md)| List of user object | + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +[auth_cookie](../README.md#auth_cookie) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **0** | successful operation | - | + + +## createUsersWithListInput + +> createUsersWithListInput(createUsersWithListInputRequest): ApiRequest[Unit] + +Creates list of users with given input array + + + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: auth_cookie + implicit val auth_cookie: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val user: Seq[User] = // Seq[User] | List of user object + + val request = apiInstance.createUsersWithListInput(user) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#createUsersWithListInput") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#createUsersWithListInput") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **user** | [**Seq[User]**](User.md)| List of user object | + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +[auth_cookie](../README.md#auth_cookie) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **0** | successful operation | - | + + +## deleteUser + +> deleteUser(deleteUserRequest): ApiRequest[Unit] + +Delete user + +This can only be done by the logged in user. + +### Example + +```scala +// Import classes: +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: auth_cookie + implicit val auth_cookie: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val username: String = username_example // String | The name that needs to be deleted + + val request = apiInstance.deleteUser(username) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#deleteUser") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#deleteUser") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **username** | **String**| The name that needs to be deleted | + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +[auth_cookie](../README.md#auth_cookie) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **400** | Invalid username supplied | - | +| **404** | User not found | - | + + +## getUserByName + +> getUserByName(getUserByNameRequest): ApiRequest[User] + +Get user by user name + + + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val username: String = username_example // String | The name that needs to be fetched. Use user1 for testing. + + val request = apiInstance.getUserByName(username) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#getUserByName") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#getUserByName") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **username** | **String**| The name that needs to be fetched. Use user1 for testing. | + +### Return type + +ApiRequest[[**User**](User.md)] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | - | +| **400** | Invalid username supplied | - | +| **404** | User not found | - | + + +## loginUser + +> loginUser(loginUserRequest): ApiRequest[String] + +Logs user into the system + + + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val username: String = username_example // String | The user name for login + + val password: String = password_example // String | The password for login in clear text + + val request = apiInstance.loginUser(username, password) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + System.out.println(s"Response body: $content") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#loginUser") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#loginUser") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **username** | **String**| The user name for login | + **password** | **String**| The password for login in clear text | + +### Return type + +ApiRequest[**String**] + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/xml, application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | successful operation | * Set-Cookie - Cookie authentication key for use with the `auth_cookie` apiKey authentication.
* X-Rate-Limit - calls per hour allowed by the user
* X-Expires-After - date in UTC when token expires
| +| **400** | Invalid username/password supplied | - | + + +## logoutUser + +> logoutUser(): ApiRequest[Unit] + +Logs out current logged in user session + + + +### Example + +```scala +// Import classes: +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: auth_cookie + implicit val auth_cookie: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val request = apiInstance.logoutUser() + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#logoutUser") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#logoutUser") + exception.printStackTrace(); + } +} +``` + +### Parameters + +This endpoint does not need any parameter. + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +[auth_cookie](../README.md#auth_cookie) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **0** | successful operation | - | + + +## updateUser + +> updateUser(updateUserRequest): ApiRequest[Unit] + +Updated user + +This can only be done by the logged in user. + +### Example + +```scala +// Import classes: +import +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +import org.apache.pekko.actor.ActorSystem +import scala.concurrent.Future +import scala.util.{Failure, Success} + +object Example extends App { + + implicit val system: ActorSystem = ActorSystem() + import system.dispatcher + + // Configure API key authorization: auth_cookie + implicit val auth_cookie: ApiKeyValue = ApiKeyValue("YOUR API KEY") + + val apiInvoker = ApiInvoker() + val apiInstance = UserApi("https://petstore.swagger.io/v2") + val username: String = username_example // String | name that need to be deleted + + val user: User = // User | Updated user object + + val request = apiInstance.updateUser(username, user) + val response = apiInvoker.execute(request) + + response.onComplete { + case Success(ApiResponse(code, content, headers)) => + System.out.println(s"Status code: $code}") + System.out.println(s"Response headers: ${headers.mkString(", ")}") + + case Failure(error @ ApiError(code, message, responseContent, cause, headers)) => + System.err.println("Exception when calling UserApi#updateUser") + System.err.println(s"Status code: $code}") + System.err.println(s"Reason: $responseContent") + System.err.println(s"Response headers: ${headers.mkString(", ")}") + error.printStackTrace(); + + case Failure(exception) => + System.err.println("Exception when calling UserApi#updateUser") + exception.printStackTrace(); + } +} +``` + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **username** | **String**| name that need to be deleted | + **user** | [**User**](User.md)| Updated user object | + +### Return type + + +ApiRequest[Unit] (empty response body) + +### Authorization + +[auth_cookie](../README.md#auth_cookie) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **400** | Invalid user supplied | - | +| **404** | User not found | - | + diff --git a/samples/client/petstore/scala-pekko/pom.xml b/samples/client/petstore/scala-pekko/pom.xml new file mode 100644 index 00000000000..a37f416a058 --- /dev/null +++ b/samples/client/petstore/scala-pekko/pom.xml @@ -0,0 +1,254 @@ + + 4.0.0 + + scala-pekko-petstore-client + + org.openapitools + scala-pekko-petstore-client + 1.0.0 + + jar + + + UTF-8 + UTF-8 + + 1.8 + 2.13.12 + 4.0.7 + 4.0.7 + 1.0.2 + 1.0.0 + 1.4.3 + 2.3.2 + 3.2.17 + 3.2.17.0 + + 3.3.1 + + + + + org.scala-lang + scala-library + ${scala.version} + provided + + + com.typesafe + config + ${typesafeconfig.version} + + + org.apache.pekko + pekko-actor_2.13 + ${pekko.version} + + + org.apache.pekko + pekko-stream_2.13 + ${pekko.version} + + + org.apache.pekko + pekko-http_2.13 + ${pekko.http.version} + + + org.json4s + json4s-jackson_2.13 + ${json4s.jackson.version} + + + org.json4s + json4s-ext_2.13 + ${json4s.jackson.version} + + + com.github.pjfanning + pekko-http-json4s_2.13 + ${pekko.http.json4s.version} + + + + + org.scalatest + scalatest_2.13 + ${scala.test.version} + test + + + org.scalatestplus + junit-4-13_2.13 + ${scala.test.plus.version} + test + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.2 + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-maven + + enforce + + + + + 2.2.0 + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M4 + + + + loggerPath + conf/log4j.properties + + + -Xms512m -Xmx1500m + methods + 4 + pertest + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.2 + + + package + + copy-dependencies + + + ${project.build.directory}/lib + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + jar + test-jar + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + add_sources + generate-sources + + add-source + + + + + src/main/java + + + + + + add_test_sources + generate-test-sources + + add-test-source + + + + + src/test/java + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${java.version} + ${java.version} + + + + net.alchim31.maven + scala-maven-plugin + ${scala.maven.plugin.version} + + + scala-compile-first + process-resources + + add-source + compile + + + + scala-test-compile + process-test-resources + + testCompile + + + + + + -feature + + + -Xms128m + -Xmx1500m + + + + + + diff --git a/samples/client/petstore/scala-pekko/project/build.properties b/samples/client/petstore/scala-pekko/project/build.properties new file mode 100644 index 00000000000..8cf07b7c254 --- /dev/null +++ b/samples/client/petstore/scala-pekko/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.8 \ No newline at end of file diff --git a/samples/client/petstore/scala-pekko/src/main/resources/reference.conf b/samples/client/petstore/scala-pekko/src/main/resources/reference.conf new file mode 100644 index 00000000000..76e9842a8f8 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/resources/reference.conf @@ -0,0 +1,24 @@ +org.openapitools.client { + + apiRequest { + + compression { + enabled: false + size-threshold: 0 + } + + trust-certificates: true + + connection-timeout: 5000ms + + default-headers { + "userAgent": "scala-pekko-petstore-client_1.0.0" + } + + // let you define custom http status code, as in : + // { code: 601, reason: "some custom http status code", success: false } + custom-codes : [] + } +} + +spray.can.host-connector.max-redirects = 10 \ No newline at end of file diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/EnumsSerializers.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/EnumsSerializers.scala new file mode 100644 index 00000000000..71ad618e31f --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/EnumsSerializers.scala @@ -0,0 +1,51 @@ +/** + * 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.api + +import org.openapitools.client.model._ +import org.json4s._ +import scala.reflect.ClassTag + +object EnumsSerializers { + + def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ + new EnumNameSerializer(OrderEnums.Status) :+ + new EnumNameSerializer(PetEnums.Status) + + 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 + } + } + +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/PetApi.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/PetApi.scala new file mode 100644 index 00000000000..f112d9ba1ff --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/PetApi.scala @@ -0,0 +1,175 @@ +/** + * 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.api + +import org.openapitools.client.model.ApiResponse +import java.io.File +import org.openapitools.client.model.Pet +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +object PetApi { + + def apply(baseUrl: String = "https://petstore.swagger.io/v2") = new PetApi(baseUrl) +} + +class PetApi(baseUrl: String) { + + /** + * + * + * Expected answers: + * code 200 : Pet (successful operation) + * code 405 : (Invalid input) + * + * @param pet Pet object that needs to be added to the store + */ + def addPet(pet: Pet): ApiRequest[Pet] = + ApiRequest[Pet](ApiMethods.POST, baseUrl, "/pet", "application/json") + .withBody(pet) + .withSuccessResponse[Pet](200) + .withErrorResponse[Unit](405) + + + /** + * + * + * Expected answers: + * code 400 : (Invalid pet value) + * + * @param petId Pet id to delete + * @param apiKey + */ + def deletePet(petId: Long, apiKey: Option[String] = None): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.DELETE, baseUrl, "/pet/{petId}", "application/json") + .withPathParam("petId", petId) + .withHeaderParam("api_key", apiKey) + .withErrorResponse[Unit](400) + + + /** + * Multiple status values can be provided with comma separated strings + * + * Expected answers: + * code 200 : Seq[Pet] (successful operation) + * code 400 : (Invalid status value) + * + * @param status Status values that need to be considered for filter + */ + def findPetsByStatus(status: Seq[String]): ApiRequest[Seq[Pet]] = + ApiRequest[Seq[Pet]](ApiMethods.GET, baseUrl, "/pet/findByStatus", "application/json") + .withQueryParam("status", ArrayValues(status, CSV)) + .withSuccessResponse[Seq[Pet]](200) + .withErrorResponse[Unit](400) + + + /** + * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. + * + * Expected answers: + * code 200 : Seq[Pet] (successful operation) + * code 400 : (Invalid tag value) + * + * @param tags Tags to filter by + */ + def findPetsByTags(tags: Seq[String]): ApiRequest[Seq[Pet]] = + ApiRequest[Seq[Pet]](ApiMethods.GET, baseUrl, "/pet/findByTags", "application/json") + .withQueryParam("tags", ArrayValues(tags, CSV)) + .withSuccessResponse[Seq[Pet]](200) + .withErrorResponse[Unit](400) + + + /** + * Returns a single pet + * + * Expected answers: + * code 200 : Pet (successful operation) + * code 400 : (Invalid ID supplied) + * code 404 : (Pet not found) + * + * Available security schemes: + * api_key (apiKey) + * + * @param petId ID of pet to return + */ + def getPetById(petId: Long)(implicit apiKey: ApiKeyValue): ApiRequest[Pet] = + ApiRequest[Pet](ApiMethods.GET, baseUrl, "/pet/{petId}", "application/json") + .withApiKey(apiKey, "api_key", HEADER) + .withPathParam("petId", petId) + .withSuccessResponse[Pet](200) + .withErrorResponse[Unit](400) + .withErrorResponse[Unit](404) + + + /** + * + * + * Expected answers: + * code 200 : Pet (successful operation) + * code 400 : (Invalid ID supplied) + * code 404 : (Pet not found) + * code 405 : (Validation exception) + * + * @param pet Pet object that needs to be added to the store + */ + def updatePet(pet: Pet): ApiRequest[Pet] = + ApiRequest[Pet](ApiMethods.PUT, baseUrl, "/pet", "application/json") + .withBody(pet) + .withSuccessResponse[Pet](200) + .withErrorResponse[Unit](400) + .withErrorResponse[Unit](404) + .withErrorResponse[Unit](405) + + + /** + * + * + * Expected answers: + * code 200 : (successful operation) + * code 405 : (Invalid input) + * + * @param petId ID of pet that needs to be updated + * @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): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.POST, baseUrl, "/pet/{petId}", "application/x-www-form-urlencoded") + .withFormParam("name", name) + .withFormParam("status", status) + .withPathParam("petId", petId) + .withSuccessResponse[Unit](200) + .withErrorResponse[Unit](405) + + + /** + * + * + * Expected answers: + * code 200 : ApiResponse (successful operation) + * + * @param petId ID of pet to update + * @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): ApiRequest[ApiResponse] = + ApiRequest[ApiResponse](ApiMethods.POST, baseUrl, "/pet/{petId}/uploadImage", "multipart/form-data") + .withFormParam("additionalMetadata", additionalMetadata) + .withFormParam("file", file) + .withPathParam("petId", petId) + .withSuccessResponse[ApiResponse](200) + + + + +} + diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/StoreApi.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/StoreApi.scala new file mode 100644 index 00000000000..bffe37c55f4 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/StoreApi.scala @@ -0,0 +1,94 @@ +/** + * 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.api + +import org.openapitools.client.model.Order +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +object StoreApi { + + def apply(baseUrl: String = "https://petstore.swagger.io/v2") = new StoreApi(baseUrl) +} + +class StoreApi(baseUrl: String) { + + /** + * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + * + * Expected answers: + * code 400 : (Invalid ID supplied) + * code 404 : (Order not found) + * + * @param orderId ID of the order that needs to be deleted + */ + def deleteOrder(orderId: String): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.DELETE, baseUrl, "/store/order/{orderId}", "application/json") + .withPathParam("orderId", orderId) + .withErrorResponse[Unit](400) + .withErrorResponse[Unit](404) + + + /** + * Returns a map of status codes to quantities + * + * Expected answers: + * code 200 : Map[String, Int] (successful operation) + * + * Available security schemes: + * api_key (apiKey) + */ + def getInventory()(implicit apiKey: ApiKeyValue): ApiRequest[Map[String, Int]] = + ApiRequest[Map[String, Int]](ApiMethods.GET, baseUrl, "/store/inventory", "application/json") + .withApiKey(apiKey, "api_key", HEADER) + .withSuccessResponse[Map[String, Int]](200) + + + /** + * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions + * + * Expected answers: + * code 200 : Order (successful operation) + * code 400 : (Invalid ID supplied) + * code 404 : (Order not found) + * + * @param orderId ID of pet that needs to be fetched + */ + def getOrderById(orderId: Long): ApiRequest[Order] = + ApiRequest[Order](ApiMethods.GET, baseUrl, "/store/order/{orderId}", "application/json") + .withPathParam("orderId", orderId) + .withSuccessResponse[Order](200) + .withErrorResponse[Unit](400) + .withErrorResponse[Unit](404) + + + /** + * + * + * Expected answers: + * code 200 : Order (successful operation) + * code 400 : (Invalid Order) + * + * @param order order placed for purchasing the pet + */ + def placeOrder(order: Order): ApiRequest[Order] = + ApiRequest[Order](ApiMethods.POST, baseUrl, "/store/order", "application/json") + .withBody(order) + .withSuccessResponse[Order](200) + .withErrorResponse[Unit](400) + + + + +} + diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/UserApi.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/UserApi.scala new file mode 100644 index 00000000000..0e49335179b --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/api/UserApi.scala @@ -0,0 +1,186 @@ +/** + * 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.api + +import java.time.OffsetDateTime +import org.openapitools.client.model.User +import org.openapitools.client.core._ +import org.openapitools.client.core.CollectionFormats._ +import org.openapitools.client.core.ApiKeyLocations._ + +object UserApi { + + def apply(baseUrl: String = "https://petstore.swagger.io/v2") = new UserApi(baseUrl) +} + +class UserApi(baseUrl: String) { + + /** + * This can only be done by the logged in user. + * + * Expected answers: + * code 0 : (successful operation) + * + * Available security schemes: + * auth_cookie (apiKey) + * + * @param user Created user object + */ + def createUser(user: User)(implicit apiKey: ApiKeyValue): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.POST, baseUrl, "/user", "application/json") + .withApiKey(apiKey, "AUTH_KEY", COOKIE) + .withBody(user) + .withDefaultErrorResponse[Unit] + + + /** + * + * + * Expected answers: + * code 0 : (successful operation) + * + * Available security schemes: + * auth_cookie (apiKey) + * + * @param user List of user object + */ + def createUsersWithArrayInput(user: Seq[User])(implicit apiKey: ApiKeyValue): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.POST, baseUrl, "/user/createWithArray", "application/json") + .withApiKey(apiKey, "AUTH_KEY", COOKIE) + .withBody(user) + .withDefaultErrorResponse[Unit] + + + /** + * + * + * Expected answers: + * code 0 : (successful operation) + * + * Available security schemes: + * auth_cookie (apiKey) + * + * @param user List of user object + */ + def createUsersWithListInput(user: Seq[User])(implicit apiKey: ApiKeyValue): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.POST, baseUrl, "/user/createWithList", "application/json") + .withApiKey(apiKey, "AUTH_KEY", COOKIE) + .withBody(user) + .withDefaultErrorResponse[Unit] + + + /** + * This can only be done by the logged in user. + * + * Expected answers: + * code 400 : (Invalid username supplied) + * code 404 : (User not found) + * + * Available security schemes: + * auth_cookie (apiKey) + * + * @param username The name that needs to be deleted + */ + def deleteUser(username: String)(implicit apiKey: ApiKeyValue): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.DELETE, baseUrl, "/user/{username}", "application/json") + .withApiKey(apiKey, "AUTH_KEY", COOKIE) + .withPathParam("username", username) + .withErrorResponse[Unit](400) + .withErrorResponse[Unit](404) + + + /** + * + * + * Expected answers: + * code 200 : User (successful operation) + * code 400 : (Invalid username supplied) + * code 404 : (User not found) + * + * @param username The name that needs to be fetched. Use user1 for testing. + */ + def getUserByName(username: String): ApiRequest[User] = + ApiRequest[User](ApiMethods.GET, baseUrl, "/user/{username}", "application/json") + .withPathParam("username", username) + .withSuccessResponse[User](200) + .withErrorResponse[Unit](400) + .withErrorResponse[Unit](404) + + + /** + * + * + * Expected answers: + * code 200 : String (successful operation) + * Headers : + * Set-Cookie - Cookie authentication key for use with the `auth_cookie` apiKey authentication. + * X-Rate-Limit - calls per hour allowed by the user + * X-Expires-After - date in UTC when token expires + * code 400 : (Invalid username/password supplied) + * + * @param username The user name for login + * @param password The password for login in clear text + */ + def loginUser(username: String, password: String): ApiRequest[String] = + ApiRequest[String](ApiMethods.GET, baseUrl, "/user/login", "application/json") + .withQueryParam("username", username) + .withQueryParam("password", password) + .withSuccessResponse[String](200) + .withErrorResponse[Unit](400) + + object LoginUserHeaders { + def setCookie(r: ApiReturnWithHeaders) = r.getStringHeader("Set-Cookie") + def xRateLimit(r: ApiReturnWithHeaders) = r.getIntHeader("X-Rate-Limit") + def xExpiresAfter(r: ApiReturnWithHeaders) = r.getOffsetDateTimeHeader("X-Expires-After") + } + + /** + * + * + * Expected answers: + * code 0 : (successful operation) + * + * Available security schemes: + * auth_cookie (apiKey) + */ + def logoutUser()(implicit apiKey: ApiKeyValue): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.GET, baseUrl, "/user/logout", "application/json") + .withApiKey(apiKey, "AUTH_KEY", COOKIE) + .withDefaultErrorResponse[Unit] + + + /** + * This can only be done by the logged in user. + * + * Expected answers: + * code 400 : (Invalid user supplied) + * code 404 : (User not found) + * + * Available security schemes: + * auth_cookie (apiKey) + * + * @param username name that need to be deleted + * @param user Updated user object + */ + def updateUser(username: String, user: User)(implicit apiKey: ApiKeyValue): ApiRequest[Unit] = + ApiRequest[Unit](ApiMethods.PUT, baseUrl, "/user/{username}", "application/json") + .withApiKey(apiKey, "AUTH_KEY", COOKIE) + .withBody(user) + .withPathParam("username", username) + .withErrorResponse[Unit](400) + .withErrorResponse[Unit](404) + + + + +} + diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiInvoker.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiInvoker.scala new file mode 100644 index 00000000000..2eee84d882f --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiInvoker.scala @@ -0,0 +1,295 @@ +/** + * 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 java.io.File + +import org.apache.pekko.actor.ActorSystem +import org.apache.pekko.http.scaladsl.Http +import org.apache.pekko.http.scaladsl.coding._ +import org.apache.pekko.http.scaladsl.model.Multipart.FormData.BodyPart +import org.apache.pekko.http.scaladsl.model.Uri.Query +import org.apache.pekko.http.scaladsl.model._ +import org.apache.pekko.http.scaladsl.model.headers._ +import org.apache.pekko.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } +import org.apache.pekko.stream.Materializer +import org.apache.pekko.stream.scaladsl.Source +import org.apache.pekko.util.{ ByteString, Timeout } +import com.github.pjfanning.pekkohttpjson4s.Json4sSupport +import org.json4s._ +import org.json4s.jackson.JsonMethods._ +import org.json4s.jackson.Serialization + +import scala.collection.immutable +import scala.concurrent.{ ExecutionContext, ExecutionContextExecutor, Future } +import scala.reflect.ClassTag + +object ApiInvoker { + + def apply()(implicit system: ActorSystem): ApiInvoker = + apply(DefaultFormats ++ Serializers.all) + + def apply(serializers: Iterable[Serializer[_]])(implicit system: ActorSystem): ApiInvoker = + apply(DefaultFormats ++ Serializers.all ++ serializers) + + def apply(formats: Formats)(implicit system: ActorSystem): ApiInvoker = new ApiInvoker(formats) + + + /** + * Allows request execution without calling apiInvoker.execute(request) + * request.response can be used to get a future of the ApiResponse generated. + * request.result can be used to get a future of the expected ApiResponse content. If content doesn't match, a + * Future will fail with a ClassCastException + * + * @param request the apiRequest to be executed + */ + implicit class ApiRequestImprovements[T: Manifest](request: ApiRequest[T]) { + + def response(invoker: ApiInvoker)(implicit ec: ExecutionContext, system: ActorSystem): Future[ApiResponse[T]] = + response(ec, system, invoker) + + def response(implicit ec: ExecutionContext, system: ActorSystem, invoker: ApiInvoker): Future[ApiResponse[T]] = + invoker.execute(request) + + def result[U <: T](implicit c: ClassTag[U], ec: ExecutionContext, system: ActorSystem, invoker: ApiInvoker): Future[U] = + invoker.execute(request).map(_.content).mapTo[U] + + } + + /** + * Allows transformation from ApiMethod to spray HttpMethods + * + * @param method the ApiMethod to be converted + */ + implicit class ApiMethodExtensions(val method: ApiMethod) { + def toAkkaHttpMethod: HttpMethod = HttpMethods.getForKey(method.value).getOrElse(HttpMethods.GET) + } + +} + +trait UnitJSONSupport { + +} + +class ApiInvoker(formats: Formats)(implicit system: ActorSystem) extends CustomContentTypes with Json4sSupport { + + import org.openapitools.client.core.ApiInvoker._ + import org.openapitools.client.core.ParametersMap._ + + implicit val ec: ExecutionContextExecutor = system.dispatcher + implicit val jsonFormats: Formats = formats + + protected val settings: ApiSettings = ApiSettings(system) + + private implicit val materializer: Materializer = Materializer(system) + private implicit val serialization: Serialization = jackson.Serialization + + + private val http = Http() + + val CompressionFilter: HttpMessage => Boolean = (msg: HttpMessage) => + Seq( + { _: HttpMessage => settings.compressionEnabled }, + Encoder.DefaultFilter, + (message: HttpMessage) => { + val long = message.entity().getContentLengthOption() + if (long.isPresent) long.getAsLong > settings.compressionSizeThreshold else true + } + ) + .map(f => f(msg)) + .forall(identity) + + + private def addAuthentication(credentialsSeq: Seq[Credentials]) = { + request: HttpRequest => + credentialsSeq.foldLeft(request) { + case (req, BasicCredentials(login, password)) => + req.addHeader(Authorization(BasicHttpCredentials(login, password))) + case (req, ApiKeyCredentials(keyValue, keyName, ApiKeyLocations.HEADER)) => + req.addHeader(RawHeader(keyName, keyValue.value)) + case (req, BearerToken(token)) => + req.addHeader(RawHeader("Authorization", s"Bearer $token")) + case (req, _) => req + } + } + + private def headers(headers: Map[String, Any]): immutable.Seq[HttpHeader] = + headers.asFormattedParams + .map { case (name, value) => RawHeader(name, value.toString) } + .to(immutable.Seq) + + + private def bodyPart(name: String, value: Any): BodyPart = { + value match { + case f: File => + BodyPart.fromFile( + name, + ContentType(MediaTypes.`application/octet-stream`), + f, + f.length().toInt + ) + case v: String => + BodyPart.Strict(name, v.toString) + case NumericValue(v) => + BodyPart.Strict(name, v.toString) + case m: ApiModel => + BodyPart.Strict(name, Serialization.write(m)) + } + } + + + private def formDataContent(request: ApiRequest[_]) = { + val params = request.formParams.asFormattedParams + if (params.isEmpty) + None + else + Some( + normalizedContentType(request.contentType).mediaType match { + case MediaTypes.`multipart/form-data` => + Multipart.FormData(Source(params.toList.map { case (name, value) => bodyPart(name, value) })) + case MediaTypes.`application/x-www-form-urlencoded` => + FormData(params.view.mapValues(_.toString).toMap) + case _: MediaType => // Default : application/x-www-form-urlencoded. + FormData(params.view.mapValues(_.toString).toMap) + } + ) + } + + private def bodyContent(request: ApiRequest[_]): Option[Any] = request + .bodyParam + .map(Extraction.decompose) + .map(compact) + + private def createRequest(uri: Uri, request: ApiRequest[_]): HttpRequest = { + val httpRequest = request.method.toAkkaHttpMethod match { + case m@(HttpMethods.GET | HttpMethods.DELETE) => HttpRequest(m, uri) + case m@(HttpMethods.POST | HttpMethods.PUT | HttpMethods.PATCH) => + formDataContent(request) orElse bodyContent(request) match { + case Some(c: FormData) => + HttpRequest(m, uri, entity = c.toEntity) + case Some(c: Multipart.FormData) => + HttpRequest(m, uri, entity = c.toEntity) + case Some(c: String) => + HttpRequest(m, uri, entity = HttpEntity(normalizedContentType(request.contentType), ByteString(c))) + case _ => + HttpRequest(m, uri, entity = HttpEntity(normalizedContentType(request.contentType), ByteString(""))) + } + case m: HttpMethod => HttpRequest(m, uri) + } + + addAuthentication(request.credentials)( + httpRequest.withHeaders(headers(request.headerParams)) + ) + } + + def makeQuery(r: ApiRequest[_]): Query = { + r.credentials.foldLeft(r.queryParams) { + case (params, ApiKeyCredentials(key, keyName, ApiKeyLocations.QUERY)) => + params + (keyName -> key.value) + case (params, _) => params + }.asFormattedParams + .view + .mapValues(_.toString) + .toMap + .foldRight[Query](Uri.Query.Empty) { + case ((name, value), acc) => acc.+:(name, value) + } + } + + def makeUri(r: ApiRequest[_]): Uri = { + val opPath = r.operationPath.replaceAll("\\{format\\}", "json") + val opPathWithParams = r.pathParams.asFormattedParams + .view + .mapValues(_.toString) + .toMap + .foldLeft(opPath) { + case (path, (name, value)) => path.replaceAll(s"\\{$name\\}", value) + } + val query = makeQuery(r) + + Uri(r.basePath + opPathWithParams).withQuery(query) + } + + def execute[T: Manifest](r: ApiRequest[T]): Future[ApiResponse[T]] = { + implicit val timeout: Timeout = settings.connectionTimeout + + val request = createRequest(makeUri(r), r) + + http + .singleRequest(request) + .map { response => + val decoder: Decoder with Decoder = response.encoding match { + case HttpEncodings.gzip => + Coders.Gzip + case HttpEncodings.deflate => + Coders.Deflate + case HttpEncodings.identity => + Coders.NoCoding + case HttpEncoding(encoding) => + throw new IllegalArgumentException(s"Unsupported encoding: $encoding") + } + + decoder.decodeMessage(response) + } + .flatMap(unmarshallApiResponse(r)) + } + + def unmarshallApiResponse[T: Manifest](request: ApiRequest[T])(response: HttpResponse): Future[ApiResponse[T]] = { + def responseForState[V](state: ResponseState, value: V): ApiResponse[V] = { + state match { + case ResponseState.Success => + ApiResponse(response.status.intValue, value, response.headers.map(header => (header.name, header.value)).toMap) + case ResponseState.Error => + throw ApiError( + response.status.intValue, + "Error response received", + Some(value), + headers = response.headers.map(header => (header.name, header.value)).toMap + ) + } + } + val mf = implicitly(manifest[T]) + request + .responseForCode(response.status.intValue) match { + case Some((Manifest.Unit, state: ResponseState)) => + Future(responseForState(state, ()).asInstanceOf[ApiResponse[T]]) + case Some((manifest, state: ResponseState)) if manifest == mf => + implicit val m: Unmarshaller[HttpEntity, T] = unmarshaller[T](mf, serialization, formats) + Unmarshal(response.entity) + .to[T] + .recoverWith { + case e => throw ApiError(response.status.intValue, s"Unable to unmarshall content to [$manifest]", Some(response.entity.toString), e) + } + .map(value => responseForState(state, value)) + case None | Some(_) => + Future.failed(ApiError(response.status.intValue, "Unexpected response code", Some(response.entity.toString))) + } + } +} + +sealed trait CustomContentTypes { + + protected def normalizedContentType(original: String): ContentType = + ContentType(parseContentType(original).mediaType, () => HttpCharsets.`UTF-8`) + + protected def parseContentType(contentType: String): ContentType = { + + ContentType.parse(contentType) match { + case Right(ct: ContentType) => + ct + case Left(error: List[ErrorInfo]) => + throw new IllegalArgumentException( + s"Error converting '$contentType' to a ContentType header: '${error.map(_.summary).mkString(", ")}'" + ) + } + } +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiRequest.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiRequest.scala new file mode 100644 index 00000000000..d6f5ae6182e --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiRequest.scala @@ -0,0 +1,67 @@ +/** + * 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 + +sealed trait ResponseState + +object ResponseState { + + case object Success extends ResponseState + + case object Error extends ResponseState + +} + +case class ApiRequest[U]( + // required fields + method: ApiMethod, + basePath: String, + operationPath: String, + contentType: String, + + // optional fields + responses: Map[Int, (Manifest[_], ResponseState)] = Map.empty, + bodyParam: Option[Any] = None, + formParams: Map[String, Any] = Map.empty, + pathParams: Map[String, Any] = Map.empty, + queryParams: Map[String, Any] = Map.empty, + headerParams: Map[String, Any] = Map.empty, + credentials: Seq[Credentials] = List.empty) { + + def withCredentials(cred: Credentials): ApiRequest[U] = copy[U](credentials = credentials :+ cred) + + def withApiKey(key: ApiKeyValue, keyName: String, location: ApiKeyLocation): ApiRequest[U] = withCredentials(ApiKeyCredentials(key, keyName, location)) + + def withSuccessResponse[T](code: Int)(implicit m: Manifest[T]): ApiRequest[U] = copy[U](responses = responses + (code -> (m, ResponseState.Success))) + + def withErrorResponse[T](code: Int)(implicit m: Manifest[T]): ApiRequest[U] = copy[U](responses = responses + (code -> (m, ResponseState.Error))) + + def withDefaultSuccessResponse[T](implicit m: Manifest[T]): ApiRequest[U] = withSuccessResponse[T](0) + + def withDefaultErrorResponse[T](implicit m: Manifest[T]): ApiRequest[U] = withErrorResponse[T](0) + + def responseForCode(statusCode: Int): Option[(Manifest[_], ResponseState)] = responses.get(statusCode) orElse responses.get(0) + + def withoutBody(): ApiRequest[U] = copy[U](bodyParam = None) + + def withBody(body: Any): ApiRequest[U] = copy[U](bodyParam = Some(body)) + + def withFormParam(name: String, value: Any): ApiRequest[U] = copy[U](formParams = formParams + (name -> value)) + + def withPathParam(name: String, value: Any): ApiRequest[U] = copy[U](pathParams = pathParams + (name -> value)) + + def withQueryParam(values: Map[String, Any]): ApiRequest[U] = copy[U](queryParams = queryParams ++ values) + + def withQueryParam(name: String, value: Any): ApiRequest[U] = copy[U](queryParams = queryParams + (name -> value)) + + def withHeaderParam(name: String, value: Any): ApiRequest[U] = copy[U](headerParams = headerParams + (name -> value)) +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiSettings.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiSettings.scala new file mode 100644 index 00000000000..31fe118a423 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/ApiSettings.scala @@ -0,0 +1,54 @@ +/** + * 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 java.util.concurrent.TimeUnit + +import org.apache.pekko.actor.{ActorSystem, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider} +import org.apache.pekko.http.scaladsl.model.{StatusCode, StatusCodes} +import org.apache.pekko.http.scaladsl.model.headers.RawHeader +import com.typesafe.config.Config + +import scala.jdk.CollectionConverters._ +import scala.concurrent.duration.FiniteDuration + +class ApiSettings(config: Config) extends Extension { + def this(system: ExtendedActorSystem) = this(system.settings.config) + + private def cfg = config.getConfig("org.openapitools.client.apiRequest") + + val alwaysTrustCertificates: Boolean = cfg.getBoolean("trust-certificates") + val defaultHeaders: List[RawHeader] = cfg.getConfig("default-headers").entrySet.asScala.toList.map(c => RawHeader(c.getKey, c.getValue.render)) + val connectionTimeout = FiniteDuration(cfg.getDuration("connection-timeout", TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS) + val compressionEnabled: Boolean = cfg.getBoolean("compression.enabled") + val compressionSizeThreshold: Int = cfg.getBytes("compression.size-threshold").toInt + val customCodes: List[StatusCode]= cfg.getConfigList("custom-codes").asScala.toList.map { c => + StatusCodes.custom( + c.getInt("code"), + c.getString("reason"), + if (c.hasPath("defaultMessage")) c.getString("defaultMessage") else c.getString("reason"), + c.getBoolean("success"), + if (c.hasPath("allowsEntity")) c.getBoolean("allowsEntity") else true + ) + } +} + +object ApiSettings extends ExtensionId[ApiSettings] with ExtensionIdProvider { + + override def lookup = ApiSettings + + override def createExtension(system: ExtendedActorSystem): ApiSettings = + new ApiSettings(system) + + // needed to get the type right when used from Java + override def get(system: ActorSystem): ApiSettings = super.get(system) +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/Serializers.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/Serializers.scala new file mode 100644 index 00000000000..a569d56b498 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/Serializers.scala @@ -0,0 +1,31 @@ +package org.openapitools.client.core + +import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId} +import java.time.format.DateTimeFormatter +import org.json4s.{Serializer, CustomSerializer, JNull} +import org.json4s.ext.JavaTypesSerializers +import org.json4s.JsonAST.JString + +import scala.util.Try + +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 d: OffsetDateTime => + JString(d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)) + })) + + case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( { + case JString(s) => LocalDate.parse(s) + }, { + case d: LocalDate => + JString(d.format(DateTimeFormatter.ISO_LOCAL_DATE)) + })) + + def all: Seq[Serializer[_]] = JavaTypesSerializers.all :+ DateTimeSerializer :+ LocalDateSerializer + +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/requests.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/requests.scala new file mode 100644 index 00000000000..4d962d76181 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/core/requests.scala @@ -0,0 +1,202 @@ +/** + * 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 java.io.File +import java.net.URLEncoder +import java.util.UUID +import java.time.OffsetDateTime + +import scala.util.Try + +sealed trait ApiReturnWithHeaders { + def headers: Map[String, String] + + def header(name: String): Option[String] = headers.get(name) + + def getStringHeader(name: String): Option[String] = header(name) + + // workaround: return date time header in string instead of datetime object + def getDateTimeHeader(name: String): Option[String] = header(name) + + def getIntHeader(name: String): Option[Int] = castedHeader(name, java.lang.Integer.parseInt) + + def getLongHeader(name: String): Option[Long] = castedHeader(name, java.lang.Long.parseLong) + + def getFloatHeader(name: String): Option[Float] = castedHeader(name, java.lang.Float.parseFloat) + + def getDoubleHeader(name: String): Option[Double] = castedHeader(name, java.lang.Double.parseDouble) + + def getBooleanHeader(name: String): Option[Boolean] = castedHeader(name, java.lang.Boolean.parseBoolean) + + def getOffsetDateTimeHeader(name: String): Option[OffsetDateTime] = castedHeader(name, java.time.OffsetDateTime.parse) + + private def castedHeader[U](name: String, conversion: String => U): Option[U] = { + Try { + header(name).map(conversion) + }.get + } +} + +sealed case class ApiResponse[T](code: Int, content: T, headers: Map[String, String] = Map.empty) + extends ApiReturnWithHeaders + +sealed case class ApiError[T](code: Int, message: String, responseContent: Option[T], cause: Throwable = null, headers: Map[String, String] = Map.empty) + extends Throwable(s"($code) $message.${responseContent.map(s => s" Content : $s").getOrElse("")}", cause) + with ApiReturnWithHeaders + +sealed case class ApiMethod(value: String) + +object ApiMethods { + val CONNECT = ApiMethod("CONNECT") + val DELETE = ApiMethod("DELETE") + val GET = ApiMethod("GET") + val HEAD = ApiMethod("HEAD") + val OPTIONS = ApiMethod("OPTIONS") + val PATCH = ApiMethod("PATCH") + val POST = ApiMethod("POST") + val PUT = ApiMethod("PUT") + val TRACE = ApiMethod("TRACE") +} + +/** + * This trait needs to be added to any model defined by the api. + */ +trait ApiModel + +/** + * Single trait defining a credential that can be transformed to a paramName / paramValue tuple + */ +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 + +} + + +/** + * Case class used to unapply numeric values only in pattern matching + * + * @param value the string representation of the numeric value + */ +sealed case class NumericValue(value: String) { + override def toString: String = value +} + +object NumericValue { + def unapply(n: Any): Option[NumericValue] = n match { + case (_: Int | _: Long | _: Float | _: Double | _: Boolean | _: Byte) => Some(NumericValue(String.valueOf(n))) + case _ => None + } +} + +/** + * Used for params being arrays + */ +sealed case class ArrayValues(values: Seq[Any], format: CollectionFormat = CollectionFormats.CSV) + +object ArrayValues { + def apply(values: Option[Seq[Any]], format: CollectionFormat): ArrayValues = + ArrayValues(values.getOrElse(Seq.empty), format) + + def apply(values: Option[Seq[Any]]): ArrayValues = ArrayValues(values, CollectionFormats.CSV) +} + + +/** + * Defines how arrays should be rendered in query strings. + */ +sealed trait CollectionFormat + +trait MergedArrayFormat extends CollectionFormat { + def separator: String +} + +object CollectionFormats { + + case object CSV extends MergedArrayFormat { + override val separator = "," + } + + case object TSV extends MergedArrayFormat { + override val separator = "\t" + } + + case object SSV extends MergedArrayFormat { + override val separator = " " + } + + case object PIPES extends MergedArrayFormat { + override val separator = "|" + } + + case object MULTI extends CollectionFormat + +} + +object ParametersMap { + + /** + * Pimp parameters maps (Map[String, Any]) in order to transform them in a sequence of String -> Any tuples, + * with valid url-encoding, arrays handling, files preservation, ... + */ + implicit class ParametersMapImprovements(val m: Map[String, Any]) { + + def asFormattedParamsList: List[(String, Any)] = m.toList.flatMap(formattedParams) + + def asFormattedParams: Map[String, Any] = m.flatMap(formattedParams) + + private def urlEncode(v: Any) = URLEncoder.encode(String.valueOf(v), "utf-8").replaceAll("\\+", "%20") + + private def formattedParams(tuple: (String, Any)): Seq[(String, Any)] = formattedParams(tuple._1, tuple._2) + + private def formattedParams(name: String, value: Any): Seq[(String, Any)] = value match { + case arr: ArrayValues => + arr.format match { + case CollectionFormats.MULTI => arr.values.flatMap(formattedParams(name, _)) + case format: MergedArrayFormat => Seq((name, arr.values.mkString(format.separator))) + } + case None => Seq.empty + case Some(opt) => formattedParams(name, opt) + case s: Seq[Any] => formattedParams(name, ArrayValues(s)) + case v: String => Seq((name, urlEncode(v))) + case v: UUID => formattedParams(name, v.toString) + case NumericValue(v) => Seq((name, urlEncode(v))) + case f: File => Seq((name, f)) + case m: ApiModel => Seq((name, m)) + } + } + +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/ApiResponse.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/ApiResponse.scala new file mode 100644 index 00000000000..fd96f81da8e --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/ApiResponse.scala @@ -0,0 +1,21 @@ +/** + * 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.model + +import org.openapitools.client.core.ApiModel + +case class ApiResponse ( + code: Option[Int] = None, + `type`: Option[String] = None, + message: Option[String] = None +) extends ApiModel + diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Category.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Category.scala new file mode 100644 index 00000000000..8f5791b43c7 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Category.scala @@ -0,0 +1,20 @@ +/** + * 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.model + +import org.openapitools.client.core.ApiModel + +case class Category ( + id: Option[Long] = None, + name: Option[String] = None +) extends ApiModel + diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Order.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Order.scala new file mode 100644 index 00000000000..370abcfb04a --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Order.scala @@ -0,0 +1,36 @@ +/** + * 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.model + +import java.time.OffsetDateTime +import org.openapitools.client.core.ApiModel + +case class Order ( + id: Option[Long] = None, + petId: Option[Long] = None, + quantity: Option[Int] = None, + shipDate: Option[OffsetDateTime] = None, + /* Order Status */ + status: Option[OrderEnums.Status] = None, + complete: Option[Boolean] = None +) extends ApiModel + +object OrderEnums { + + type Status = Status.Value + object Status extends Enumeration { + val Placed = Value("placed") + val Approved = Value("approved") + val Delivered = Value("delivered") + } + +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Pet.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Pet.scala new file mode 100644 index 00000000000..9422e64d58b --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Pet.scala @@ -0,0 +1,35 @@ +/** + * 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.model + +import org.openapitools.client.core.ApiModel + +case class Pet ( + id: Option[Long] = None, + category: Option[Category] = None, + name: String, + photoUrls: Seq[String], + tags: Option[Seq[Tag]] = None, + /* pet status in the store */ + status: Option[PetEnums.Status] = None +) extends ApiModel + +object PetEnums { + + type Status = Status.Value + object Status extends Enumeration { + val Available = Value("available") + val Pending = Value("pending") + val Sold = Value("sold") + } + +} diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Tag.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Tag.scala new file mode 100644 index 00000000000..10e6455e072 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/Tag.scala @@ -0,0 +1,20 @@ +/** + * 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.model + +import org.openapitools.client.core.ApiModel + +case class Tag ( + id: Option[Long] = None, + name: Option[String] = None +) extends ApiModel + diff --git a/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/User.scala b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/User.scala new file mode 100644 index 00000000000..d8dafdc59c4 --- /dev/null +++ b/samples/client/petstore/scala-pekko/src/main/scala/org/openapitools/client/model/User.scala @@ -0,0 +1,27 @@ +/** + * 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.model + +import org.openapitools.client.core.ApiModel + +case class User ( + id: Option[Long] = None, + username: Option[String] = None, + firstName: Option[String] = None, + lastName: Option[String] = None, + email: Option[String] = None, + password: Option[String] = None, + phone: Option[String] = None, + /* User Status */ + userStatus: Option[Int] = None +) extends ApiModel + diff --git a/website/i18n/en.json b/website/i18n/en.json index af769f8ec26..1599045b79f 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -458,6 +458,10 @@ "title": "Config Options for scala-akka", "sidebar_label": "scala-akka" }, + "generators/scala-pekko": { + "title": "Config Options for scala-pekko", + "sidebar_label": "scala-pekko" + }, "generators/scala-finch": { "title": "Config Options for scala-finch", "sidebar_label": "scala-finch"