From 40cbbbfefdeff2c9cc1f19895adbbc32122b5724 Mon Sep 17 00:00:00 2001 From: Aleksandr Nekrasov Date: Fri, 28 Feb 2020 14:48:55 +0700 Subject: [PATCH] [scala] [template] scala sttp client (#5429) * scala-sttp-client template * invoker for sttp fixed and tests added * clean up pet api test from redunant comments * docs updated * fix artefact name, model comments and redunant generic * code optimization * cross scala versions 2.11 2.12 2.13 * date serializers extracted and joda enabled as default * basic and bearer authorization added, apikey in query supported --- bin/openapi3/scala-sttp-petstore.sh | 32 +++ docs/generators.md | 1 + docs/generators/scala-sttp.md | 216 ++++++++++++++++++ .../languages/ScalaSttpClientCodegen.java | 73 ++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../scala-sttp-client/README.mustache | 107 +++++++++ .../resources/scala-sttp-client/api.mustache | 45 ++++ .../scala-sttp-client/apiInvoker.mustache | 50 ++++ .../scala-sttp-client/build.sbt.mustache | 27 +++ .../enumsSerializers.mustache | 42 ++++ .../scala-sttp-client/javadoc.mustache | 25 ++ .../scala-sttp-client/licenseInfo.mustache | 11 + .../methodParameters.mustache | 1 + .../scala-sttp-client/model.mustache | 50 ++++ .../operationReturnType.mustache | 1 + .../scala-sttp-client/paramCreation.mustache | 1 + .../paramFormCreation.mustache | 1 + .../paramQueryCreation.mustache | 1 + .../scala-sttp-client/requests.mustache | 48 ++++ .../scala-sttp-client/responseState.mustache | 1 + .../scala-sttp-client/serializers.mustache | 57 +++++ .../scala-sttp/.openapi-generator-ignore | 23 ++ .../scala-sttp/.openapi-generator/VERSION | 1 + samples/client/petstore/scala-sttp/README.md | 121 ++++++++++ samples/client/petstore/scala-sttp/build.sbt | 25 ++ .../scala-sttp/project/build.properties | 1 + .../client/api/EnumsSerializers.scala | 51 +++++ .../org/openapitools/client/api/PetApi.scala | 163 +++++++++++++ .../openapitools/client/api/StoreApi.scala | 92 ++++++++ .../org/openapitools/client/api/UserApi.scala | 170 ++++++++++++++ .../openapitools/client/core/ApiInvoker.scala | 60 +++++ .../client/core/Serializers.scala | 29 +++ .../openapitools/client/core/requests.scala | 58 +++++ .../client/model/ApiResponse.scala | 26 +++ .../openapitools/client/model/Category.scala | 25 ++ .../client/model/InlineObject.scala | 23 ++ .../client/model/InlineObject1.scala | 24 ++ .../org/openapitools/client/model/Order.scala | 41 ++++ .../org/openapitools/client/model/Pet.scala | 40 ++++ .../org/openapitools/client/model/Tag.scala | 25 ++ .../org/openapitools/client/model/User.scala | 32 +++ .../src/test/scala/PetApiTest.scala | 99 ++++++++ 42 files changed, 1920 insertions(+) create mode 100644 bin/openapi3/scala-sttp-petstore.sh create mode 100644 docs/generators/scala-sttp.md create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/apiInvoker.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/build.sbt.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/enumsSerializers.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/javadoc.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/licenseInfo.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/methodParameters.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/model.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/operationReturnType.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/paramCreation.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/paramFormCreation.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/paramQueryCreation.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/requests.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/responseState.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-sttp-client/serializers.mustache create mode 100644 samples/client/petstore/scala-sttp/.openapi-generator-ignore create mode 100644 samples/client/petstore/scala-sttp/.openapi-generator/VERSION create mode 100644 samples/client/petstore/scala-sttp/README.md create mode 100644 samples/client/petstore/scala-sttp/build.sbt create mode 100644 samples/client/petstore/scala-sttp/project/build.properties create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/EnumsSerializers.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/PetApi.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/StoreApi.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/UserApi.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/ApiInvoker.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/Serializers.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/requests.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/ApiResponse.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Category.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject1.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Order.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Pet.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Tag.scala create mode 100644 samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/User.scala create mode 100644 samples/client/petstore/scala-sttp/src/test/scala/PetApiTest.scala diff --git a/bin/openapi3/scala-sttp-petstore.sh b/bin/openapi3/scala-sttp-petstore.sh new file mode 100644 index 000000000000..2a9753df2956 --- /dev/null +++ b/bin/openapi3/scala-sttp-petstore.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +SCRIPT="$0" +echo "# START SCRIPT: $SCRIPT" + +while [ -h "$SCRIPT" ] ; do + ls=`ls -ld "$SCRIPT"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=`dirname "$SCRIPT"`/"$link" + fi +done + +if [ ! -d "${APP_DIR}" ]; then + APP_DIR=`dirname "$SCRIPT"`/.. + APP_DIR=`cd "${APP_DIR}"; pwd` +fi + +executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar" + +if [ ! -f "$executable" ] +then + mvn clean package +fi + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="generate --artifact-id "scala-sttp-petstore-client" -t modules/openapi-generator/src/main/resources/scala-sttp-client -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g scala-sttp -o samples/client/petstore/scala-sttp $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/docs/generators.md b/docs/generators.md index c8dd81b0cc7c..ab157a3e4607 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -54,6 +54,7 @@ The following generators are available: * [scala-akka](generators/scala-akka.md) * [scala-gatling](generators/scala-gatling.md) * [scala-httpclient-deprecated (deprecated)](generators/scala-httpclient-deprecated.md) +* [scala-sttp](generators/scala-sttp.md) * [scalaz](generators/scalaz.md) * [swift2-deprecated (deprecated)](generators/swift2-deprecated.md) * [swift3-deprecated (deprecated)](generators/swift3-deprecated.md) diff --git a/docs/generators/scala-sttp.md b/docs/generators/scala-sttp.md new file mode 100644 index 000000000000..bcf665f875b7 --- /dev/null +++ b/docs/generators/scala-sttp.md @@ -0,0 +1,216 @@ +--- +title: Config Options for scala-sttp +sidebar_label: scala-sttp +--- + +| 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| +|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true| +|mainPackage|Top-level package name, which defines 'apiPackage', 'modelPackage', 'invokerPackage'| |org.openapitools.client| +|modelPackage|package for generated models| |null| +|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.DateTime| +|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 + +### 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 +|Array|✓|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 + +### 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 + +### 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/ScalaSttpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java new file mode 100644 index 000000000000..a00f57263a37 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaSttpClientCodegen.java @@ -0,0 +1,73 @@ +package org.openapitools.codegen.languages; + +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.servers.Server; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.CodegenOperation; +import org.openapitools.codegen.SupportingFile; + +import java.io.File; +import java.util.List; + +public class ScalaSttpClientCodegen extends ScalaAkkaClientCodegen implements CodegenConfig { + protected String mainPackage = "org.openapitools.client"; + + public ScalaSttpClientCodegen() { + super(); + } + + + @Override + public void processOpts() { + super.processOpts(); + if (additionalProperties.containsKey("mainPackage")) { + setMainPackage((String) additionalProperties.get("mainPackage")); + additionalProperties.replace("configKeyPath", this.configKeyPath); + apiPackage = mainPackage + ".api"; + modelPackage = mainPackage + ".model"; + invokerPackage = mainPackage + ".core"; + additionalProperties.put("apiPackage", apiPackage); + additionalProperties.put("modelPackage", modelPackage); + } + + if (!additionalProperties.containsKey("java8")) { + additionalProperties.put("joda", "true"); + } + + supportingFiles.clear(); + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); + final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator); + supportingFiles.add(new SupportingFile("requests.mustache", invokerFolder, "requests.scala")); + supportingFiles.add(new SupportingFile("apiInvoker.mustache", invokerFolder, "ApiInvoker.scala")); + final String apiFolder = (sourceFolder + File.separator + apiPackage).replace(".", File.separator); + supportingFiles.add(new SupportingFile("enumsSerializers.mustache", apiFolder, "EnumsSerializers.scala")); + supportingFiles.add(new SupportingFile("serializers.mustache", invokerFolder, "Serializers.scala")); + } + + @Override + public String getName() { + return "scala-sttp"; + } + + @Override + public String getHelp() { + return "Generates a Scala client library base on Sttp."; + } + + @Override + public String encodePath(String input) { + String result = super.encodePath(input); + return result.replace("{","${"); + } + + @Override + public CodegenOperation fromOperation(String path, + String httpMethod, + Operation operation, + List servers) { + CodegenOperation op = super.fromOperation(path, httpMethod, operation, servers); + op.path = encodePath(path); + return op; + } +} 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 c48f628a3aac..89cbc16b8965 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 @@ -126,3 +126,4 @@ org.openapitools.codegen.languages.AsciidocDocumentationCodegen org.openapitools.codegen.languages.FsharpFunctionsServerCodegen org.openapitools.codegen.languages.MarkdownDocumentationCodegen +org.openapitools.codegen.languages.ScalaSttpClientCodegen diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/README.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/README.mustache new file mode 100644 index 000000000000..0a61209568da --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/README.mustache @@ -0,0 +1,107 @@ +# {{artifactId}} + +{{appName}} +- API version: {{appVersion}} +{{^hideGenerationTimestamp}} + - Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} + +{{#appDescriptionWithNewLines}}{{{appDescriptionWithNewLines}}}{{/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 + +## Documentation for API Endpoints + +All URIs are relative to *{{basePath}}* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | **{{operationId}}** | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}} +{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + +## Documentation for Models + +{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md) +{{/model}}{{/models}} + +## Documentation for Authorization + +{{^authMethods}}All endpoints do not require authorization. +{{/authMethods}}Authentication schemes defined for the API: +{{#authMethods}}### {{name}} + +{{#isApiKey}}- **Type**: API key +- **API key parameter name**: {{keyParamName}} +- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}} +{{/isApiKey}} +{{#isBasic}}- **Type**: HTTP basic authentication +{{/isBasic}} +{{#isOAuth}}- **Type**: OAuth +- **Flow**: {{flow}} +- **Authorization URL**: {{authorizationUrl}} +- **Scopes**: {{^scopes}}N/A{{/scopes}} +{{#scopes}} - {{scope}}: {{description}} +{{/scopes}} +{{/isOAuth}} + +{{/authMethods}} + +## Author + +{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}} +{{/hasMore}}{{/apis}}{{/apiInfo}} diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/api.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/api.mustache new file mode 100644 index 000000000000..412bfca507d4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/api.mustache @@ -0,0 +1,45 @@ +{{>licenseInfo}} +package {{package}} + +{{#imports}} +import {{import}} +{{/imports}} +import {{mainPackage}}.core._ +import alias._ +import sttp.client._ +import sttp.model.Method + +{{#operations}} +object {{classname}} { + + def apply(baseUrl: String = "{{{basePath}}}")(implicit serializer: SttpSerializer) = new {{classname}}(baseUrl) +} + +class {{classname}}(baseUrl: String)(implicit serializer: SttpSerializer) { + + import Helpers._ + import serializer._ + +{{#operation}} +{{#javadocRenderer}} +{{>javadoc}} +{{/javadocRenderer}} + def {{operationId}}({{>methodParameters}}): ApiRequestT[{{>operationReturnType}}] = + basicRequest + .method(Method.{{httpMethod.toUpperCase}}, uri"$baseUrl{{{path}}}{{#queryParams.0}}?{{#queryParams}}{{baseName}}=${{{paramName}}}{{^-last}}&{{/-last}}{{/queryParams}}{{/queryParams.0}}{{#isApiKey}}{{#isKeyInQuery}}{{^queryParams.0}}?{{/queryParams.0}}{{#queryParams.0}}&{{/queryParams.0}}{{keyParamName}}=${apiKey.value}&{{/isKeyInQuery}}{{/isApiKey}}") + .contentType({{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{^consumes}}"application/json"{{/consumes}}){{#headerParams}} + .header({{>paramCreation}}){{/headerParams}}{{#authMethods}}{{#isBasic}}{{#isBasicBasic}} + .auth.withCredentials(basicAuth.user, basicAuth.password){{/isBasicBasic}}{{#isBasicBearer}} + .auth.bearer(bearerToken.token){{/isBasicBearer}}{{/isBasic}}{{#isApiKey}}{{#isKeyInHeader}} + .header("{{keyParamName}}", apiKey.value){{/isKeyInHeader}}{{#isKeyInCookie}} + .cookie("{{keyParamName}}", apiKey.value){{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}}{{#formParams.0}} + .body(Map({{#formParams}} + {{>paramFormCreation}},{{/formParams}} + )){{/formParams.0}}{{#bodyParam}} + .body({{paramName}}){{/bodyParam}} + .response(asJson[{{>operationReturnType}}]) + +{{/operation}} +} + +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/apiInvoker.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/apiInvoker.mustache new file mode 100644 index 000000000000..254e92c74aac --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/apiInvoker.mustache @@ -0,0 +1,50 @@ +{{>licenseInfo}} +package {{{mainPackage}}}.core + +import org.json4s._ +import sttp.client._ +import sttp.model.StatusCode +import org.openapitools.client.api.EnumsSerializers +import sttp.client.json4s.SttpJson4sApi +import sttp.client.monad.MonadError + +class SttpSerializer(implicit val format: Formats = DefaultFormats ++ EnumsSerializers.all ++ Serializers.all, + implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization) extends SttpJson4sApi + +class HttpException(val statusCode: StatusCode, val statusText: String, val message: String) extends Exception(s"[$statusCode] $statusText: $message") + +object Helpers { + + // Helper to handle Optional header parameters + implicit class optionalParams(val request: RequestT[Identity, Either[String, String], Nothing]) extends AnyVal { + def header( header: String, optValue: Option[Any]): RequestT[Identity, Either[String, String], Nothing] = { + optValue.map( value => request.header(header, value.toString)).getOrElse(request) + } + } + +} + +object ApiInvoker { + + /** + * Allows request execution without calling apiInvoker.execute(request) + * request.result can be used to get a monad wrapped content. + * + * @param request the apiRequest to be executed + */ + implicit class ApiRequestImprovements[R[_], T](request: RequestT[Identity, Either[ResponseError[Exception], T], Nothing]) { + + def result(implicit backend: SttpBackend[R, Nothing, Nothing]): R[T] = { + val responseT = request.send() + val ME: MonadError[R] = backend.responseMonad + ME.flatMap(responseT) { + response => + response.body match { + case Left(ex) => ME.error[T](new HttpException(response.code, response.statusText, ex.body)) + case Right(value) => ME.unit(value) + } + } + } + } + +} diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/build.sbt.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/build.sbt.mustache new file mode 100644 index 000000000000..00fe48b731da --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/build.sbt.mustache @@ -0,0 +1,27 @@ +version := "{{artifactVersion}}" +name := "{{artifactId}}" +organization := "{{groupId}}" + +scalaVersion := "2.13.0" + +crossScalaVersions := Seq(scalaVersion.value, "2.12.10", "2.11.12") + +libraryDependencies ++= Seq( + "com.softwaremill.sttp.client" %% "core" % "2.0.0", + "com.softwaremill.sttp.client" %% "json4s" % "2.0.0", +{{#joda}} + "joda-time" % "joda-time" % "2.10.1", +{{/joda}} + "org.json4s" %% "json4s-jackson" % "3.6.7", + // test dependencies + "org.scalatest" %% "scalatest" % "3.0.8" % Test, + "junit" % "junit" % "4.13" % "test" +) + +scalacOptions := Seq( + "-unchecked", + "-deprecation", + "-feature" +) + +publishArtifact in (Compile, packageDoc) := false \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/enumsSerializers.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/enumsSerializers.mustache new file mode 100644 index 000000000000..8c7e6f2e41e5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-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-sttp-client/javadoc.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/javadoc.mustache new file mode 100644 index 000000000000..e42fa1dcdcd7 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/javadoc.mustache @@ -0,0 +1,25 @@ +{{#notes}} +{{{notes}}} + +{{/notes}} +Expected answers: +{{#responses}} + code {{code}} : {{{dataType}}} {{#message}}({{{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-sttp-client/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/licenseInfo.mustache new file mode 100644 index 000000000000..835764cfc729 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/licenseInfo.mustache @@ -0,0 +1,11 @@ +/** + * {{{appName}}} + * {{{appDescription}}} + * + * {{#version}}The version of the OpenAPI document: {{{version}}}{{/version}} + * {{#infoEmail}}Contact: {{{infoEmail}}}{{/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-sttp-client/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/methodParameters.mustache new file mode 100644 index 000000000000..54dc2f92a51f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-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}}{{#hasMore}}, {{/hasMore}}{{/allParams}}{{#authMethods.0}})(implicit {{#authMethods}}{{#isApiKey}}apiKey: ApiKeyValue{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}basicAuth: BasicCredentials{{/isBasicBasic}}{{#isBasicBearer}}bearerToken: BearerToken{{/isBasicBearer}}{{/isBasic}}{{#hasMore}}, {{/hasMore}}{{/authMethods}}{{/authMethods.0}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/model.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/model.mustache new file mode 100644 index 000000000000..941266f3306e --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/model.mustache @@ -0,0 +1,50 @@ +{{>licenseInfo}} +package {{package}} + +{{#imports}} +import {{import}} +{{/imports}} +import {{mainPackage}}.core.ApiModel + +{{#models}} +{{#model}} +{{#description}} +{{#javadocRenderer}} +{{#title}} +{{{title}}} +{{/title}} +{{{description}}} +{{/javadocRenderer}} +{{/description}} +case class {{classname}}( + {{#vars}} + {{#description}} + /* {{{description}}} */ + {{/description}} + {{{name}}}: {{^required}}Option[{{/required}}{{^isEnum}}{{dataType}}{{/isEnum}}{{#isEnum}}{{classname}}Enums.{{datatypeWithEnum}}{{/isEnum}}{{^required}}] = None{{/required}}{{#hasMore}},{{/hasMore}} + {{/vars}} +) extends ApiModel + +{{#hasEnums}} +object {{classname}}Enums { + + {{#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-sttp-client/operationReturnType.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/operationReturnType.mustache new file mode 100644 index 000000000000..a8917911853a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/operationReturnType.mustache @@ -0,0 +1 @@ +{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Unit{{/returnType}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/paramCreation.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/paramCreation.mustache new file mode 100644 index 000000000000..68280bd9a36b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/paramCreation.mustache @@ -0,0 +1 @@ +"{{baseName}}", {{#isContainer}}ArrayValues({{{paramName}}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}{{{paramName}}}{{/isContainer}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/paramFormCreation.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/paramFormCreation.mustache new file mode 100644 index 000000000000..a0f1a65c0746 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/paramFormCreation.mustache @@ -0,0 +1 @@ +"{{baseName}}" -> {{#isContainer}}ArrayValues({{{paramName}}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}){{/isContainer}}{{^isContainer}}{{{paramName}}}{{/isContainer}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/paramQueryCreation.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/paramQueryCreation.mustache new file mode 100644 index 000000000000..8a1ea8cfcbf7 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/paramQueryCreation.mustache @@ -0,0 +1 @@ +{{#isContainer}}${ formatQueryArray("{{{baseName}}}",{{{paramName}}}{{#collectionFormat}}, {{collectionFormat.toUpperCase}}{{/collectionFormat}}) }{{/isContainer}}{{^isContainer}}{{baseName}}=${ {{{paramName}}} }{{/isContainer}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/requests.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/requests.mustache new file mode 100644 index 000000000000..31158fd67cbf --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/requests.mustache @@ -0,0 +1,48 @@ +{{>licenseInfo}} +package {{mainPackage}}.core + +import sttp.client.{Identity, RequestT, ResponseError} + +/** + * This trait needs to be added to any model defined by the api. + */ +trait ApiModel + +/** + * Sttp type aliases + */ +object alias { + type ApiRequestT[T] = RequestT[Identity, Either[ResponseError[Exception], T], Nothing] +} + +/** + * Single trait defining a credential that can be transformed to a paramName / paramValue tupple + */ +sealed trait Credentials { + def asQueryParam: Option[(String, String)] = None +} + +sealed case class BasicCredentials(user: String, password: String) extends Credentials + +sealed case class BearerToken(token: String) extends Credentials + +sealed case class ApiKeyCredentials(key: ApiKeyValue, keyName: String, location: ApiKeyLocation) extends Credentials { + override def asQueryParam: Option[(String, String)] = location match { + case ApiKeyLocations.QUERY => Some((keyName, key.value)) + case _ => None + } +} + +sealed case class ApiKeyValue(value: String) + +sealed trait ApiKeyLocation + +object ApiKeyLocations { + + case object QUERY extends ApiKeyLocation + + case object HEADER extends ApiKeyLocation + + case object COOKIE extends ApiKeyLocation + +} diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/responseState.mustache new file mode 100644 index 000000000000..d1b3798e6de1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/responseState.mustache @@ -0,0 +1 @@ +{{#isDefault}}Success{{/isDefault}}{{^isDefault}}Error{{/isDefault}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-sttp-client/serializers.mustache b/modules/openapi-generator/src/main/resources/scala-sttp-client/serializers.mustache new file mode 100644 index 000000000000..a17d77069157 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-sttp-client/serializers.mustache @@ -0,0 +1,57 @@ +package {{invokerPackage}} + +{{#java8}} +import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId} +import java.time.format.DateTimeFormatter +import scala.util.Try +{{/java8}} +{{#joda}} +import org.joda.time.DateTime +import org.joda.time.format.ISODateTimeFormat +{{/joda}} +import org.json4s.{Serializer, CustomSerializer, JNull} +import org.json4s.JsonAST.JString + +object Serializers { + +{{#java8}} + case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( { + case JString(s) => + Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse + Try(LocalDateTime.parse(s).atZone(ZoneId.systemDefault()).toOffsetDateTime) getOrElse (null) + case JNull => null + }, { + case d: OffsetDateTime => + JString(d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)) + })) + + case object LocalDateSerializer extends CustomSerializer[LocalDate]( _ => ( { + case JString(s) => LocalDate.parse(s) + case JNull => null + }, { + case d: LocalDate => + JString(d.format(DateTimeFormatter.ISO_LOCAL_DATE)) + })) +{{/java8}} +{{#joda}} + case object DateTimeSerializer extends CustomSerializer[DateTime](_ => ( { + case JString(s) => + ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(s) + case JNull => null + }, { + case d: org.joda.time.DateTime => + JString(ISODateTimeFormat.dateTime().print(d)) + }) + ) + + case object LocalDateSerializer extends CustomSerializer[org.joda.time.LocalDate](_ => ( { + case JString(s) => org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate(s) + case JNull => null + }, { + case d: org.joda.time.LocalDate => JString(d.toString("yyyy-MM-dd")) + })) +{{/joda}} + + def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ LocalDateSerializer :+ DateTimeSerializer + +} diff --git a/samples/client/petstore/scala-sttp/.openapi-generator-ignore b/samples/client/petstore/scala-sttp/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/scala-sttp/.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-sttp/.openapi-generator/VERSION b/samples/client/petstore/scala-sttp/.openapi-generator/VERSION new file mode 100644 index 000000000000..bfbf77eb7fad --- /dev/null +++ b/samples/client/petstore/scala-sttp/.openapi-generator/VERSION @@ -0,0 +1 @@ +4.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/client/petstore/scala-sttp/README.md b/samples/client/petstore/scala-sttp/README.md new file mode 100644 index 000000000000..b3d417a26fe5 --- /dev/null +++ b/samples/client/petstore/scala-sttp/README.md @@ -0,0 +1,121 @@ +# scala-sttp-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-sttp-petstore-client + 1.0.0 + compile + +``` + +### Gradle users + +Add this dependency to your project's build file: + +```groovy +compile "org.openapitools:scala-sttp-petstore-client:1.0.0" +``` + +### SBT users + +```scala +libraryDependencies += "org.openapitools" % "scala-sttp-petstore-client" % "1.0.0" +``` + +## Getting Started + +## Documentation for API Endpoints + +All URIs are relative to *http://petstore.swagger.io/v2* + +Class | Method | HTTP request | Description +------------ | ------------- | ------------- | ------------- +*PetApi* | **addPet** | **POST** /pet | Add a new pet to the store +*PetApi* | **deletePet** | **DELETE** /pet/${petId} | Deletes a pet +*PetApi* | **findPetsByStatus** | **GET** /pet/findByStatus | Finds Pets by status +*PetApi* | **findPetsByTags** | **GET** /pet/findByTags | Finds Pets by tags +*PetApi* | **getPetById** | **GET** /pet/${petId} | Find pet by ID +*PetApi* | **updatePet** | **PUT** /pet | Update an existing pet +*PetApi* | **updatePetWithForm** | **POST** /pet/${petId} | Updates a pet in the store with form data +*PetApi* | **uploadFile** | **POST** /pet/${petId}/uploadImage | uploads an image +*StoreApi* | **deleteOrder** | **DELETE** /store/order/${orderId} | Delete purchase order by ID +*StoreApi* | **getInventory** | **GET** /store/inventory | Returns pet inventories by status +*StoreApi* | **getOrderById** | **GET** /store/order/${orderId} | Find purchase order by ID +*StoreApi* | **placeOrder** | **POST** /store/order | Place an order for a pet +*UserApi* | **createUser** | **POST** /user | Create user +*UserApi* | **createUsersWithArrayInput** | **POST** /user/createWithArray | Creates list of users with given input array +*UserApi* | **createUsersWithListInput** | **POST** /user/createWithList | Creates list of users with given input array +*UserApi* | **deleteUser** | **DELETE** /user/${username} | Delete user +*UserApi* | **getUserByName** | **GET** /user/${username} | Get user by user name +*UserApi* | **loginUser** | **GET** /user/login | Logs user into the system +*UserApi* | **logoutUser** | **GET** /user/logout | Logs out current logged in user session +*UserApi* | **updateUser** | **PUT** /user/${username} | Updated user + + +## Documentation for Models + + - [ApiResponse](ApiResponse.md) + - [Category](Category.md) + - [InlineObject](InlineObject.md) + - [InlineObject1](InlineObject1.md) + - [Order](Order.md) + - [Pet](Pet.md) + - [Tag](Tag.md) + - [User](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-sttp/build.sbt b/samples/client/petstore/scala-sttp/build.sbt new file mode 100644 index 000000000000..6964f664a1d4 --- /dev/null +++ b/samples/client/petstore/scala-sttp/build.sbt @@ -0,0 +1,25 @@ +version := "1.0.0" +name := "scala-sttp-petstore-client" +organization := "org.openapitools" + +scalaVersion := "2.13.0" + +crossScalaVersions := Seq(scalaVersion.value, "2.12.10", "2.11.12") + +libraryDependencies ++= Seq( + "com.softwaremill.sttp.client" %% "core" % "2.0.0", + "com.softwaremill.sttp.client" %% "json4s" % "2.0.0", + "joda-time" % "joda-time" % "2.10.1", + "org.json4s" %% "json4s-jackson" % "3.6.7", + // test dependencies + "org.scalatest" %% "scalatest" % "3.0.8" % Test, + "junit" % "junit" % "4.13" % "test" +) + +scalacOptions := Seq( + "-unchecked", + "-deprecation", + "-feature" +) + +publishArtifact in (Compile, packageDoc) := false \ No newline at end of file diff --git a/samples/client/petstore/scala-sttp/project/build.properties b/samples/client/petstore/scala-sttp/project/build.properties new file mode 100644 index 000000000000..c0bab04941d7 --- /dev/null +++ b/samples/client/petstore/scala-sttp/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.2.8 diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/EnumsSerializers.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/EnumsSerializers.scala new file mode 100644 index 000000000000..71ad618e31fb --- /dev/null +++ b/samples/client/petstore/scala-sttp/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-sttp/src/main/scala/org/openapitools/client/api/PetApi.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/PetApi.scala new file mode 100644 index 000000000000..5211d09c9c5c --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/PetApi.scala @@ -0,0 +1,163 @@ +/** + * 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 alias._ +import sttp.client._ +import sttp.model.Method + +object PetApi { + + def apply(baseUrl: String = "http://petstore.swagger.io/v2")(implicit serializer: SttpSerializer) = new PetApi(baseUrl) +} + +class PetApi(baseUrl: String)(implicit serializer: SttpSerializer) { + + import Helpers._ + import serializer._ + + /** + * 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): ApiRequestT[Pet] = + basicRequest + .method(Method.POST, uri"$baseUrl/pet") + .contentType("application/json") + .body(pet) + .response(asJson[Pet]) + + /** + * Expected answers: + * code 400 : (Invalid pet value) + * + * @param petId Pet id to delete + * @param apiKey + */ + def deletePet(petId: Long, apiKey: Option[String] = None): ApiRequestT[Unit] = + basicRequest + .method(Method.DELETE, uri"$baseUrl/pet/${petId}") + .contentType("application/json") + .header("api_key", apiKey) + .response(asJson[Unit]) + + /** + * 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]): ApiRequestT[Seq[Pet]] = + basicRequest + .method(Method.GET, uri"$baseUrl/pet/findByStatus?status=$status") + .contentType("application/json") + .response(asJson[Seq[Pet]]) + + /** + * 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]): ApiRequestT[Seq[Pet]] = + basicRequest + .method(Method.GET, uri"$baseUrl/pet/findByTags?tags=$tags") + .contentType("application/json") + .response(asJson[Seq[Pet]]) + + /** + * 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): ApiRequestT[Pet] = + basicRequest + .method(Method.GET, uri"$baseUrl/pet/${petId}") + .contentType("application/json") + .header("api_key", apiKey.value) + .response(asJson[Pet]) + + /** + * 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): ApiRequestT[Pet] = + basicRequest + .method(Method.PUT, uri"$baseUrl/pet") + .contentType("application/json") + .body(pet) + .response(asJson[Pet]) + + /** + * Expected answers: + * 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): ApiRequestT[Unit] = + basicRequest + .method(Method.POST, uri"$baseUrl/pet/${petId}") + .contentType("application/x-www-form-urlencoded") + .body(Map( + "name" -> name, + "status" -> status, + )) + .response(asJson[Unit]) + + /** + * 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): ApiRequestT[ApiResponse] = + basicRequest + .method(Method.POST, uri"$baseUrl/pet/${petId}/uploadImage") + .contentType("multipart/form-data") + .body(Map( + "additionalMetadata" -> additionalMetadata, + "file" -> file, + )) + .response(asJson[ApiResponse]) + +} + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/StoreApi.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/StoreApi.scala new file mode 100644 index 000000000000..907cc9f42f0f --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/StoreApi.scala @@ -0,0 +1,92 @@ +/** + * 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 alias._ +import sttp.client._ +import sttp.model.Method + +object StoreApi { + + def apply(baseUrl: String = "http://petstore.swagger.io/v2")(implicit serializer: SttpSerializer) = new StoreApi(baseUrl) +} + +class StoreApi(baseUrl: String)(implicit serializer: SttpSerializer) { + + import Helpers._ + import serializer._ + + /** + * 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): ApiRequestT[Unit] = + basicRequest + .method(Method.DELETE, uri"$baseUrl/store/order/${orderId}") + .contentType("application/json") + .response(asJson[Unit]) + + /** + * 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): ApiRequestT[Map[String, Int]] = + basicRequest + .method(Method.GET, uri"$baseUrl/store/inventory") + .contentType("application/json") + .header("api_key", apiKey.value) + .response(asJson[Map[String, Int]]) + + /** + * For valid response try integer IDs with value <= 5 or > 10. Other values will generated 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): ApiRequestT[Order] = + basicRequest + .method(Method.GET, uri"$baseUrl/store/order/${orderId}") + .contentType("application/json") + .response(asJson[Order]) + + /** + * Expected answers: + * code 200 : Order (successful operation) + * code 400 : (Invalid Order) + * + * @param order order placed for purchasing the pet + */ + def placeOrder(order: Order): ApiRequestT[Order] = + basicRequest + .method(Method.POST, uri"$baseUrl/store/order") + .contentType("application/json") + .body(order) + .response(asJson[Order]) + +} + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/UserApi.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/UserApi.scala new file mode 100644 index 000000000000..7b7df3b8e149 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/api/UserApi.scala @@ -0,0 +1,170 @@ +/** + * 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.User +import org.openapitools.client.core._ +import alias._ +import sttp.client._ +import sttp.model.Method + +object UserApi { + + def apply(baseUrl: String = "http://petstore.swagger.io/v2")(implicit serializer: SttpSerializer) = new UserApi(baseUrl) +} + +class UserApi(baseUrl: String)(implicit serializer: SttpSerializer) { + + import Helpers._ + import serializer._ + + /** + * 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): ApiRequestT[Unit] = + basicRequest + .method(Method.POST, uri"$baseUrl/user") + .contentType("application/json") + .cookie("AUTH_KEY", apiKey.value) + .body(user) + .response(asJson[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): ApiRequestT[Unit] = + basicRequest + .method(Method.POST, uri"$baseUrl/user/createWithArray") + .contentType("application/json") + .cookie("AUTH_KEY", apiKey.value) + .body(user) + .response(asJson[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): ApiRequestT[Unit] = + basicRequest + .method(Method.POST, uri"$baseUrl/user/createWithList") + .contentType("application/json") + .cookie("AUTH_KEY", apiKey.value) + .body(user) + .response(asJson[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): ApiRequestT[Unit] = + basicRequest + .method(Method.DELETE, uri"$baseUrl/user/${username}") + .contentType("application/json") + .cookie("AUTH_KEY", apiKey.value) + .response(asJson[Unit]) + + /** + * 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): ApiRequestT[User] = + basicRequest + .method(Method.GET, uri"$baseUrl/user/${username}") + .contentType("application/json") + .response(asJson[User]) + + /** + * 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 toekn 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): ApiRequestT[String] = + basicRequest + .method(Method.GET, uri"$baseUrl/user/login?username=$username&password=$password") + .contentType("application/json") + .response(asJson[String]) + + /** + * Expected answers: + * code 0 : (successful operation) + * + * Available security schemes: + * auth_cookie (apiKey) + */ + def logoutUser()(implicit apiKey: ApiKeyValue): ApiRequestT[Unit] = + basicRequest + .method(Method.GET, uri"$baseUrl/user/logout") + .contentType("application/json") + .cookie("AUTH_KEY", apiKey.value) + .response(asJson[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): ApiRequestT[Unit] = + basicRequest + .method(Method.PUT, uri"$baseUrl/user/${username}") + .contentType("application/json") + .cookie("AUTH_KEY", apiKey.value) + .body(user) + .response(asJson[Unit]) + +} + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/ApiInvoker.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/ApiInvoker.scala new file mode 100644 index 000000000000..dc98ff4d1365 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/ApiInvoker.scala @@ -0,0 +1,60 @@ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +package org.openapitools.client.core + +import org.json4s._ +import sttp.client._ +import sttp.model.StatusCode +import org.openapitools.client.api.EnumsSerializers +import sttp.client.json4s.SttpJson4sApi +import sttp.client.monad.MonadError + +class SttpSerializer(implicit val format: Formats = DefaultFormats ++ EnumsSerializers.all ++ Serializers.all, + implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization) extends SttpJson4sApi + +class HttpException(val statusCode: StatusCode, val statusText: String, val message: String) extends Exception(s"[$statusCode] $statusText: $message") + +object Helpers { + + // Helper to handle Optional header parameters + implicit class optionalParams(val request: RequestT[Identity, Either[String, String], Nothing]) extends AnyVal { + def header( header: String, optValue: Option[Any]): RequestT[Identity, Either[String, String], Nothing] = { + optValue.map( value => request.header(header, value.toString)).getOrElse(request) + } + } + +} + +object ApiInvoker { + + /** + * Allows request execution without calling apiInvoker.execute(request) + * request.result can be used to get a monad wrapped content. + * + * @param request the apiRequest to be executed + */ + implicit class ApiRequestImprovements[R[_], T](request: RequestT[Identity, Either[ResponseError[Exception], T], Nothing]) { + + def result(implicit backend: SttpBackend[R, Nothing, Nothing]): R[T] = { + val responseT = request.send() + val ME: MonadError[R] = backend.responseMonad + ME.flatMap(responseT) { + response => + response.body match { + case Left(ex) => ME.error[T](new HttpException(response.code, response.statusText, ex.body)) + case Right(value) => ME.unit(value) + } + } + } + } + +} diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/Serializers.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/Serializers.scala new file mode 100644 index 000000000000..80188ba5e6a1 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/Serializers.scala @@ -0,0 +1,29 @@ +package org.openapitools.client.core + +import org.joda.time.DateTime +import org.joda.time.format.ISODateTimeFormat +import org.json4s.{Serializer, CustomSerializer, JNull} +import org.json4s.JsonAST.JString + +object Serializers { + + case object DateTimeSerializer extends CustomSerializer[DateTime](_ => ( { + case JString(s) => + ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(s) + case JNull => null + }, { + case d: org.joda.time.DateTime => + JString(ISODateTimeFormat.dateTime().print(d)) + }) + ) + + case object LocalDateSerializer extends CustomSerializer[org.joda.time.LocalDate](_ => ( { + case JString(s) => org.joda.time.format.DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate(s) + case JNull => null + }, { + case d: org.joda.time.LocalDate => JString(d.toString("yyyy-MM-dd")) + })) + + def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ LocalDateSerializer :+ DateTimeSerializer + +} diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/requests.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/requests.scala new file mode 100644 index 000000000000..1f45be8103e1 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/core/requests.scala @@ -0,0 +1,58 @@ +/** + * OpenAPI Petstore + * This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +package org.openapitools.client.core + +import sttp.client.{Identity, RequestT, ResponseError} + +/** + * This trait needs to be added to any model defined by the api. + */ +trait ApiModel + +/** + * Sttp type aliases + */ +object alias { + type ApiRequestT[T] = RequestT[Identity, Either[ResponseError[Exception], T], Nothing] +} + +/** + * Single trait defining a credential that can be transformed to a paramName / paramValue tupple + */ +sealed trait Credentials { + def asQueryParam: Option[(String, String)] = None +} + +sealed case class BasicCredentials(user: String, password: String) extends Credentials + +sealed case class BearerToken(token: String) extends Credentials + +sealed case class ApiKeyCredentials(key: ApiKeyValue, keyName: String, location: ApiKeyLocation) extends Credentials { + override def asQueryParam: Option[(String, String)] = location match { + case ApiKeyLocations.QUERY => Some((keyName, key.value)) + case _ => None + } +} + +sealed case class ApiKeyValue(value: String) + +sealed trait ApiKeyLocation + +object ApiKeyLocations { + + case object QUERY extends ApiKeyLocation + + case object HEADER extends ApiKeyLocation + + case object COOKIE extends ApiKeyLocation + +} diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/ApiResponse.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/ApiResponse.scala new file mode 100644 index 000000000000..3a3b6d6f499a --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/ApiResponse.scala @@ -0,0 +1,26 @@ +/** + * 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 + + /** + * An uploaded response + * Describes the result of uploading an image resource + */ +case class ApiResponse( + code: Option[Int] = None, + `type`: Option[String] = None, + message: Option[String] = None +) extends ApiModel + + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Category.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Category.scala new file mode 100644 index 000000000000..011164617cfb --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Category.scala @@ -0,0 +1,25 @@ +/** + * 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 + + /** + * Pet category + * A category for a pet + */ +case class Category( + id: Option[Long] = None, + name: Option[String] = None +) extends ApiModel + + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject.scala new file mode 100644 index 000000000000..a8c5493161a1 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject.scala @@ -0,0 +1,23 @@ +/** + * 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 InlineObject( + /* Updated name of the pet */ + name: Option[String] = None, + /* Updated status of the pet */ + status: Option[String] = None +) extends ApiModel + + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject1.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject1.scala new file mode 100644 index 000000000000..480cf8c2e10a --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/InlineObject1.scala @@ -0,0 +1,24 @@ +/** + * 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.io.File +import org.openapitools.client.core.ApiModel + +case class InlineObject1( + /* Additional data to pass to server */ + additionalMetadata: Option[String] = None, + /* file to upload */ + file: Option[File] = None +) extends ApiModel + + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Order.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Order.scala new file mode 100644 index 000000000000..b8f11b0b3c38 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Order.scala @@ -0,0 +1,41 @@ +/** + * 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.joda.time.DateTime +import org.openapitools.client.core.ApiModel + + /** + * Pet Order + * An order for a pets from the pet store + */ +case class Order( + id: Option[Long] = None, + petId: Option[Long] = None, + quantity: Option[Int] = None, + shipDate: Option[DateTime] = 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-sttp/src/main/scala/org/openapitools/client/model/Pet.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Pet.scala new file mode 100644 index 000000000000..75b528c3c0a1 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Pet.scala @@ -0,0 +1,40 @@ +/** + * 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 + + /** + * a Pet + * A pet for sale in the pet store + */ +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-sttp/src/main/scala/org/openapitools/client/model/Tag.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Tag.scala new file mode 100644 index 000000000000..299ee5161a8b --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/Tag.scala @@ -0,0 +1,25 @@ +/** + * 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 + + /** + * Pet Tag + * A tag for a pet + */ +case class Tag( + id: Option[Long] = None, + name: Option[String] = None +) extends ApiModel + + diff --git a/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/User.scala b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/User.scala new file mode 100644 index 000000000000..bd2e6c3ba2a7 --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/main/scala/org/openapitools/client/model/User.scala @@ -0,0 +1,32 @@ +/** + * 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 + + /** + * a User + * A User who is purchasing from the pet store + */ +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/samples/client/petstore/scala-sttp/src/test/scala/PetApiTest.scala b/samples/client/petstore/scala-sttp/src/test/scala/PetApiTest.scala new file mode 100644 index 000000000000..f03ae223b51a --- /dev/null +++ b/samples/client/petstore/scala-sttp/src/test/scala/PetApiTest.scala @@ -0,0 +1,99 @@ +import org.junit.runner.RunWith +import org.openapitools.client.api._ +import org.openapitools.client.core.{ApiInvoker, ApiKeyValue, SttpSerializer} +import org.openapitools.client.model._ +import org.scalatest.Inspectors._ +import org.scalatest._ +import org.scalatest.junit.JUnitRunner +import sttp.client.{HttpURLConnectionBackend, Identity, NothingT, SttpBackend} + +@RunWith(classOf[JUnitRunner]) +class PetApiTest extends AsyncFlatSpec with Matchers { + + implicit val sttpSerializer: SttpSerializer = new SttpSerializer + implicit val backend: SttpBackend[Identity, Nothing, NothingT] = HttpURLConnectionBackend() + val api = new PetApi("https://petstore3.swagger.io/api/v3") + + implicit val apiKey: ApiKeyValue = ApiKeyValue("api-key") + + import ApiInvoker._ + + behavior of "PetApi" + + it should "add and fetch a pet" in { + val petId = 1000 + val createdPet = Pet( + Some(petId), + Some(Category(Some(1), Some("sold"))), + "dragon", + (for (i <- 1 to 10) yield "http://foo.com/photo/" + i).toList, + Some((for (i <- 1 to 5) yield org.openapitools.client.model.Tag(Some(i), Some("tag-" + i))).toList), + Some(PetEnums.Status.Sold) + ) + + val addPetRequest = api.addPet(createdPet) + val getPetRequest = api.getPetById(petId) + + addPetRequest.result + val pet = getPetRequest.result + + pet should have( + 'id(createdPet.id), + 'status(createdPet.status), + 'category(createdPet.category), + 'name(createdPet.name) + ) + pet.tags should not be empty + pet.tags.get should contain theSameElementsInOrderAs createdPet.tags.get + pet.photoUrls should contain theSameElementsInOrderAs createdPet.photoUrls + } + + it should "update a pet" in { + val petId = (Math.random() * 1000000000).toLong + val createdPetObj = Pet( + Some(petId), + Some(Category(Some(1), Some("sold"))), + "programmer", + (for (i <- 1 to 10) yield "http://foo.com/photo/" + i).toList, + Some((for (i <- 1 to 5) yield org.openapitools.client.model.Tag(Some(i), Some("tag-" + i))).toList), + Some(PetEnums.Status.Available) + ) + + val createdPet = api.addPet(createdPetObj).result + val pet = api.getPetById(createdPet.id.get).result + val updatedPetObj = pet.copy(status = Some(PetEnums.Status.Sold), name = "developer") + val updatedPet = api.updatePet(updatedPetObj).result + val updatedRequested = api.getPetById(createdPet.id.get).result + + pet.name should be("programmer") + pet.status should be(Some(PetEnums.Status.Available)) + + updatedPet.name should be("developer") + updatedPet.status should be(Some(PetEnums.Status.Sold)) + + updatedRequested.name should be("developer") + updatedRequested.status should be(Some(PetEnums.Status.Sold)) + + } + + it should "find pets by status" in { + val pets = api.findPetsByStatus(List("available")).result + pets should not be empty + + + forAll(pets.toList) { pet => + pet.status should contain(PetEnums.Status.Available) + } + } + + it should "find pets by tag" in { + val pets = api.findPetsByTags(List("tag1", "tag2")).result + pets should not be empty + + forAll(pets.toList) { pet => + val tagNames = pet.tags.toList.flatten.map(_.name).collect { case Some(name) => name } + tagNames should contain atLeastOneOf("tag1", "tag2") + } + } + +} \ No newline at end of file