From 94b05e589daaf93a73488c9527ae83fb80542c25 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Fri, 6 Sep 2024 13:19:41 -0400 Subject: [PATCH 01/18] fix attemp --- docs/templating.md | 2 +- .../ScalaHttp4sScala3ClientCodegen.java | 450 ++++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../scala-http4s-scala3/api.mustache | 62 +++ .../scala-http4s-scala3/auth_model.mustache | 7 + .../scala-http4s-scala3/baseClient.mustache | 70 +++ .../scala-http4s-scala3/build.sbt.mustache | 44 ++ .../failedRequest.mustache | 5 + .../jsonEntityEncoderDecoder.mustache | 26 + .../methodParameters.mustache | 1 + .../scala-http4s-scala3/model.mustache | 68 +++ .../models_package.mustache | 25 + .../operationReturnType.mustache | 1 + .../paramCreation.mustache | 1 + .../pathParamCreation.mustache | 1 + .../project/build.properties.mustache | 1 + ...p4sScala3ClientCodegenOptionsProvider.java | 53 +++ ...alaHttp4sScala3ClientCodegenModelTest.java | 31 ++ .../ScalaHttp4sScala3ClientCodegenTest.java | 19 + 19 files changed, 867 insertions(+), 1 deletion(-) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonEntityEncoderDecoder.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/operationReturnType.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramCreation.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/pathParamCreation.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/project/build.properties.mustache create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sScala3ClientCodegenOptionsProvider.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java diff --git a/docs/templating.md b/docs/templating.md index eab039bb2222..5465cbe81074 100644 --- a/docs/templating.md +++ b/docs/templating.md @@ -257,7 +257,7 @@ cd ~/.openapi-generator/example gradle assemble # or, regenerate the wrapper gradle wrapper --gradle-version 4.8 --distribution-type all -./gradlew assemble +./gradlew assemblef ``` You should see a log message showing our added dependency being downloaded: diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java new file mode 100644 index 000000000000..03bc92973b8b --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -0,0 +1,450 @@ +/* + * 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 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.util.*; + +public class ScalaHttp4sScala3ClientCodegen extends AbstractScalaCodegen implements CodegenConfig { + protected String mainPackage = "org.openapitools.client"; //TODO: configuable?? + protected String groupId = "org.openapitools"; //TODO: configuable?? + protected String artifactId = "http4s-client-scala3"; //TODO: configuable?? + protected String sourceFolder = "scala"; + protected String sourceSubFolder = "main"; + 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; + public static final String EXCLUDE_SBT = "excludeSbt"; // generate as whole project + public static final String SOURCE_SUBFOLDER = "sourceSubfolder"; // generate as whole project + + private String packageName = mainPackage; + + @SuppressWarnings("hiding") + protected final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sScala3ClientCodegen.class); + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "scala-http4s-scala3"; + } + + public String getHelp() { + return "Generates a scala-http4s-scala3 client."; + } + + public ScalaHttp4sScala3ClientCodegen() { + super(); + + modifyFeatureSet(features -> features + .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom)) + .securityFeatures(EnumSet.noneOf(SecurityFeature.class)) + .excludeGlobalFeatures( + GlobalFeature.XMLStructureDefinitions, + GlobalFeature.Callbacks, + GlobalFeature.LinkObjects, + GlobalFeature.ParameterStyling + ) + .excludeSchemaSupportFeatures( + SchemaSupportFeature.Polymorphism + ) + .excludeParameterFeatures( + ParameterFeature.Cookie + ) + ); + + useOneOfInterfaces = true; + supportsMultipleInheritance = true; + supportsInheritance = true; + supportsMixins = true; + addOneOfInterfaceImports = true; + + outputFolder = "generated-code" + File.separator + "scala-http4s-scala3"; + modelTemplateFiles.put("model.mustache", ".scala"); + apiTemplateFiles.put("api.mustache", ".scala"); + embeddedTemplateDir = templateDir = "scala-http4s-scala3"; + apiPackage = mainPackage; + modelPackage = mainPackage + ".models"; + + setReservedWordsLowerCase( + Arrays.asList( + // Scala + "abstract", "case", "catch", "class", "def", + "do", "else", "extends", "false", "final", + "finally", "for", "forSome", "if", "implicit", + "import", "lazy", "match", "new", "null", + "object", "override", "package", "private", "protected", + "return", "sealed", "super", "this", "throw", + "trait", "try", "true", "type", "val", + "var", "while", "with", "yield", + // Scala-interop languages keywords + "abstract", "continue", "switch", "assert", + "default", "synchronized", "goto", + "break", "double", "implements", "byte", + "public", "throws", "enum", "instanceof", "transient", + "int", "short", "char", "interface", "static", + "void", "finally", "long", "strictfp", "volatile", "const", "float", + "native") + ); + + defaultIncludes = new HashSet<>( + Arrays.asList("double", + "Int", + "Long", + "Float", + "Double", + "char", + "float", + "String", + "boolean", + "Boolean", + "Double", + "Integer", + "Long", + "Float", + "List", + "Set", + "Map") + ); + + typeMapping = new HashMap<>(); + typeMapping.put("string", "String"); + typeMapping.put("boolean", "Boolean"); + typeMapping.put("integer", "Int"); + typeMapping.put("long", "Long"); + typeMapping.put("float", "Float"); + typeMapping.put("double", "Double"); + typeMapping.put("number", "BigDecimal"); + typeMapping.put("decimal", "BigDecimal"); + typeMapping.put("date-time", "ZonedDateTime"); + typeMapping.put("offset-date-time", "OffsetDateTime"); + typeMapping.put("date", "LocalDate"); + typeMapping.put("file", "File"); + typeMapping.put("array", "List"); + typeMapping.put("list", "List"); + typeMapping.put("map", "Map"); + typeMapping.put("object", "Object"); + typeMapping.put("binary", "Array[Byte]"); + typeMapping.put("Date", "LocalDate"); + typeMapping.put("DateTime", "ZonedDateTime"); + typeMapping.put("OffsetDateTime", "OffsetDateTime"); + typeMapping.put("uuid", "UUID"); + + additionalProperties.put("modelPackage", modelPackage()); + additionalProperties.put("apiPackage", apiPackage()); + additionalProperties.put("infoUrl", "http://org.openapitools"); + additionalProperties.put("infoEmail", "team@openapitools.org"); + additionalProperties.put("licenseInfo", "Apache 2.0"); + additionalProperties.put("licenseUrl", "http://apache.org/licenses/LICENSE-2.0.html"); + + + languageSpecificPrimitives = new HashSet<>( + Arrays.asList( + "String", + "Boolean", + "Double", + "Int", + "Integer", + "Long", + "Float", + "Any", + "AnyVal", + "AnyRef", + "Object", + "BigDecimal" + ) + ); + instantiationTypes.put("array", "ArrayList"); + instantiationTypes.put("map", "HashMap"); + + importMapping = new HashMap<>(); + importMapping.put("UUID", "java.util.UUID"); + importMapping.put("URI", "java.net.URI"); + importMapping.put("File", "java.io.File"); + importMapping.put("Date", "java.util.Date"); + importMapping.put("Timestamp", "java.sql.Timestamp"); + importMapping.put("Map", "scala.collection.immutable.Map"); + importMapping.put("HashMap", "scala.collection.immutable.HashMap"); + importMapping.put("Seq", "scala.collection.immutable.Seq"); + importMapping.put("ArrayBuffer", "scala.collection.mutable.ArrayBuffer"); + importMapping.put("DateTime", "java.time.LocalDateTime"); + importMapping.put("LocalDateTime", "java.time.LocalDateTime"); + importMapping.put("LocalDate", "java.time.LocalDate"); + importMapping.put("LocalTime", "java.time.LocalTime"); + importMapping.put("ZonedDateTime", "java.time.ZonedDateTime"); + importMapping.put("OffsetDateTime", "java.time.OffsetDateTime"); + //refined + importMapping.put("Refined", "eu.timepit.refined.api.Refined"); + importMapping.put("And", "eu.timepit.refined.boolean.And"); + importMapping.put("MinSize", "eu.timepit.refined.collection.MinSize"); + importMapping.put("MaxSize", "eu.timepit.refined.collection.MaxSize"); + importMapping.put("MatchesRegex", "eu.timepit.refined.string.MatchesRegex"); + importMapping.put("Greater", "eu.timepit.refined.numeric.Greater"); + importMapping.put("GreaterEqual", "eu.timepit.refined.numeric.GreaterEqual"); + importMapping.put("Less", "eu.timepit.refined.numeric.Less"); + importMapping.put("LessEqual", "eu.timepit.refined.numeric.LessEqual"); + + cliOptions.add(new CliOption(EXCLUDE_SBT, "exclude sbt from generation")); + cliOptions.add(new CliOption(SOURCE_SUBFOLDER, "name of subfolder, for example to generate code in src/scala/generated")); + + //inlineSchemaOption.put("SKIP_SCHEMA_REUSE", "true"); + //inlineSchemaOption.put("REFACTOR_ALLOF_INLINE_SCHEMAS", "true"); + + cliOptions.add(new CliOption("mainPackage", "Main package name").defaultValue("org.openapitools.client")); + } + + @Override + public void processOpts() { + super.processOpts(); + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { + packageName = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME); + + setApiPackage(packageName + ".apis"); + additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); + + setModelPackage(packageName + ".models"); + additionalProperties.put(CodegenConstants.PACKAGE_NAME, modelPackage()); + } else { + additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); + } + + if (additionalProperties.containsKey(SOURCE_SUBFOLDER)) { + sourceSubFolder = (String) additionalProperties.get(SOURCE_SUBFOLDER); + } + additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); + additionalProperties.put("fnCapitalize", new CapitalizeLambda()); + additionalProperties.put("fnCamelize", new CamelizeLambda(true)); + + sourceFolder = "src" + File.separator + sourceSubFolder + File.separator + sourceFolder; + + //supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("baseClient.mustache", packageFileFolderRelative(), "BaseClient.scala")); + supportingFiles.add(new SupportingFile("jsonEntityEncoderDecoder.mustache", packageFileFolderRelative(), "JsonEntityEncoderDecoder.scala")); + //supportingFiles.add(new SupportingFile("model.mustache", modelFileFolderRelative(), "Models.scala")); + supportingFiles.add(new SupportingFile("failedRequest.mustache", modelFileFolderRelative(), "FailedRequest.scala")); + supportingFiles.add(new SupportingFile("auth_model.mustache", modelFileFolderRelative(), "Authorization.scala")); + supportingFiles.add(new SupportingFile("models_package.mustache", modelFileFolderRelative(), "package.scala")); + supportingFiles.add(new SupportingFile("api.mustache", packageFileFolderRelative(), "Api.scala")); + + apiTemplateFiles.put("api.mustache", ".scala"); + + if (!additionalProperties.containsKey(EXCLUDE_SBT) && !Boolean.parseBoolean((String) additionalProperties.get(EXCLUDE_SBT))) { + supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); + supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); + } + additionalProperties.put(CodegenConstants.GROUP_ID, groupId); + additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); + additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); + + + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + apiFileFolderRelative(); + } + + private String apiFileFolderRelative() { + return sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar); + } + + @Override + public String modelFileFolder() { + return outputFolder + File.separator + modelFileFolderRelative(); + } + + private String modelFileFolderRelative() { + return sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar); + } + + private String packageFileFolderRelative() { + return sourceFolder + File.separator + packageName.replace('.', File.separatorChar); + } + + + @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)) { + String inner = getSchemaType(ModelUtils.getSchemaItems(p)); + if (ModelUtils.isSet(p)) { + 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 ba2d139af981..a5287fae892c 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 @@ -123,6 +123,7 @@ org.openapitools.codegen.languages.ScalaPekkoClientCodegen org.openapitools.codegen.languages.ScalaAkkaHttpServerCodegen org.openapitools.codegen.languages.ScalaFinchServerCodegen org.openapitools.codegen.languages.ScalaGatlingCodegen +org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen org.openapitools.codegen.languages.ScalaHttp4sServerCodegen org.openapitools.codegen.languages.ScalaLagomServerCodegen org.openapitools.codegen.languages.ScalaPlayFrameworkServerCodegen diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache new file mode 100644 index 000000000000..d041ff046b22 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -0,0 +1,62 @@ +package {{apiPackage}} + +import cats.effect.Concurrent +import io.circe.Encoder +import org.http4s.Uri +import org.http4s.client.Client as Http4sClient +import {{modelPackage}}.* + +{{#operations}} +trait {{classname}}ApiEndpoints[F[*]] { + +{{#operation}} + def {{operationId}}({{>methodParameters}}, requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] +{{/operation}} + +} +{{/operations}} + +{{#operations}} +class {{classname}}ApiEndpointsImpl[F[*]: Concurrent]( + override val baseUrl: Uri, + auth: Option[Authorization] = None, + defaultHeaders: Seq[(String, String)] = Nil, + httpClient: Http4sClient[F] +) extends BaseClient[F](baseUrl, auth, defaultHeaders, httpClient) with {{classname}}ApiEndpoints[F] { + + import JsonEntityEncoderDecoder.* + + + override def {{operationId}}({{>methodParameters}}, requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] = { + + val headerParameters = Seq( + {{#headerParams}} + {{>paramCreation}} + {{/headerParams}} + ) + + val urlPath = s"{{{path}}}"{{#pathParams}}.replace({{>pathParamCreation}}){{/pathParams}} + + _executeRequest[Unit, {{>operationReturnType}}]( + method = "{{httpMethod.toUpperCase}}", + path = urlPath, + {{#bodyParam}} + body = Some({{paramName}}), + {{/bodyParam}} + queryParameters = {{#queryParams}}{{>paramCreation}}{{/queryParams}}, + requestHeaders = requestHeaders ++ headerParameters) { +{{#responses}} +{{#is2xx}}case r if r.status.code == {{code}} => parseJson[F, {{>operationReturnType}}]("{{apiPackage}}.{{>operationReturnType}}", r){{/is2xx}} +{{#is3xx}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(ErrorResponse(r.status.code)){{/is3xx}} +{{#is4xx}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(ErrorResponse(r.status.code)){{/is4xx}} +{{#is5xx}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(ErrorResponse(r.status.code)){{/is5xx}} +{{#isDefault}}case r => Concurrent[F].raiseError(FailedRequest(r.status.code, s"Unsupported response code ${r.status.code}")){{/isDefault}} +{{/responses}} + } + + } + +} +{{/operations}} + + diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache new file mode 100644 index 000000000000..ef0a98b4a864 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache @@ -0,0 +1,7 @@ +package {{modelPackage}} + +trait Authorization extends _root_.scala.Product with _root_.scala.Serializable + +object Authorization { + final case class Basic(username: String, password: Option[String] = None) extends Authorization +} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache new file mode 100644 index 000000000000..72de02a93ecd --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -0,0 +1,70 @@ +package {{apiPackage}} + +import cats.effect.Concurrent +import io.circe.Encoder +import org.http4s.{Header, Headers, Method, Request, Response, Uri} +import org.http4s.client.Client as Http4sClient +import org.http4s.QueryParamEncoder.* +import org.typelevel.ci.CIString +import java.util.Base64 +import java.nio.charset.StandardCharsets +import {{modelPackage}}.* + +abstract class BaseClient[F[*]: Concurrent]( + val baseUrl: Uri, + auth: Option[Authorization] = None, + defaultHeaders: Seq[(String, String)] = Nil, + httpClient: Http4sClient[F] +) { + + val ApiVersion: String = "{{artifactVersion}}" + + private lazy val defaultApiHeaders = Seq( + ("X-Apidoc-Version", ApiVersion) + ) + + def apiHeaders: Seq[(String, String)] = defaultApiHeaders + + def modifyRequest(request: Request[F]): Request[F] = request + + def _executeRequest[T, U]( + method: String, + path: String, + body: Option[T] = None, + queryParameters: Seq[(String, String)] = Nil, + requestHeaders: Seq[(String, String)] = Nil + )(handler: Response[F] => F[U])(implicit encoder: Encoder[T]): F[U] = { + + val m = Method.fromString(method) match { + case Right(m) => m + case Left(e) => sys.error(e.toString) + } + + val headers = Headers( + ( + apiHeaders ++ + defaultHeaders ++ + requestHeaders + ).groupBy(_._1).map { case (k, l) => Header.Raw(CIString(k), l.last._2) }.toList + ) + + val queryMap = queryParameters.groupBy(_._1).map { case (k, v) => k -> v.map(_._2) } + val uri = (baseUrl / path).setQueryParams(queryMap) + + val request = Request[F](method = m, uri = uri, headers = headers) + + val reqAndMaybeAuth = auth.fold(request) { + case Authorization.Basic(username, passwordOpt) => + val userpass = s"$username:${passwordOpt.getOrElse("")}" + val token = Base64.getEncoder.encodeToString(userpass.getBytes(StandardCharsets.ISO_8859_1)) + request.putHeaders(Header.Raw(CIString("Authorization"), s"Basic $token")) + case a => sys.error("Invalid authorization scheme[" + a.getClass + "]") + } + + import JsonEntityEncoderDecoder.* + val reqAndMaybeAuthAndBody = body.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity) + + httpClient.run(modifyRequest(reqAndMaybeAuthAndBody)).use(handler) + } + +} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache new file mode 100644 index 000000000000..bc38757577f1 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache @@ -0,0 +1,44 @@ +scalaVersion := "3.3.3" + +val CirceVersion = "0.14.9" +val Http4sVersion = "0.23.26" +val RefinedVersion = "0.11.2" + +libraryDependencies ++= Seq( + "org.http4s" %% "http4s-ember-server" % Http4sVersion, + "org.http4s" %% "http4s-ember-client" % Http4sVersion, + "org.http4s" %% "http4s-circe" % Http4sVersion, + "org.http4s" %% "http4s-dsl" % Http4sVersion, + "org.http4s" %% "http4s-client" % Http4sVersion, + "org.typelevel" %% "jawn-fs2" % "2.4.0", + "io.circe" %% "circe-core" % CirceVersion, + "io.circe" %% "circe-generic" % CirceVersion, + "io.circe" %% "circe-parser" % CirceVersion, + "io.circe" %% "circe-refined" % CirceVersion, + "eu.timepit" %% "refined" % RefinedVersion, + "com.beachape" %% "enumeratum-circe" % "1.7.4", + // test dependencies + "org.scalatest" %% "scalatest" % "3.2.19" % "test" +) + +scalacOptions := Seq( + "-encoding", + "UTF-8", + "-deprecation", + "-unchecked", + "-feature", + "-language:existentials,experimental.macros,higherKinds,implicitConversions,postfixOps,adhocExtensions", + "-Yretain-trees", + "-Xmax-inlines:100", + "-Ykind-projector:underscores", + "-source:future" +) + +lazy val root = project + .in(file(".")) + .settings( + version := "{{artifactVersion}}", + name := "{{artifactId}}", + organization := "{{groupId}}", + publishArtifact := false + ) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache new file mode 100644 index 000000000000..787977b928e5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache @@ -0,0 +1,5 @@ +package {{modelPackage}} + +case class FailedRequest(responseCode: Int, message: String, requestUri: Option[java.net.URI] = None) + extends Exception(s"HTTP URL: $requestUri, status: $responseCode; message: $message") + diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonEntityEncoderDecoder.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonEntityEncoderDecoder.mustache new file mode 100644 index 000000000000..b804c4d3d361 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonEntityEncoderDecoder.mustache @@ -0,0 +1,26 @@ +package {{apiPackage}} + +import cats.effect.* +import cats.implicits.* +import io.circe.{Decoder, Encoder} +import org.http4s.{EntityDecoder, EntityEncoder, Response} +import org.http4s.circe as http4sCirce +import {{modelPackage}}.* + +object JsonEntityEncoderDecoder { + + implicit def circeJsonEncoder[F[*]: Concurrent, A](implicit encoder: Encoder[A]): EntityEncoder[F, A] = + http4sCirce.jsonEncoderOf[F, A] + implicit def circeJsonDecoder[F[*]: Concurrent, A](implicit decoder: Decoder[A]): EntityDecoder[F, A] = + http4sCirce.jsonOf[F, A] + + def parseJson[F[*]: Concurrent, T]( + className: String, + r: Response[F] + )(implicit decoder: Decoder[T]): F[T] = r.attemptAs[T].value.flatMap { + case Right(value) => Concurrent[F].pure(value) + case Left(error) => Concurrent[F].raiseError(FailedRequest(r.status.code, s"Invalid json for class[" + className + "]", None)) + } + +} + diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache new file mode 100644 index 000000000000..9a1b04d7747f --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/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-http4s-scala3/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache new file mode 100644 index 000000000000..89825fde1b2c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache @@ -0,0 +1,68 @@ +package {{modelPackage}} + +import enumeratum.values.* +import io.circe.* +import io.circe.Decoder.* +import io.circe.Encoder.* +import io.circe.refined.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + +import java.time.* +{{#imports}} +import {{import}} +{{/imports}} + +{{#models}} +{{#model}} +/** +* {{{description}}} +{{#vars}} +* @param {{name}} {{{description}}} +{{/vars}} +*/ +{{#isEnum}} +sealed abstract class {{classname}}(val value: String) extends StringEnumEntry +case object {{classname}} extends StringEnum[{{classname}}] with StringCirceEnum[{{classname}}] { + {{#allowableValues}} + {{#values}} + case object {{#fnEnumEntry}}{{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}{{/fnEnumEntry}} extends {{name}}("{{.}}") + {{/values}} + {{/allowableValues}} + val values: IndexedSeq[{{classname}}] = findValues +} +{{/isEnum}} +{{^isEnum}} +case class {{classname}}( +{{#vars}} + {{name}}: {{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}{{^-last}},{{/-last}} +{{/vars}} +) + +object {{name}} { + implicit val encoder{{name}}: Encoder[{{name}}] = Encoder.instance { t => + Json.fromFields{ + Seq( + {{#vars}} + {{#required}}Some("{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}" -> t.{{name}}.asJson){{/required}}{{^required}}t.{{name}}.map(v => "{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}" -> v.asJson){{/required}}{{^-last}},{{/-last}} + {{/vars}} + ).flatten + } + } + implicit val decode{{name}}: Decoder[{{name}}] = Decoder.instance { c => + for { + {{#vars}} + {{name}} <- c.downField("{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}").as[{{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}] + {{/vars}} + } yield {{name}}( + {{#vars}} + {{name}} = {{name}}{{^-last}},{{/-last}} + {{/vars}} + ) + } +} + +{{/isEnum}} +{{/model}} +{{/models}} + diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache new file mode 100644 index 000000000000..1fe8a92ee5f5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache @@ -0,0 +1,25 @@ +package {{apiPackage}} + +import io.circe.{Decoder, Encoder} + +package object models { + + implicit val decodeUUID: Decoder[_root_.java.util.UUID] = + Decoder.decodeString.map(str => _root_.java.util.UUID.fromString(str)) + + implicit val encodeUUID: Encoder[_root_.java.util.UUID] = + Encoder.encodeString.contramap[_root_.java.util.UUID](uuid => uuid.toString) + + implicit val decodeInstant: Decoder[_root_.java.time.Instant] = + Decoder.decodeString.map(str => _root_.java.time.OffsetDateTime.parse(str).toInstant) + + implicit val encodeInstant: Encoder[_root_.java.time.Instant] = + Encoder.encodeString.contramap[_root_.java.time.Instant](_.toString) + + implicit val decodeLocalDate: Decoder[_root_.java.time.LocalDate] = + Decoder.decodeString.map(str => _root_.java.time.LocalDate.parse(str)) + + implicit val encodeLocalDate: Encoder[_root_.java.time.LocalDate] = + Encoder.encodeString.contramap[_root_.java.time.LocalDate](_.toString) + +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/operationReturnType.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/operationReturnType.mustache new file mode 100644 index 000000000000..170935cad639 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/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-http4s-scala3/paramCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramCreation.mustache new file mode 100644 index 000000000000..d44490e5397b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/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-http4s-scala3/pathParamCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/pathParamCreation.mustache new file mode 100644 index 000000000000..e6bbc05789f8 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/pathParamCreation.mustache @@ -0,0 +1 @@ +"\{{{baseName}}\}", {{{paramName}}}.toString diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/project/build.properties.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/project/build.properties.mustache new file mode 100644 index 000000000000..4d5f78cc412a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/project/build.properties.mustache @@ -0,0 +1 @@ +sbt.version=1.9.9 \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sScala3ClientCodegenOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sScala3ClientCodegenOptionsProvider.java new file mode 100644 index 000000000000..8c2e00bc783f --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sScala3ClientCodegenOptionsProvider.java @@ -0,0 +1,53 @@ +/* +package org.openapitools.codegen.options; + +import org.openapitools.codegen.CodegenConstants; +import org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class ScalaHttp4sScala3ClientCodegenOptionsProvider implements OptionsProvider { + public static final String PROJECT_NAME_VALUE = "OpenAPI"; + public static final String MODEL_PACKAGE_VALUE = "package"; + 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 MODEL_PROPERTY_NAMING = "PascalCase"; + public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false"; + public static final String PREPEND_FORM_OR_BODY_PARAMETERS_VALUE = "true"; + public static final String DATE_LIBRARY = "java8"; + + @Override + public String getLanguage() { + return "scala-http4s-scala3"; + } + + @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(CodegenConstants.MODEL_PROPERTY_NAMING, MODEL_PROPERTY_NAMING) + .put("dateLibrary", DATE_LIBRARY) + .put(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "true") + .build(); + } + + @Override + public boolean isServer() { + return false; + } +} + +*/ diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java new file mode 100644 index 000000000000..fca5eb70249c --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java @@ -0,0 +1,31 @@ +package org.openapitools.codegen.scala.http4s.scala3; + +import org.openapitools.codegen.*; +import org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen; +import io.swagger.models.*; +import io.swagger.models.properties.*; + +import org.testng.Assert; +import org.testng.annotations.Test; + +@SuppressWarnings("static-method") +public class ScalaHttp4sScala3ClientCodegenModelTest { + + @Test(description = "convert a simple java model") + public void simpleModelTest() { + final Model model = new ModelImpl() + .description("a sample model") + .property("id", new LongProperty()) + .property("name", new StringProperty()) + .required("id") + .required("name"); + final ScalaHttp4sScala3ClientCodegen codegen = new ScalaHttp4sScala3ClientCodegen(); + + + + // TODO: Complete this test. + //Assert.fail("Not implemented."); + } + +} + diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java new file mode 100644 index 000000000000..1665bcd5cf4c --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java @@ -0,0 +1,19 @@ +package org.openapitools.codegen.scala.http4s.scala3; + +import org.openapitools.codegen.*; +import org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen; +import io.swagger.models.*; +import io.swagger.parser.SwaggerParser; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class ScalaHttp4sScala3ClientCodegenTest { + + ScalaHttp4sScala3ClientCodegen codegen = new ScalaHttp4sScala3ClientCodegen(); + + @Test + public void shouldSucceed() throws Exception { + // TODO: Complete this test. + //Assert.fail("Not implemented."); + } +} From d3b401492db124530450148d1f188e5aa2c7a616 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Mon, 9 Sep 2024 14:22:31 -0400 Subject: [PATCH 02/18] use java.time.Instant --- .../ScalaHttp4sScala3ClientCodegen.java | 105 ++++++++---------- .../scala-http4s-scala3/api.mustache | 2 +- .../scala-http4s-scala3/licenseInfo.mustache | 11 ++ .../scala-http4s-scala3/model.mustache | 1 - 4 files changed, 61 insertions(+), 58 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/licenseInfo.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index 03bc92973b8b..cea2d28663f6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -33,29 +33,26 @@ import java.util.*; public class ScalaHttp4sScala3ClientCodegen extends AbstractScalaCodegen implements CodegenConfig { + private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sScala3ClientCodegen.class); + protected String mainPackage = "org.openapitools.client"; //TODO: configuable?? protected String groupId = "org.openapitools"; //TODO: configuable?? protected String artifactId = "http4s-client-scala3"; //TODO: configuable?? protected String sourceFolder = "scala"; protected String sourceSubFolder = "main"; 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; public static final String EXCLUDE_SBT = "excludeSbt"; // generate as whole project public static final String SOURCE_SUBFOLDER = "sourceSubfolder"; // generate as whole project + public static final String MAIN_PACKAGE = "mainPackage"; // generate as whole project private String packageName = mainPackage; - @SuppressWarnings("hiding") - protected final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sScala3ClientCodegen.class); - @Override public CodegenType getTag() { return CodegenType.CLIENT; @@ -96,9 +93,10 @@ public ScalaHttp4sScala3ClientCodegen() { addOneOfInterfaceImports = true; outputFolder = "generated-code" + File.separator + "scala-http4s-scala3"; + embeddedTemplateDir = templateDir = "scala-http4s-scala3"; + modelTemplateFiles.put("model.mustache", ".scala"); apiTemplateFiles.put("api.mustache", ".scala"); - embeddedTemplateDir = templateDir = "scala-http4s-scala3"; apiPackage = mainPackage; modelPackage = mainPackage + ".models"; @@ -143,6 +141,23 @@ public ScalaHttp4sScala3ClientCodegen() { "Map") ); + languageSpecificPrimitives = new HashSet<>( + Arrays.asList( + "String", + "Boolean", + "Double", + "Int", + "Integer", + "Long", + "Float", + "Any", + "AnyVal", + "AnyRef", + "Object", + "BigDecimal" + ) + ); + typeMapping = new HashMap<>(); typeMapping.put("string", "String"); typeMapping.put("boolean", "Boolean"); @@ -152,9 +167,9 @@ public ScalaHttp4sScala3ClientCodegen() { typeMapping.put("double", "Double"); typeMapping.put("number", "BigDecimal"); typeMapping.put("decimal", "BigDecimal"); - typeMapping.put("date-time", "ZonedDateTime"); - typeMapping.put("offset-date-time", "OffsetDateTime"); typeMapping.put("date", "LocalDate"); + typeMapping.put("date-time", "Instant"); + typeMapping.put("offset-date-time", "OffsetDateTime"); typeMapping.put("file", "File"); typeMapping.put("array", "List"); typeMapping.put("list", "List"); @@ -162,7 +177,7 @@ public ScalaHttp4sScala3ClientCodegen() { typeMapping.put("object", "Object"); typeMapping.put("binary", "Array[Byte]"); typeMapping.put("Date", "LocalDate"); - typeMapping.put("DateTime", "ZonedDateTime"); + typeMapping.put("DateTime", "Instant"); typeMapping.put("OffsetDateTime", "OffsetDateTime"); typeMapping.put("uuid", "UUID"); @@ -173,26 +188,6 @@ public ScalaHttp4sScala3ClientCodegen() { additionalProperties.put("licenseInfo", "Apache 2.0"); additionalProperties.put("licenseUrl", "http://apache.org/licenses/LICENSE-2.0.html"); - - languageSpecificPrimitives = new HashSet<>( - Arrays.asList( - "String", - "Boolean", - "Double", - "Int", - "Integer", - "Long", - "Float", - "Any", - "AnyVal", - "AnyRef", - "Object", - "BigDecimal" - ) - ); - instantiationTypes.put("array", "ArrayList"); - instantiationTypes.put("map", "HashMap"); - importMapping = new HashMap<>(); importMapping.put("UUID", "java.util.UUID"); importMapping.put("URI", "java.net.URI"); @@ -203,13 +198,13 @@ public ScalaHttp4sScala3ClientCodegen() { importMapping.put("HashMap", "scala.collection.immutable.HashMap"); importMapping.put("Seq", "scala.collection.immutable.Seq"); importMapping.put("ArrayBuffer", "scala.collection.mutable.ArrayBuffer"); - importMapping.put("DateTime", "java.time.LocalDateTime"); + importMapping.put("Instant", "java.time.Instant"); importMapping.put("LocalDateTime", "java.time.LocalDateTime"); importMapping.put("LocalDate", "java.time.LocalDate"); importMapping.put("LocalTime", "java.time.LocalTime"); importMapping.put("ZonedDateTime", "java.time.ZonedDateTime"); importMapping.put("OffsetDateTime", "java.time.OffsetDateTime"); - //refined + //refined TODO?? importMapping.put("Refined", "eu.timepit.refined.api.Refined"); importMapping.put("And", "eu.timepit.refined.boolean.And"); importMapping.put("MinSize", "eu.timepit.refined.collection.MinSize"); @@ -220,18 +215,39 @@ public ScalaHttp4sScala3ClientCodegen() { importMapping.put("Less", "eu.timepit.refined.numeric.Less"); importMapping.put("LessEqual", "eu.timepit.refined.numeric.LessEqual"); + instantiationTypes.put("array", "ArrayList"); + instantiationTypes.put("map", "HashMap"); + cliOptions.add(new CliOption(EXCLUDE_SBT, "exclude sbt from generation")); cliOptions.add(new CliOption(SOURCE_SUBFOLDER, "name of subfolder, for example to generate code in src/scala/generated")); + cliOptions.add(new CliOption(MAIN_PACKAGE, "Main package name").defaultValue("org.openapitools.client")); //inlineSchemaOption.put("SKIP_SCHEMA_REUSE", "true"); //inlineSchemaOption.put("REFACTOR_ALLOF_INLINE_SCHEMAS", "true"); - cliOptions.add(new CliOption("mainPackage", "Main package name").defaultValue("org.openapitools.client")); } @Override public void processOpts() { super.processOpts(); + + if (DateLibraries.java8.name().equals(dateLibrary)) { + typeMapping.put("date", "LocalDate"); + typeMapping.put("date-time", "Instant"); + typeMapping.put("offset-date-time", "OffsetDateTime"); + typeMapping.put("Date", "LocalDate"); + typeMapping.put("DateTime", "Instant"); + typeMapping.put("OffsetDateTime", "OffsetDateTime"); + this.importMapping.put("LocalDate", "java.time.LocalDate"); + this.importMapping.put("Instant", "java.time.Instant"); + this.importMapping.put("OffsetDateTime", "java.time.OffsetDateTime"); + additionalProperties.put("java8", "true"); + } else { + String error = "DateLibrary " + dateLibrary + " is not supported. Please java8"; + LOGGER.error(error); + throw new RuntimeException(error); + } + if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { packageName = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME); @@ -248,8 +264,6 @@ public void processOpts() { sourceSubFolder = (String) additionalProperties.get(SOURCE_SUBFOLDER); } additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); - additionalProperties.put("fnCapitalize", new CapitalizeLambda()); - additionalProperties.put("fnCamelize", new CamelizeLambda(true)); sourceFolder = "src" + File.separator + sourceSubFolder + File.separator + sourceFolder; @@ -400,27 +414,6 @@ public String toDefaultValue(Schema p) { } - 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) { diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index d041ff046b22..622f9e7fe50e 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -1,3 +1,4 @@ +{{>licenseInfo}} package {{apiPackage}} import cats.effect.Concurrent @@ -26,7 +27,6 @@ class {{classname}}ApiEndpointsImpl[F[*]: Concurrent]( import JsonEntityEncoderDecoder.* - override def {{operationId}}({{>methodParameters}}, requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] = { val headerParameters = Seq( diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/licenseInfo.mustache new file mode 100644 index 000000000000..16ba21d203d0 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/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-http4s-scala3/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache index 89825fde1b2c..508c757e23e5 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache @@ -8,7 +8,6 @@ import io.circe.refined.* import io.circe.syntax.* import io.circe.{ Decoder, Encoder } -import java.time.* {{#imports}} import {{import}} {{/imports}} From 33ba493882bfad5ee6c6ba926f2b6855100469b4 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Mon, 9 Sep 2024 15:44:52 -0400 Subject: [PATCH 03/18] fix client gen --- .../ScalaHttp4sScala3ClientCodegen.java | 36 +++++++++++++++--- .../scala-http4s-scala3/api.mustache | 38 +++++++++---------- .../scala-http4s-scala3/baseClient.mustache | 2 +- .../bodyParamType.mustache | 1 + .../failedRequest.mustache | 31 ++++++++++++++- ...Decoder.mustache => jsonSupports.mustache} | 4 +- .../methodParameters.mustache | 2 +- .../pathParamCreation.mustache | 1 - .../responseState.mustache | 7 ++++ 9 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/bodyParamType.mustache rename modules/openapi-generator/src/main/resources/scala-http4s-scala3/{jsonEntityEncoderDecoder.mustache => jsonSupports.mustache} (88%) delete mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/pathParamCreation.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index cea2d28663f6..d8bbe21ac579 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -17,9 +17,10 @@ package org.openapitools.codegen.languages; +import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.security.SecurityScheme; -import org.apache.commons.lang3.StringUtils; +import io.swagger.v3.oas.models.servers.Server; import org.openapitools.codegen.*; import org.openapitools.codegen.meta.features.*; import org.openapitools.codegen.model.ModelMap; @@ -31,6 +32,8 @@ import java.io.File; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class ScalaHttp4sScala3ClientCodegen extends AbstractScalaCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sScala3ClientCodegen.class); @@ -269,14 +272,11 @@ public void processOpts() { //supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); supportingFiles.add(new SupportingFile("baseClient.mustache", packageFileFolderRelative(), "BaseClient.scala")); - supportingFiles.add(new SupportingFile("jsonEntityEncoderDecoder.mustache", packageFileFolderRelative(), "JsonEntityEncoderDecoder.scala")); + supportingFiles.add(new SupportingFile("jsonSupports.mustache", packageFileFolderRelative(), "JsonSupports.scala")); //supportingFiles.add(new SupportingFile("model.mustache", modelFileFolderRelative(), "Models.scala")); supportingFiles.add(new SupportingFile("failedRequest.mustache", modelFileFolderRelative(), "FailedRequest.scala")); supportingFiles.add(new SupportingFile("auth_model.mustache", modelFileFolderRelative(), "Authorization.scala")); supportingFiles.add(new SupportingFile("models_package.mustache", modelFileFolderRelative(), "package.scala")); - supportingFiles.add(new SupportingFile("api.mustache", packageFileFolderRelative(), "Api.scala")); - - apiTemplateFiles.put("api.mustache", ".scala"); if (!additionalProperties.containsKey(EXCLUDE_SBT) && !Boolean.parseBoolean((String) additionalProperties.get(EXCLUDE_SBT))) { supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); @@ -286,7 +286,6 @@ public void processOpts() { additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); - } @Override @@ -376,6 +375,31 @@ public String toParamName(String name) { return formatIdentifier(name, false); } + @Override + public String encodePath(String input) { + String path = super.encodePath(input); + + // The parameter names in the URI must be converted to the same case as + // the method parameter. + StringBuilder buf = new StringBuilder(path.length()); + Matcher matcher = Pattern.compile("[{](.*?)[}]").matcher(path); + while (matcher.find()) { + matcher.appendReplacement(buf, "\\${" + toParamName(matcher.group(0)) + "}"); + } + matcher.appendTail(buf); + return buf.toString(); + } + + @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; + } + @Override public String toEnumName(CodegenProperty property) { return formatIdentifier(property.baseName, true); diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index 622f9e7fe50e..23f03beb0c85 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -8,54 +8,50 @@ import org.http4s.client.Client as Http4sClient import {{modelPackage}}.* {{#operations}} -trait {{classname}}ApiEndpoints[F[*]] { +trait {{classname}}Endpoints[F[*]] { {{#operation}} - def {{operationId}}({{>methodParameters}}, requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] + def {{operationId}}({{>methodParameters}}requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] {{/operation}} } {{/operations}} {{#operations}} -class {{classname}}ApiEndpointsImpl[F[*]: Concurrent]( +class {{classname}}EndpointsImpl[F[*]: Concurrent]( override val baseUrl: Uri, auth: Option[Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, httpClient: Http4sClient[F] -) extends BaseClient[F](baseUrl, auth, defaultHeaders, httpClient) with {{classname}}ApiEndpoints[F] { +) extends BaseClient[F](baseUrl, auth, defaultHeaders, httpClient) with {{classname}}Endpoints[F] { - import JsonEntityEncoderDecoder.* - - override def {{operationId}}({{>methodParameters}}, requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] = { + import JsonSupports.* + import io.circe.syntax.EncoderOps + import cats.implicits.* +{{#operation}} + override def {{operationId}}({{>methodParameters}}requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] = { val headerParameters = Seq( - {{#headerParams}} - {{>paramCreation}} - {{/headerParams}} + {{#headerParams}}{{>paramCreation}}{{/headerParams}} ) - val urlPath = s"{{{path}}}"{{#pathParams}}.replace({{>pathParamCreation}}){{/pathParams}} + val urlPath = s"{{{path}}}" - _executeRequest[Unit, {{>operationReturnType}}]( + _executeRequest[{{>bodyParamType}}, {{>operationReturnType}}]( method = "{{httpMethod.toUpperCase}}", path = urlPath, {{#bodyParam}} body = Some({{paramName}}), {{/bodyParam}} - queryParameters = {{#queryParams}}{{>paramCreation}}{{/queryParams}}, + {{#queryParams}} + queryParameters = {{>paramCreation}}, + {{/queryParams}} requestHeaders = requestHeaders ++ headerParameters) { -{{#responses}} -{{#is2xx}}case r if r.status.code == {{code}} => parseJson[F, {{>operationReturnType}}]("{{apiPackage}}.{{>operationReturnType}}", r){{/is2xx}} -{{#is3xx}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(ErrorResponse(r.status.code)){{/is3xx}} -{{#is4xx}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(ErrorResponse(r.status.code)){{/is4xx}} -{{#is5xx}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(ErrorResponse(r.status.code)){{/is5xx}} -{{#isDefault}}case r => Concurrent[F].raiseError(FailedRequest(r.status.code, s"Unsupported response code ${r.status.code}")){{/isDefault}} -{{/responses}} + {{>responseState}} } - } + {{/operation}} } {{/operations}} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache index 72de02a93ecd..acfb51cd3989 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -61,7 +61,7 @@ abstract class BaseClient[F[*]: Concurrent]( case a => sys.error("Invalid authorization scheme[" + a.getClass + "]") } - import JsonEntityEncoderDecoder.* + import JsonSupports.* val reqAndMaybeAuthAndBody = body.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity) httpClient.run(modifyRequest(reqAndMaybeAuthAndBody)).use(handler) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/bodyParamType.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/bodyParamType.mustache new file mode 100644 index 000000000000..372c5821ada5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/bodyParamType.mustache @@ -0,0 +1 @@ +{{#hasBodyParam}}{{#allParams}}{{#isBodyParam}}{{dataType}}{{/isBodyParam}}{{/allParams}}{{/hasBodyParam}}{{^hasBodyParam}}Unit{{/hasBodyParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache index 787977b928e5..4936f2a3538f 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache @@ -1,5 +1,32 @@ package {{modelPackage}} -case class FailedRequest(responseCode: Int, message: String, requestUri: Option[java.net.URI] = None) - extends Exception(s"HTTP URL: $requestUri, status: $responseCode; message: $message") +import io.circe.* +import io.circe.Decoder.* +import io.circe.Encoder.* +import io.circe.syntax.* + +case class FailedRequest(code: Int, message: String) extends Exception(s"Server return status code: $code; message: $message") + +object FailedRequest { + + implicit val encoderFailedRequest: Encoder[FailedRequest] = Encoder.instance { t => + Json.fromFields{ + Seq( + Some("code" -> t.code.asJson), + Some("message" -> t.message.asJson) + ).flatten + } + } + + implicit val decodeFailedRequest: Decoder[FailedRequest] = Decoder.instance { c => + for { + code <- c.downField("code").as[Int] + message <- c.downField("message").as[String] + } yield FailedRequest( + code = code, + message = message + ) + } + +} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonEntityEncoderDecoder.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache similarity index 88% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonEntityEncoderDecoder.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache index b804c4d3d361..e3ab131cbbd3 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonEntityEncoderDecoder.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache @@ -7,7 +7,7 @@ import org.http4s.{EntityDecoder, EntityEncoder, Response} import org.http4s.circe as http4sCirce import {{modelPackage}}.* -object JsonEntityEncoderDecoder { +object JsonSupports { implicit def circeJsonEncoder[F[*]: Concurrent, A](implicit encoder: Encoder[A]): EntityEncoder[F, A] = http4sCirce.jsonEncoderOf[F, A] @@ -19,7 +19,7 @@ object JsonEntityEncoderDecoder { r: Response[F] )(implicit decoder: Decoder[T]): F[T] = r.attemptAs[T].value.flatMap { case Right(value) => Concurrent[F].pure(value) - case Left(error) => Concurrent[F].raiseError(FailedRequest(r.status.code, s"Invalid json for class[" + className + "]", None)) + case Left(error) => Concurrent[F].raiseError(FailedRequest(r.status.code, s"Invalid json for class[" + className + "]")) } } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache index 9a1b04d7747f..1e9c34ba5299 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache @@ -1 +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 +{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}{{#isContainer}}{{dataType}}{{/isContainer}}{{^isContainer}}Option[{{dataType}}]{{/isContainer}}{{/required}}{{^defaultValue}}{{^required}}{{^isContainer}} = None{{/isContainer}}{{/required}}{{/defaultValue}}, {{/allParams}}{{#authMethods.0}})(implicit {{#authMethods}}{{#isApiKey}}apiKey: ApiKeyValue{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}basicAuth: BasicCredentials{{/isBasicBasic}}{{#isBasicBearer}}bearerToken: BearerToken{{/isBasicBearer}}{{/isBasic}}, {{/authMethods}}{{/authMethods.0}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/pathParamCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/pathParamCreation.mustache deleted file mode 100644 index e6bbc05789f8..000000000000 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/pathParamCreation.mustache +++ /dev/null @@ -1 +0,0 @@ -"\{{{baseName}}\}", {{{paramName}}}.toString diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache new file mode 100644 index 000000000000..5f74bb5dc986 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache @@ -0,0 +1,7 @@ +{{#responses}}{{#is2xx}}{{#dataType}} + case r if r.status.code == {{code}} => parseJson[F, {{>operationReturnType}}]("{{>operationReturnType}}", r){{/dataType}}{{^dataType}} + case r if r.status.code == {{code}} => Concurrent[F].pure(()){{/dataType}}{{/is2xx}}{{^is2xx}}{{#dataType}} + case r if r.status.code == {{code}} => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}} + case r if r.status.code == {{code}} => Concurrent[F].raiseError(FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/is2xx}} +{{/responses}} + case r => Concurrent[F].raiseError(FailedRequest(r.status.code, r.status.reason)) From 3dba0afc12b9e15e31dbca4b385aec90bfb69572 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Tue, 10 Sep 2024 10:04:21 -0400 Subject: [PATCH 04/18] Tweeks --- .../ScalaHttp4sScala3ClientCodegen.java | 4 ++-- .../scala-http4s-scala3/baseClient.mustache | 10 +++++----- .../scala-http4s-scala3/failedRequest.mustache | 16 ++++++++-------- .../scala-http4s-scala3/jsonSupports.mustache | 2 +- .../scala-http4s-scala3/responseState.mustache | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index d8bbe21ac579..16755d1b3ded 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -246,7 +246,7 @@ public void processOpts() { this.importMapping.put("OffsetDateTime", "java.time.OffsetDateTime"); additionalProperties.put("java8", "true"); } else { - String error = "DateLibrary " + dateLibrary + " is not supported. Please java8"; + String error = "DateLibrary " + dateLibrary + " is not supported. Please use java8"; LOGGER.error(error); throw new RuntimeException(error); } @@ -274,7 +274,7 @@ public void processOpts() { supportingFiles.add(new SupportingFile("baseClient.mustache", packageFileFolderRelative(), "BaseClient.scala")); supportingFiles.add(new SupportingFile("jsonSupports.mustache", packageFileFolderRelative(), "JsonSupports.scala")); //supportingFiles.add(new SupportingFile("model.mustache", modelFileFolderRelative(), "Models.scala")); - supportingFiles.add(new SupportingFile("failedRequest.mustache", modelFileFolderRelative(), "FailedRequest.scala")); + supportingFiles.add(new SupportingFile("failedRequest.mustache", modelFileFolderRelative(), "_FailedRequest.scala")); supportingFiles.add(new SupportingFile("auth_model.mustache", modelFileFolderRelative(), "Authorization.scala")); supportingFiles.add(new SupportingFile("models_package.mustache", modelFileFolderRelative(), "package.scala")); diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache index acfb51cd3989..d2e62945b77b 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -23,11 +23,11 @@ abstract class BaseClient[F[*]: Concurrent]( ("X-Apidoc-Version", ApiVersion) ) - def apiHeaders: Seq[(String, String)] = defaultApiHeaders + protected def apiHeaders: Seq[(String, String)] = defaultApiHeaders - def modifyRequest(request: Request[F]): Request[F] = request + protected def modifyRequest(request: Request[F]): Request[F] = request - def _executeRequest[T, U]( + protected def _executeRequest[T, U]( method: String, path: String, body: Option[T] = None, @@ -43,8 +43,8 @@ abstract class BaseClient[F[*]: Concurrent]( val headers = Headers( ( apiHeaders ++ - defaultHeaders ++ - requestHeaders + defaultHeaders ++ + requestHeaders ).groupBy(_._1).map { case (k, l) => Header.Raw(CIString(k), l.last._2) }.toList ) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache index 4936f2a3538f..0959cf41b280 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache @@ -5,24 +5,24 @@ import io.circe.Decoder.* import io.circe.Encoder.* import io.circe.syntax.* -case class FailedRequest(code: Int, message: String) extends Exception(s"Server return status code: $code; message: $message") +case class _FailedRequest(code: Int, message: String) extends Exception(s"Server return status code: $code; message: $message") -object FailedRequest { +object _FailedRequest { - implicit val encoderFailedRequest: Encoder[FailedRequest] = Encoder.instance { t => + implicit val encoderFailedRequest: Encoder[_FailedRequest] = Encoder.instance { t => Json.fromFields{ Seq( - Some("code" -> t.code.asJson), - Some("message" -> t.message.asJson) - ).flatten + "code" -> t.code.asJson, + "message" -> t.message.asJson + ) } } - implicit val decodeFailedRequest: Decoder[FailedRequest] = Decoder.instance { c => + implicit val decodeFailedRequest: Decoder[_FailedRequest] = Decoder.instance { c => for { code <- c.downField("code").as[Int] message <- c.downField("message").as[String] - } yield FailedRequest( + } yield _FailedRequest( code = code, message = message ) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache index e3ab131cbbd3..590489eec719 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache @@ -19,7 +19,7 @@ object JsonSupports { r: Response[F] )(implicit decoder: Decoder[T]): F[T] = r.attemptAs[T].value.flatMap { case Right(value) => Concurrent[F].pure(value) - case Left(error) => Concurrent[F].raiseError(FailedRequest(r.status.code, s"Invalid json for class[" + className + "]")) + case Left(error) => Concurrent[F].raiseError(_FailedRequest(r.status.code, s"Invalid json for class[" + className + "]")) } } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache index 5f74bb5dc986..12cd61561a14 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache @@ -1,7 +1,7 @@ {{#responses}}{{#is2xx}}{{#dataType}} case r if r.status.code == {{code}} => parseJson[F, {{>operationReturnType}}]("{{>operationReturnType}}", r){{/dataType}}{{^dataType}} case r if r.status.code == {{code}} => Concurrent[F].pure(()){{/dataType}}{{/is2xx}}{{^is2xx}}{{#dataType}} - case r if r.status.code == {{code}} => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}} - case r if r.status.code == {{code}} => Concurrent[F].raiseError(FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/is2xx}} + case r if r.status.code == {{code}} => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}} + case r if r.status.code == {{code}} => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/is2xx}} {{/responses}} - case r => Concurrent[F].raiseError(FailedRequest(r.status.code, r.status.reason)) + case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) From 44d08f8d8230efe6f60659694e7e065f9a7e46de Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Tue, 10 Sep 2024 12:52:52 -0400 Subject: [PATCH 05/18] header and form --- .../ScalaHttp4sScala3ClientCodegen.java | 17 +++++++---- .../scala-http4s-scala3/api.mustache | 28 ++++++++++--------- .../scala-http4s-scala3/baseClient.mustache | 11 +++++--- .../headerParamCreation.mustache | 3 ++ .../methodParameters.mustache | 2 +- .../paramCreation.mustache | 1 - .../scala-http4s-scala3/queryParams.mustache | 1 + 7 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache delete mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramCreation.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index 16755d1b3ded..7b1499b8609e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -74,7 +74,11 @@ public ScalaHttp4sScala3ClientCodegen() { modifyFeatureSet(features -> features .wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom)) - .securityFeatures(EnumSet.noneOf(SecurityFeature.class)) + .securityFeatures(EnumSet.of( + SecurityFeature.BasicAuth, + SecurityFeature.ApiKey, + SecurityFeature.BearerToken + )) .excludeGlobalFeatures( GlobalFeature.XMLStructureDefinitions, GlobalFeature.Callbacks, @@ -88,7 +92,7 @@ public ScalaHttp4sScala3ClientCodegen() { ParameterFeature.Cookie ) ); - + useOneOfInterfaces = true; supportsMultipleInheritance = true; supportsInheritance = true; @@ -174,7 +178,7 @@ public ScalaHttp4sScala3ClientCodegen() { typeMapping.put("date-time", "Instant"); typeMapping.put("offset-date-time", "OffsetDateTime"); typeMapping.put("file", "File"); - typeMapping.put("array", "List"); + typeMapping.put("array", "Seq"); typeMapping.put("list", "List"); typeMapping.put("map", "Map"); typeMapping.put("object", "Object"); @@ -218,8 +222,10 @@ public ScalaHttp4sScala3ClientCodegen() { importMapping.put("Less", "eu.timepit.refined.numeric.Less"); importMapping.put("LessEqual", "eu.timepit.refined.numeric.LessEqual"); - instantiationTypes.put("array", "ArrayList"); - instantiationTypes.put("map", "HashMap"); + instantiationTypes.put("array", "Seq"); + instantiationTypes.put("seq", "Seq"); + instantiationTypes.put("list", "List"); + instantiationTypes.put("map", "Map"); cliOptions.add(new CliOption(EXCLUDE_SBT, "exclude sbt from generation")); cliOptions.add(new CliOption(SOURCE_SUBFOLDER, "name of subfolder, for example to generate code in src/scala/generated")); @@ -451,6 +457,7 @@ public String escapeQuotationMark(String input) { return input.replace("\"", ""); } + //TODO public void setMainPackage(String mainPackage) { this.configKeyPath = this.mainPackage = mainPackage; } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index 23f03beb0c85..40c4d362ca17 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -5,13 +5,16 @@ import cats.effect.Concurrent import io.circe.Encoder import org.http4s.Uri import org.http4s.client.Client as Http4sClient +{{#imports}} +import {{import}} +{{/imports}} import {{modelPackage}}.* {{#operations}} trait {{classname}}Endpoints[F[*]] { {{#operation}} - def {{operationId}}({{>methodParameters}}requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] + def {{operationId}}({{>methodParameters}}): F[{{>operationReturnType}}] {{/operation}} } @@ -30,23 +33,22 @@ class {{classname}}EndpointsImpl[F[*]: Concurrent]( import cats.implicits.* {{#operation}} - override def {{operationId}}({{>methodParameters}}requestHeaders: Seq[(String, String)] = Nil): F[{{>operationReturnType}}] = { - val headerParameters = Seq( - {{#headerParams}}{{>paramCreation}}{{/headerParams}} - ) + override def {{operationId}}({{>methodParameters}}): F[{{>operationReturnType}}] = { + val requestHeaders = + {{>headerParamCreation}} val urlPath = s"{{{path}}}" + {{#hasQueryParams}} + val queryParameters = ( + {{>queryParams}} + ).toSeq.flatten{{/hasQueryParams}} _executeRequest[{{>bodyParamType}}, {{>operationReturnType}}]( method = "{{httpMethod.toUpperCase}}", - path = urlPath, - {{#bodyParam}} - body = Some({{paramName}}), - {{/bodyParam}} - {{#queryParams}} - queryParameters = {{>paramCreation}}, - {{/queryParams}} - requestHeaders = requestHeaders ++ headerParameters) { + path = urlPath,{{#bodyParam}} + body = Some({{paramName}}),{{/bodyParam}}{{#hasQueryParams}} + queryParameters = queryParameters, {{/hasQueryParams}} + requestHeaders = requestHeaders) { {{>responseState}} } } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache index d2e62945b77b..aa01a79df0b3 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -2,7 +2,7 @@ package {{apiPackage}} import cats.effect.Concurrent import io.circe.Encoder -import org.http4s.{Header, Headers, Method, Request, Response, Uri} +import org.http4s.{Header, Headers, Method, Request, Response, Uri, UrlForm} import org.http4s.client.Client as Http4sClient import org.http4s.QueryParamEncoder.* import org.typelevel.ci.CIString @@ -31,7 +31,8 @@ abstract class BaseClient[F[*]: Concurrent]( method: String, path: String, body: Option[T] = None, - queryParameters: Seq[(String, String)] = Nil, + formBody : Option[UrlForm] = None, + queryParameters: Seq[(String, Any)] = Nil, requestHeaders: Seq[(String, String)] = Nil )(handler: Response[F] => F[U])(implicit encoder: Encoder[T]): F[U] = { @@ -48,7 +49,7 @@ abstract class BaseClient[F[*]: Concurrent]( ).groupBy(_._1).map { case (k, l) => Header.Raw(CIString(k), l.last._2) }.toList ) - val queryMap = queryParameters.groupBy(_._1).map { case (k, v) => k -> v.map(_._2) } + val queryMap = queryParameters.groupBy(_._1).map { case (k, v) => k -> v.map(_._2.toString) } val uri = (baseUrl / path).setQueryParams(queryMap) val request = Request[F](method = m, uri = uri, headers = headers) @@ -62,7 +63,9 @@ abstract class BaseClient[F[*]: Concurrent]( } import JsonSupports.* - val reqAndMaybeAuthAndBody = body.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity) + val reqAndMaybeAuthAndBody = + if (formBody.nonEmpty) formBody.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity) + else body.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity) httpClient.run(modifyRequest(reqAndMaybeAuthAndBody)).use(handler) } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache new file mode 100644 index 000000000000..1240109083e9 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache @@ -0,0 +1,3 @@ +{{^hasHeaderParams}}Seq(){{/hasHeaderParams}}{{#hasHeaderParams}}Seq( + {{#headerParams}}{{#required}}Some("{{baseName}}" -> {{{paramName}}}){{/required}}{{^required}}{{{paramName}}}.map(x => "{{baseName}}" -> x){{/required}}{{^-last}}, {{/-last}}{{/headerParams}} + ).flatten{{/hasHeaderParams}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache index 1e9c34ba5299..0ef36c171cf0 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache @@ -1 +1 @@ -{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}{{#isContainer}}{{dataType}}{{/isContainer}}{{^isContainer}}Option[{{dataType}}]{{/isContainer}}{{/required}}{{^defaultValue}}{{^required}}{{^isContainer}} = None{{/isContainer}}{{/required}}{{/defaultValue}}, {{/allParams}}{{#authMethods.0}})(implicit {{#authMethods}}{{#isApiKey}}apiKey: ApiKeyValue{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}basicAuth: BasicCredentials{{/isBasicBasic}}{{#isBasicBearer}}bearerToken: BearerToken{{/isBasicBearer}}{{/isBasic}}, {{/authMethods}}{{/authMethods.0}} \ No newline at end of file +{{#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}}, {{/authMethods}}{{/authMethods.0}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramCreation.mustache deleted file mode 100644 index d44490e5397b..000000000000 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramCreation.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#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-http4s-scala3/queryParams.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache new file mode 100644 index 000000000000..da60caf9feb4 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache @@ -0,0 +1 @@ +{{#queryParams}}{{#isContainer}}Some({{paramName}}.map("{{baseName}}" -> _)){{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}}{{/queryParams}} \ No newline at end of file From b8142697d0453247b2a4224cfa9730fc80e6e3b6 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Wed, 11 Sep 2024 14:56:05 -0400 Subject: [PATCH 06/18] use scala 3 enum --- .../ScalaHttp4sScala3ClientCodegen.java | 15 +++-------- .../scala-http4s-scala3/api.mustache | 13 ++++------ .../scala-http4s-scala3/baseClient.mustache | 1 + .../scala-http4s-scala3/build.sbt.mustache | 7 ----- .../errorResponsePart.mustache | 1 + .../failedRequest.mustache | 4 +-- .../scala-http4s-scala3/jsonSupports.mustache | 1 + .../scala-http4s-scala3/model.mustache | 26 +++++++++---------- .../models_package.mustache | 12 ++++----- .../scala-http4s-scala3/queryParams.mustache | 9 ++++++- .../responseState.mustache | 12 ++++----- .../successResponsePart.mustache | 1 + 12 files changed, 46 insertions(+), 56 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/errorResponsePart.mustache create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/successResponsePart.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index 7b1499b8609e..82da82396db3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -92,7 +92,7 @@ public ScalaHttp4sScala3ClientCodegen() { ParameterFeature.Cookie ) ); - + useOneOfInterfaces = true; supportsMultipleInheritance = true; supportsInheritance = true; @@ -181,7 +181,7 @@ public ScalaHttp4sScala3ClientCodegen() { typeMapping.put("array", "Seq"); typeMapping.put("list", "List"); typeMapping.put("map", "Map"); - typeMapping.put("object", "Object"); + typeMapping.put("object", "Json"); typeMapping.put("binary", "Array[Byte]"); typeMapping.put("Date", "LocalDate"); typeMapping.put("DateTime", "Instant"); @@ -199,6 +199,7 @@ public ScalaHttp4sScala3ClientCodegen() { importMapping.put("UUID", "java.util.UUID"); importMapping.put("URI", "java.net.URI"); importMapping.put("File", "java.io.File"); + importMapping.put("Json", "io.circe.Json"); importMapping.put("Date", "java.util.Date"); importMapping.put("Timestamp", "java.sql.Timestamp"); importMapping.put("Map", "scala.collection.immutable.Map"); @@ -211,16 +212,6 @@ public ScalaHttp4sScala3ClientCodegen() { importMapping.put("LocalTime", "java.time.LocalTime"); importMapping.put("ZonedDateTime", "java.time.ZonedDateTime"); importMapping.put("OffsetDateTime", "java.time.OffsetDateTime"); - //refined TODO?? - importMapping.put("Refined", "eu.timepit.refined.api.Refined"); - importMapping.put("And", "eu.timepit.refined.boolean.And"); - importMapping.put("MinSize", "eu.timepit.refined.collection.MinSize"); - importMapping.put("MaxSize", "eu.timepit.refined.collection.MaxSize"); - importMapping.put("MatchesRegex", "eu.timepit.refined.string.MatchesRegex"); - importMapping.put("Greater", "eu.timepit.refined.numeric.Greater"); - importMapping.put("GreaterEqual", "eu.timepit.refined.numeric.GreaterEqual"); - importMapping.put("Less", "eu.timepit.refined.numeric.Less"); - importMapping.put("LessEqual", "eu.timepit.refined.numeric.LessEqual"); instantiationTypes.put("array", "Seq"); instantiationTypes.put("seq", "Seq"); diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index 40c4d362ca17..b6efb7ce5841 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -30,7 +30,7 @@ class {{classname}}EndpointsImpl[F[*]: Concurrent]( import JsonSupports.* import io.circe.syntax.EncoderOps - import cats.implicits.* + import cats.implicits.toFlatMapOps {{#operation}} override def {{operationId}}({{>methodParameters}}): F[{{>operationReturnType}}] = { @@ -38,16 +38,13 @@ class {{classname}}EndpointsImpl[F[*]: Concurrent]( {{>headerParamCreation}} val urlPath = s"{{{path}}}" - {{#hasQueryParams}} - val queryParameters = ( - {{>queryParams}} - ).toSeq.flatten{{/hasQueryParams}} + {{>queryParams}} _executeRequest[{{>bodyParamType}}, {{>operationReturnType}}]( method = "{{httpMethod.toUpperCase}}", - path = urlPath,{{#bodyParam}} - body = Some({{paramName}}),{{/bodyParam}}{{#hasQueryParams}} - queryParameters = queryParameters, {{/hasQueryParams}} + path = urlPath, + {{#bodyParam}}body = {{#required}}Some({{paramName}}){{/required}}{{^required}}{{paramName}}{{/required}},{{/bodyParam}} + queryParameters = queryParameters, requestHeaders = requestHeaders) { {{>responseState}} } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache index aa01a79df0b3..76faed80274d 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -1,3 +1,4 @@ +{{>licenseInfo}} package {{apiPackage}} import cats.effect.Concurrent diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache index bc38757577f1..a3f6ef1c8250 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache @@ -2,22 +2,15 @@ scalaVersion := "3.3.3" val CirceVersion = "0.14.9" val Http4sVersion = "0.23.26" -val RefinedVersion = "0.11.2" libraryDependencies ++= Seq( - "org.http4s" %% "http4s-ember-server" % Http4sVersion, "org.http4s" %% "http4s-ember-client" % Http4sVersion, "org.http4s" %% "http4s-circe" % Http4sVersion, "org.http4s" %% "http4s-dsl" % Http4sVersion, "org.http4s" %% "http4s-client" % Http4sVersion, - "org.typelevel" %% "jawn-fs2" % "2.4.0", "io.circe" %% "circe-core" % CirceVersion, "io.circe" %% "circe-generic" % CirceVersion, "io.circe" %% "circe-parser" % CirceVersion, - "io.circe" %% "circe-refined" % CirceVersion, - "eu.timepit" %% "refined" % RefinedVersion, - "com.beachape" %% "enumeratum-circe" % "1.7.4", - // test dependencies "org.scalatest" %% "scalatest" % "3.2.19" % "test" ) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/errorResponsePart.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/errorResponsePart.mustache new file mode 100644 index 000000000000..b879b7607f69 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/errorResponsePart.mustache @@ -0,0 +1 @@ +{{#dataType}}case r if r.status.code == {{code}} => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache index 0959cf41b280..d623555e8555 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache @@ -9,7 +9,7 @@ case class _FailedRequest(code: Int, message: String) extends Exception(s"Server object _FailedRequest { - implicit val encoderFailedRequest: Encoder[_FailedRequest] = Encoder.instance { t => + given encoderFailedRequest: Encoder[_FailedRequest] = Encoder.instance { t => Json.fromFields{ Seq( "code" -> t.code.asJson, @@ -18,7 +18,7 @@ object _FailedRequest { } } - implicit val decodeFailedRequest: Decoder[_FailedRequest] = Decoder.instance { c => + given decodeFailedRequest: Decoder[_FailedRequest] = Decoder.instance { c => for { code <- c.downField("code").as[Int] message <- c.downField("message").as[String] diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache index 590489eec719..a4283468b665 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache @@ -1,3 +1,4 @@ +{{>licenseInfo}} package {{apiPackage}} import cats.effect.* diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache index 508c757e23e5..68b324c4eaee 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache @@ -1,10 +1,6 @@ package {{modelPackage}} -import enumeratum.values.* import io.circe.* -import io.circe.Decoder.* -import io.circe.Encoder.* -import io.circe.refined.* import io.circe.syntax.* import io.circe.{ Decoder, Encoder } @@ -21,14 +17,19 @@ import {{import}} {{/vars}} */ {{#isEnum}} -sealed abstract class {{classname}}(val value: String) extends StringEnumEntry -case object {{classname}} extends StringEnum[{{classname}}] with StringCirceEnum[{{classname}}] { +enum {{classname}}(val value: String) { {{#allowableValues}} {{#values}} - case object {{#fnEnumEntry}}{{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}{{/fnEnumEntry}} extends {{name}}("{{.}}") + case {{#fnEnumEntry}}{{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}{{/fnEnumEntry}} extends {{classname}}("{{.}}") {{/values}} {{/allowableValues}} - val values: IndexedSeq[{{classname}}] = findValues +} + +object {{classname}} { + given decoder{{classname}}: Decoder[{{classname}}] = + Decoder.decodeString.map(str => {{classname}}.valueOf(str)) + given encoder{{classname}}: Encoder[{{classname}}] = + Encoder.encodeString.contramap[{{classname}}](_.value) } {{/isEnum}} {{^isEnum}} @@ -38,8 +39,8 @@ case class {{classname}}( {{/vars}} ) -object {{name}} { - implicit val encoder{{name}}: Encoder[{{name}}] = Encoder.instance { t => +object {{classname}} { + given encoder{{classname}}: Encoder[{{classname}}] = Encoder.instance { t => Json.fromFields{ Seq( {{#vars}} @@ -48,19 +49,18 @@ object {{name}} { ).flatten } } - implicit val decode{{name}}: Decoder[{{name}}] = Decoder.instance { c => + given decode{{classname}}: Decoder[{{classname}}] = Decoder.instance { c => for { {{#vars}} {{name}} <- c.downField("{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}").as[{{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}] {{/vars}} - } yield {{name}}( + } yield {{classname}}( {{#vars}} {{name}} = {{name}}{{^-last}},{{/-last}} {{/vars}} ) } } - {{/isEnum}} {{/model}} {{/models}} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache index 1fe8a92ee5f5..90dfde1cd51b 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache @@ -4,22 +4,22 @@ import io.circe.{Decoder, Encoder} package object models { - implicit val decodeUUID: Decoder[_root_.java.util.UUID] = + given decodeUUID: Decoder[_root_.java.util.UUID] = Decoder.decodeString.map(str => _root_.java.util.UUID.fromString(str)) - implicit val encodeUUID: Encoder[_root_.java.util.UUID] = + given encodeUUID: Encoder[_root_.java.util.UUID] = Encoder.encodeString.contramap[_root_.java.util.UUID](uuid => uuid.toString) - implicit val decodeInstant: Decoder[_root_.java.time.Instant] = + given decodeInstant: Decoder[_root_.java.time.Instant] = Decoder.decodeString.map(str => _root_.java.time.OffsetDateTime.parse(str).toInstant) - implicit val encodeInstant: Encoder[_root_.java.time.Instant] = + given encodeInstant: Encoder[_root_.java.time.Instant] = Encoder.encodeString.contramap[_root_.java.time.Instant](_.toString) - implicit val decodeLocalDate: Decoder[_root_.java.time.LocalDate] = + given decodeLocalDate: Decoder[_root_.java.time.LocalDate] = Decoder.decodeString.map(str => _root_.java.time.LocalDate.parse(str)) - implicit val encodeLocalDate: Encoder[_root_.java.time.LocalDate] = + given encodeLocalDate: Encoder[_root_.java.time.LocalDate] = Encoder.encodeString.contramap[_root_.java.time.LocalDate](_.toString) } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache index da60caf9feb4..a9632a065233 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache @@ -1 +1,8 @@ -{{#queryParams}}{{#isContainer}}Some({{paramName}}.map("{{baseName}}" -> _)){{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}}{{/queryParams}} \ No newline at end of file +{{#hasQueryParams}} +val queryParameters = ( + {{#queryParams}}{{#isContainer}}Some({{paramName}}.map("{{baseName}}" -> _)){{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}}{{/queryParams}} + ).toSeq.flatten +{{/hasQueryParams}} +{{^hasQueryParams}} +val queryParameters = Nil +{{/hasQueryParams}} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache index 12cd61561a14..a61b7853939f 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache @@ -1,7 +1,5 @@ -{{#responses}}{{#is2xx}}{{#dataType}} - case r if r.status.code == {{code}} => parseJson[F, {{>operationReturnType}}]("{{>operationReturnType}}", r){{/dataType}}{{^dataType}} - case r if r.status.code == {{code}} => Concurrent[F].pure(()){{/dataType}}{{/is2xx}}{{^is2xx}}{{#dataType}} - case r if r.status.code == {{code}} => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}} - case r if r.status.code == {{code}} => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/is2xx}} -{{/responses}} - case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) +{{#responses}} + {{#is2xx}}{{>successResponsePart}}{{/is2xx}}{{^is2xx}} + {{#is3xx}}{{>successResponsePart}}{{/is3xx}} + {{^is3xx}}{{^isDefault}}{{>errorResponsePart}}{{/isDefault}}{{#isDefault}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}} + {{^hasDefaultResponse}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/hasDefaultResponse}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/successResponsePart.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/successResponsePart.mustache new file mode 100644 index 000000000000..75ceace3e8ed --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/successResponsePart.mustache @@ -0,0 +1 @@ +{{#dataType}}case r if r.status.code == {{code}} => parseJson[F, {{>operationReturnType}}]("{{>operationReturnType}}", r){{/dataType}}{{^dataType}}case r if r.status.code == {{code}} => Concurrent[F].pure(()){{/dataType}} \ No newline at end of file From 9f94a388fca2cd32b5ada59661f6a5edf50a2c6a Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Wed, 11 Sep 2024 22:19:39 -0400 Subject: [PATCH 07/18] more tweeks --- .../src/main/resources/scala-http4s-scala3/api.mustache | 4 ++-- .../src/main/resources/scala-http4s-scala3/model.mustache | 2 +- .../resources/scala-http4s-scala3/queryParams.mustache | 7 +++---- .../resources/scala-http4s-scala3/responseState.mustache | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index b6efb7ce5841..873f809e2896 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -36,14 +36,14 @@ class {{classname}}EndpointsImpl[F[*]: Concurrent]( override def {{operationId}}({{>methodParameters}}): F[{{>operationReturnType}}] = { val requestHeaders = {{>headerParamCreation}} - val urlPath = s"{{{path}}}" {{>queryParams}} + val body = {{#bodyParam}}{{#required}}Some({{paramName}}){{/required}}{{^required}}{{paramName}}{{/required}}{{/bodyParam}}{{^bodyParam}}None{{/bodyParam}} _executeRequest[{{>bodyParamType}}, {{>operationReturnType}}]( method = "{{httpMethod.toUpperCase}}", path = urlPath, - {{#bodyParam}}body = {{#required}}Some({{paramName}}){{/required}}{{^required}}{{paramName}}{{/required}},{{/bodyParam}} + body = body, queryParameters = queryParameters, requestHeaders = requestHeaders) { {{>responseState}} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache index 68b324c4eaee..8b1f955fd3d5 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache @@ -49,7 +49,7 @@ object {{classname}} { ).flatten } } - given decode{{classname}}: Decoder[{{classname}}] = Decoder.instance { c => + given decoder{{classname}}: Decoder[{{classname}}] = Decoder.instance { c => for { {{#vars}} {{name}} <- c.downField("{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}").as[{{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}] diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache index a9632a065233..c108f09c7590 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache @@ -1,8 +1,7 @@ {{#hasQueryParams}} val queryParameters = ( {{#queryParams}}{{#isContainer}}Some({{paramName}}.map("{{baseName}}" -> _)){{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}}{{/queryParams}} - ).toSeq.flatten -{{/hasQueryParams}} + ).toSeq.flatten{{/hasQueryParams}} {{^hasQueryParams}} -val queryParameters = Nil -{{/hasQueryParams}} + val queryParameters = Nil +{{/hasQueryParams}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache index a61b7853939f..a3d113eb3187 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache @@ -1,5 +1,5 @@ {{#responses}} {{#is2xx}}{{>successResponsePart}}{{/is2xx}}{{^is2xx}} {{#is3xx}}{{>successResponsePart}}{{/is3xx}} - {{^is3xx}}{{^isDefault}}{{>errorResponsePart}}{{/isDefault}}{{#isDefault}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}} - {{^hasDefaultResponse}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/hasDefaultResponse}} \ No newline at end of file + {{^is3xx}}{{^isDefault}}{{>errorResponsePart}}{{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}} + case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) \ No newline at end of file From 487725416b44a45edae5673743fd505d1fd04d3d Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Thu, 12 Sep 2024 13:39:04 -0400 Subject: [PATCH 08/18] add additional properties; add auth --- .../codegen/CodegenOperation.java | 13 ++- .../ScalaHttp4sScala3ClientCodegen.java | 110 +++++++----------- .../scala-http4s-scala3/api.mustache | 18 ++- .../scala-http4s-scala3/authModel.mustache | 10 ++ .../scala-http4s-scala3/auth_model.mustache | 7 -- .../scala-http4s-scala3/baseClient.mustache | 12 +- .../scala-http4s-scala3/build.sbt.mustache | 4 +- .../failedRequest.mustache | 1 + .../headerParamCreation.mustache | 7 +- .../methodParameters.mustache | 2 +- .../scala-http4s-scala3/model.mustache | 3 +- ...ackage.mustache => modelsPackage.mustache} | 9 +- .../scala-http4s-scala3/queryParams.mustache | 10 +- .../responseState.mustache | 9 +- 14 files changed, 103 insertions(+), 112 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache delete mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache rename modules/openapi-generator/src/main/resources/scala-http4s-scala3/{models_package.mustache => modelsPackage.mustache} (77%) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java index db659a22d541..127853a88fe7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java @@ -30,7 +30,7 @@ public class CodegenOperation { hasVersionHeaders = false, hasVersionQueryParams = false, isResponseBinary = false, isResponseFile = false, isResponseOptional = false, hasReference = false, defaultReturnType = false, isRestfulIndex, isRestfulShow, isRestfulCreate, isRestfulUpdate, isRestfulDestroy, - isRestful, isDeprecated, isCallbackRequest, uniqueItems, hasDefaultResponse = false, hasConstantParams = false, + isRestful, isDeprecated, isCallbackRequest, uniqueItems, hasDefaultResponse = false, hasOnlyDefaultResponse = false, hasConstantParams = false, hasErrorResponseObject, // if 4xx, 5xx responses have at least one error object defined hasSingleParam = false; // if the operation has only one parameter; public CodegenProperty returnProperty; @@ -215,6 +215,13 @@ public boolean getHasDefaultResponse() { return responses.stream().anyMatch(response -> response.isDefault); } + /** + * Check if the responses contain only 1 entry and it's default + * + * @return true if responses contain only 1 entry and it's a default response, false otherwise + */ + public boolean getHasOnlyDefaultResponse() { return responses.size() == 1 && getHasDefaultResponse(); } + public boolean getAllResponsesAreErrors() { return responses.stream().allMatch(response -> response.is4xx || response.is5xx); } @@ -351,6 +358,7 @@ public String toString() { sb.append(", isResponseOptional=").append(isResponseOptional); sb.append(", hasReference=").append(hasReference); sb.append(", hasDefaultResponse=").append(hasDefaultResponse); + sb.append(", hasOnlyDefaultResponse=").append(hasOnlyDefaultResponse); sb.append(", hasErrorResponseObject=").append(hasErrorResponseObject); sb.append(", hasSingleParam=").append(hasSingleParam); sb.append(", isRestfulIndex=").append(isRestfulIndex); @@ -432,6 +440,7 @@ public boolean equals(Object o) { isResponseOptional == that.isResponseOptional && hasReference == that.hasReference && hasDefaultResponse == that.hasDefaultResponse && + hasOnlyDefaultResponse == that.hasOnlyDefaultResponse && hasErrorResponseObject == that.hasErrorResponseObject && hasSingleParam == that.hasSingleParam && isRestfulIndex == that.isRestfulIndex && @@ -496,7 +505,7 @@ public int hashCode() { return Objects.hash(responseHeaders, hasAuthMethods, hasConsumes, hasProduces, hasParams, hasOptionalParams, hasRequiredParams, returnTypeIsPrimitive, returnSimpleType, subresourceOperation, isMap, isArray, isMultipart, isVoid, isResponseBinary, isResponseFile, isResponseOptional, hasReference, - hasDefaultResponse, isRestfulIndex, isRestfulShow, isRestfulCreate, isRestfulUpdate, isRestfulDestroy, + hasDefaultResponse, hasOnlyDefaultResponse, isRestfulIndex, isRestfulShow, isRestfulCreate, isRestfulUpdate, isRestfulDestroy, isRestful, isDeprecated, isCallbackRequest, uniqueItems, path, operationId, returnType, httpMethod, returnBaseType, returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse, discriminator, consumes, produces, prioritizedContentTypes, servers, bodyParam, allParams, bodyParams, diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index 82da82396db3..03aa3efcfd4f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -38,23 +38,16 @@ public class ScalaHttp4sScala3ClientCodegen extends AbstractScalaCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sScala3ClientCodegen.class); - protected String mainPackage = "org.openapitools.client"; //TODO: configuable?? - protected String groupId = "org.openapitools"; //TODO: configuable?? - protected String artifactId = "http4s-client-scala3"; //TODO: configuable?? - protected String sourceFolder = "scala"; - protected String sourceSubFolder = "main"; + protected String mainPackage = "org.openapitools.client"; + protected String groupId = "org.openapitools"; + protected String artifactId = "http4s-client-scala3"; protected String artifactVersion = "1.0.0"; - protected String apiDocPath = "docs/"; - protected String modelDocPath = "docs/"; - protected String configKey = "apiRequest"; - protected String configKeyPath = mainPackage; - protected boolean registerNonStandardStatusCodes = true; - protected boolean removeOAuthSecurities = true; - public static final String EXCLUDE_SBT = "excludeSbt"; // generate as whole project - public static final String SOURCE_SUBFOLDER = "sourceSubfolder"; // generate as whole project - public static final String MAIN_PACKAGE = "mainPackage"; // generate as whole project - - private String packageName = mainPackage; + protected boolean registerNonStandardStatusCodes = true; //TODO?? + protected boolean removeOAuthSecurities = true; //TODO?? + protected boolean excludeSbt = false; + public static final String EXCLUDE_SBT = "excludeSbt"; + protected String sourceFolder = "src" + File.separator + "main" + File.separator + "scala"; + protected String projectName = artifactId; @Override public CodegenType getTag() { @@ -86,7 +79,8 @@ public ScalaHttp4sScala3ClientCodegen() { GlobalFeature.ParameterStyling ) .excludeSchemaSupportFeatures( - SchemaSupportFeature.Polymorphism + SchemaSupportFeature.Polymorphism, + SchemaSupportFeature.not ) .excludeParameterFeatures( ParameterFeature.Cookie @@ -99,13 +93,13 @@ public ScalaHttp4sScala3ClientCodegen() { supportsMixins = true; addOneOfInterfaceImports = true; - outputFolder = "generated-code" + File.separator + "scala-http4s-scala3"; embeddedTemplateDir = templateDir = "scala-http4s-scala3"; modelTemplateFiles.put("model.mustache", ".scala"); apiTemplateFiles.put("api.mustache", ".scala"); - apiPackage = mainPackage; - modelPackage = mainPackage + ".models"; + + setApiPackage(mainPackage+ ".apis"); + setModelPackage(mainPackage + ".models"); setReservedWordsLowerCase( Arrays.asList( @@ -188,8 +182,9 @@ public ScalaHttp4sScala3ClientCodegen() { typeMapping.put("OffsetDateTime", "OffsetDateTime"); typeMapping.put("uuid", "UUID"); - additionalProperties.put("modelPackage", modelPackage()); - additionalProperties.put("apiPackage", apiPackage()); + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); + additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); + additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName); additionalProperties.put("infoUrl", "http://org.openapitools"); additionalProperties.put("infoEmail", "team@openapitools.org"); additionalProperties.put("licenseInfo", "Apache 2.0"); @@ -218,12 +213,8 @@ public ScalaHttp4sScala3ClientCodegen() { instantiationTypes.put("list", "List"); instantiationTypes.put("map", "Map"); - cliOptions.add(new CliOption(EXCLUDE_SBT, "exclude sbt from generation")); - cliOptions.add(new CliOption(SOURCE_SUBFOLDER, "name of subfolder, for example to generate code in src/scala/generated")); - cliOptions.add(new CliOption(MAIN_PACKAGE, "Main package name").defaultValue("org.openapitools.client")); - - //inlineSchemaOption.put("SKIP_SCHEMA_REUSE", "true"); - //inlineSchemaOption.put("REFACTOR_ALLOF_INLINE_SCHEMAS", "true"); + //this option allows inline enums to be separate own models + inlineSchemaOption.put("RESOLVE_INLINE_ENUMS", "true"); } @@ -249,42 +240,40 @@ public void processOpts() { } if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { - packageName = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME); - - setApiPackage(packageName + ".apis"); + mainPackage = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME); + setApiPackage(mainPackage + ".apis"); + setModelPackage(mainPackage + ".models"); additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); - - setModelPackage(packageName + ".models"); - additionalProperties.put(CodegenConstants.PACKAGE_NAME, modelPackage()); + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); } else { - additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); + additionalProperties.put(CodegenConstants.PACKAGE_NAME, mainPackage); } - - if (additionalProperties.containsKey(SOURCE_SUBFOLDER)) { - sourceSubFolder = (String) additionalProperties.get(SOURCE_SUBFOLDER); + if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { + //you can set your own source folder, i.e. target/scala-3.3.3/src_managed/main + this.sourceFolder = (String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER); + } + if (additionalProperties.containsKey(CodegenConstants.PROJECT_NAME)) { + this.projectName = (String) additionalProperties.get(CodegenConstants.PROJECT_NAME); } - additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); - sourceFolder = "src" + File.separator + sourceSubFolder + File.separator + sourceFolder; + additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); - //supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - supportingFiles.add(new SupportingFile("baseClient.mustache", packageFileFolderRelative(), "BaseClient.scala")); - supportingFiles.add(new SupportingFile("jsonSupports.mustache", packageFileFolderRelative(), "JsonSupports.scala")); - //supportingFiles.add(new SupportingFile("model.mustache", modelFileFolderRelative(), "Models.scala")); + supportingFiles.add(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); + supportingFiles.add(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); supportingFiles.add(new SupportingFile("failedRequest.mustache", modelFileFolderRelative(), "_FailedRequest.scala")); - supportingFiles.add(new SupportingFile("auth_model.mustache", modelFileFolderRelative(), "Authorization.scala")); - supportingFiles.add(new SupportingFile("models_package.mustache", modelFileFolderRelative(), "package.scala")); + supportingFiles.add(new SupportingFile("authModel.mustache", modelFileFolderRelative(), "Authorization.scala")); + supportingFiles.add(new SupportingFile("modelsPackage.mustache", modelFileFolderRelative(), "package.scala")); - if (!additionalProperties.containsKey(EXCLUDE_SBT) && !Boolean.parseBoolean((String) additionalProperties.get(EXCLUDE_SBT))) { + if (additionalProperties.containsKey(EXCLUDE_SBT)) { + this.excludeSbt = convertPropertyToBoolean(EXCLUDE_SBT); + } + if (!excludeSbt) { supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); } - additionalProperties.put(CodegenConstants.GROUP_ID, groupId); - additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); - additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); - } + @Override public String apiFileFolder() { return outputFolder + File.separator + apiFileFolderRelative(); @@ -303,11 +292,10 @@ private String modelFileFolderRelative() { return sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar); } - private String packageFileFolderRelative() { - return sourceFolder + File.separator + packageName.replace('.', File.separatorChar); + private String apisFileFolderRelative() { + return sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar); } - @Override public String escapeReservedWord(String name) { if (this.reservedWordsMappings().containsKey(name)) { @@ -448,18 +436,4 @@ public String escapeQuotationMark(String input) { return input.replace("\"", ""); } - //TODO - 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/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index 873f809e2896..74f95ef52362 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -23,10 +23,10 @@ trait {{classname}}Endpoints[F[*]] { {{#operations}} class {{classname}}EndpointsImpl[F[*]: Concurrent]( override val baseUrl: Uri, - auth: Option[Authorization] = None, + defaultAuth: Option[Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, httpClient: Http4sClient[F] -) extends BaseClient[F](baseUrl, auth, defaultHeaders, httpClient) with {{classname}}Endpoints[F] { +) extends BaseClient[F](baseUrl, defaultAuth, defaultHeaders, httpClient) with {{classname}}Endpoints[F] { import JsonSupports.* import io.circe.syntax.EncoderOps @@ -34,19 +34,17 @@ class {{classname}}EndpointsImpl[F[*]: Concurrent]( {{#operation}} override def {{operationId}}({{>methodParameters}}): F[{{>operationReturnType}}] = { - val requestHeaders = - {{>headerParamCreation}} - val urlPath = s"{{{path}}}" + val requestHeaders = {{>headerParamCreation}} {{>queryParams}} - val body = {{#bodyParam}}{{#required}}Some({{paramName}}){{/required}}{{^required}}{{paramName}}{{/required}}{{/bodyParam}}{{^bodyParam}}None{{/bodyParam}} _executeRequest[{{>bodyParamType}}, {{>operationReturnType}}]( method = "{{httpMethod.toUpperCase}}", - path = urlPath, - body = body, + path = s"{{{path}}}", + body = {{#bodyParam}}{{#required}}Some({{paramName}}){{/required}}{{^required}}{{paramName}}{{/required}}{{/bodyParam}}{{^bodyParam}}None{{/bodyParam}}, queryParameters = queryParameters, - requestHeaders = requestHeaders) { - {{>responseState}} + requestHeaders = requestHeaders, + auth = {{#authMethods}}Some(auth){{/authMethods}}{{^authMethods}}None{{/authMethods}}) { + {{>responseState}} } } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache new file mode 100644 index 000000000000..50f982b426ed --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache @@ -0,0 +1,10 @@ +{{>licenseInfo}} +package {{modelPackage}} + +sealed trait Authorization extends _root_.scala.Product with _root_.scala.Serializable + +object Authorization { + final case class Basic(username: String, password: Option[String] = None) extends Authorization + final case class ApiKey(value: String) extends Authorization + final case class Bearer(token: String) extends Authorization +} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache deleted file mode 100644 index ef0a98b4a864..000000000000 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/auth_model.mustache +++ /dev/null @@ -1,7 +0,0 @@ -package {{modelPackage}} - -trait Authorization extends _root_.scala.Product with _root_.scala.Serializable - -object Authorization { - final case class Basic(username: String, password: Option[String] = None) extends Authorization -} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache index 76faed80274d..64fc617d83c5 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -13,7 +13,7 @@ import {{modelPackage}}.* abstract class BaseClient[F[*]: Concurrent]( val baseUrl: Uri, - auth: Option[Authorization] = None, + defaultAuth: Option[Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, httpClient: Http4sClient[F] ) { @@ -34,7 +34,8 @@ abstract class BaseClient[F[*]: Concurrent]( body: Option[T] = None, formBody : Option[UrlForm] = None, queryParameters: Seq[(String, Any)] = Nil, - requestHeaders: Seq[(String, String)] = Nil + requestHeaders: Seq[(String, String)] = Nil, + auth: Option[Authorization] = None )(handler: Response[F] => F[U])(implicit encoder: Encoder[T]): F[U] = { val m = Method.fromString(method) match { @@ -55,12 +56,15 @@ abstract class BaseClient[F[*]: Concurrent]( val request = Request[F](method = m, uri = uri, headers = headers) - val reqAndMaybeAuth = auth.fold(request) { + val reqAndMaybeAuth = auth.orElse(defaultAuth).fold(request) { case Authorization.Basic(username, passwordOpt) => val userpass = s"$username:${passwordOpt.getOrElse("")}" val token = Base64.getEncoder.encodeToString(userpass.getBytes(StandardCharsets.ISO_8859_1)) request.putHeaders(Header.Raw(CIString("Authorization"), s"Basic $token")) - case a => sys.error("Invalid authorization scheme[" + a.getClass + "]") + case Authorization.Bearer(token) => + request.putHeaders(Header.Raw(CIString("Authorization"), s"Bearer $token")) + case Authorization.ApiKey(apiKey) => + request.putHeaders(Header.Raw(CIString("api_key"), apiKey)) } import JsonSupports.* diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache index a3f6ef1c8250..f5ae72dd6898 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache @@ -30,8 +30,6 @@ scalacOptions := Seq( lazy val root = project .in(file(".")) .settings( - version := "{{artifactVersion}}", - name := "{{artifactId}}", - organization := "{{groupId}}", + name := "{{projectName}}", publishArtifact := false ) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache index d623555e8555..a2845b037741 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache @@ -1,3 +1,4 @@ +{{>licenseInfo}} package {{modelPackage}} import io.circe.* diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache index 1240109083e9..fd92ce93e069 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache @@ -1,3 +1,4 @@ -{{^hasHeaderParams}}Seq(){{/hasHeaderParams}}{{#hasHeaderParams}}Seq( - {{#headerParams}}{{#required}}Some("{{baseName}}" -> {{{paramName}}}){{/required}}{{^required}}{{{paramName}}}.map(x => "{{baseName}}" -> x){{/required}}{{^-last}}, {{/-last}}{{/headerParams}} - ).flatten{{/hasHeaderParams}} \ No newline at end of file +Seq( + Some("Content-Type" -> {{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{^consumes}}"application/json"{{/consumes}}){{#hasHeaderParams}}, + {{#headerParams}}{{#required}}Some("{{baseName}}" -> {{{paramName}}}){{/required}}{{^required}}{{{paramName}}}.map(x => "{{baseName}}" -> x){{/required}}{{^-last}}, {{/-last}}{{/headerParams}}{{/hasHeaderParams}} + ).flatten \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache index 0ef36c171cf0..f1e2085e5c27 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache @@ -1 +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}}, {{/authMethods}}{{/authMethods.0}} \ No newline at end of file +{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}Option[{{dataType}}]{{/required}}{{^defaultValue}}{{^required}} = None{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#authMethods.0}})(implicit auth: {{#isApiKey}}Authorization.ApiKey{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}Authorization.Basic{{/isBasicBasic}}{{#isBasicBearer}}Authorization.Bearer{{/isBasicBearer}}{{/isBasic}}{{/authMethods.0}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache index 8b1f955fd3d5..810e09336e14 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache @@ -1,3 +1,4 @@ +{{>licenseInfo}} package {{modelPackage}} import io.circe.* @@ -29,7 +30,7 @@ object {{classname}} { given decoder{{classname}}: Decoder[{{classname}}] = Decoder.decodeString.map(str => {{classname}}.valueOf(str)) given encoder{{classname}}: Encoder[{{classname}}] = - Encoder.encodeString.contramap[{{classname}}](_.value) + Encoder.encodeString.contramap[{{classname}}](_.value) } {{/isEnum}} {{^isEnum}} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/modelsPackage.mustache similarity index 77% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s-scala3/modelsPackage.mustache index 90dfde1cd51b..5211e1dd5cc7 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/models_package.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/modelsPackage.mustache @@ -1,4 +1,5 @@ -package {{apiPackage}} +{{>licenseInfo}} +package {{packageName}} import io.circe.{Decoder, Encoder} @@ -22,4 +23,10 @@ package object models { given encodeLocalDate: Encoder[_root_.java.time.LocalDate] = Encoder.encodeString.contramap[_root_.java.time.LocalDate](_.toString) + given decodeJson: Decoder[io.circe.Json] = + Decoder.decodeString.map(str => io.circe.Json.fromString(str)) + + given encodeJson: Encoder[io.circe.Json] = + Encoder.encodeString.contramap[io.circe.Json](_.toString) + } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache index c108f09c7590..c6604e546a69 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache @@ -1,7 +1,3 @@ -{{#hasQueryParams}} -val queryParameters = ( - {{#queryParams}}{{#isContainer}}Some({{paramName}}.map("{{baseName}}" -> _)){{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}}{{/queryParams}} - ).toSeq.flatten{{/hasQueryParams}} -{{^hasQueryParams}} - val queryParameters = Nil -{{/hasQueryParams}} \ No newline at end of file +{{#hasQueryParams}}val queryParameters = ({{#queryParams}} + {{#isContainer}}{{#required}}Some({{paramName}}.map("{{baseName}}" -> _)){{/required}}{{^required}}{{paramName}}.map(x => x.map("{{baseName}}" -> _)){{/required}}{{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}}{{/queryParams}} + ).toSeq.flatten{{/hasQueryParams}}{{^hasQueryParams}}val queryParameters = Nil{{/hasQueryParams}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache index a3d113eb3187..c959ec03ab1b 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache @@ -1,5 +1,4 @@ -{{#responses}} - {{#is2xx}}{{>successResponsePart}}{{/is2xx}}{{^is2xx}} - {{#is3xx}}{{>successResponsePart}}{{/is3xx}} - {{^is3xx}}{{^isDefault}}{{>errorResponsePart}}{{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}} - case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) \ No newline at end of file +{{#hasOnlyDefaultResponse}}{{#responses.0}}{{#dataType}}r => parseJson[F, {{>operationReturnType}}]("{{>operationReturnType}}", r){{/dataType}}{{^dataType}}r => Concurrent[F].pure(()){{/dataType}}{{/responses.0}}{{/hasOnlyDefaultResponse}} + {{^hasOnlyDefaultResponse}}{{#responses}}{{#is2xx}}{{>successResponsePart}}{{/is2xx}}{{^is2xx}} + {{#is3xx}}{{>successResponsePart}}{{/is3xx}} + {{^is3xx}}{{^isDefault}}{{>errorResponsePart}}{{/isDefault}}{{#isDefault}}{{#dataType}}case r => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}}{{/hasOnlyDefaultResponse}} \ No newline at end of file From a598f68f70f9324d543410dba11847f21e439dc2 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Mon, 16 Sep 2024 13:41:39 -0400 Subject: [PATCH 09/18] add form media type --- .../ScalaHttp4sScala3ClientCodegen.java | 7 ++++--- .../scala-http4s-scala3/api.mustache | 14 ++++++++++---- .../scala-http4s-scala3/authModel.mustache | 10 +++++----- .../scala-http4s-scala3/baseClient.mustache | 19 ++++++++++++------- .../methodParameters.mustache | 2 +- .../paramsCreation.mustache | 1 + .../scala-http4s-scala3/queryParams.mustache | 3 --- .../responseState.mustache | 9 +++++---- 8 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramsCreation.mustache delete mode 100644 modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index 03aa3efcfd4f..086690432260 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -83,7 +83,8 @@ public ScalaHttp4sScala3ClientCodegen() { SchemaSupportFeature.not ) .excludeParameterFeatures( - ParameterFeature.Cookie + ParameterFeature.Cookie, + ParameterFeature.FormMultipart ) ); @@ -176,7 +177,7 @@ public ScalaHttp4sScala3ClientCodegen() { typeMapping.put("list", "List"); typeMapping.put("map", "Map"); typeMapping.put("object", "Json"); - typeMapping.put("binary", "Array[Byte]"); + typeMapping.put("binary", "File"); typeMapping.put("Date", "LocalDate"); typeMapping.put("DateTime", "Instant"); typeMapping.put("OffsetDateTime", "OffsetDateTime"); @@ -261,7 +262,7 @@ public void processOpts() { supportingFiles.add(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); supportingFiles.add(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); supportingFiles.add(new SupportingFile("failedRequest.mustache", modelFileFolderRelative(), "_FailedRequest.scala")); - supportingFiles.add(new SupportingFile("authModel.mustache", modelFileFolderRelative(), "Authorization.scala")); + supportingFiles.add(new SupportingFile("authModel.mustache", modelFileFolderRelative(), "_Authorization.scala")); supportingFiles.add(new SupportingFile("modelsPackage.mustache", modelFileFolderRelative(), "package.scala")); if (additionalProperties.containsKey(EXCLUDE_SBT)) { diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index 74f95ef52362..0b3975223ee6 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -23,7 +23,7 @@ trait {{classname}}Endpoints[F[*]] { {{#operations}} class {{classname}}EndpointsImpl[F[*]: Concurrent]( override val baseUrl: Uri, - defaultAuth: Option[Authorization] = None, + defaultAuth: Option[_Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, httpClient: Http4sClient[F] ) extends BaseClient[F](baseUrl, defaultAuth, defaultHeaders, httpClient) with {{classname}}Endpoints[F] { @@ -34,14 +34,20 @@ class {{classname}}EndpointsImpl[F[*]: Concurrent]( {{#operation}} override def {{operationId}}({{>methodParameters}}): F[{{>operationReturnType}}] = { - val requestHeaders = {{>headerParamCreation}} - {{>queryParams}} + val requestHeaders = {{>headerParamCreation}}{{#hasFormParams}} + val formParameters = Some(({{#formParams}} + {{>paramsCreation}}{{/formParams}} + ).toSeq.flatten){{/hasFormParams}}{{#hasQueryParams}} + val queryParameters = ({{#queryParams}} + {{>paramsCreation}}{{/queryParams}} + ).toSeq.flatten{{/hasQueryParams}} _executeRequest[{{>bodyParamType}}, {{>operationReturnType}}]( method = "{{httpMethod.toUpperCase}}", path = s"{{{path}}}", body = {{#bodyParam}}{{#required}}Some({{paramName}}){{/required}}{{^required}}{{paramName}}{{/required}}{{/bodyParam}}{{^bodyParam}}None{{/bodyParam}}, - queryParameters = queryParameters, + formParameters = {{^hasFormParams}}None,{{/hasFormParams}}{{#hasFormParams}}formParameters,{{/hasFormParams}} + queryParameters = {{^hasQueryParams}}Nil,{{/hasQueryParams}}{{#hasQueryParams}}queryParameters,{{/hasQueryParams}} requestHeaders = requestHeaders, auth = {{#authMethods}}Some(auth){{/authMethods}}{{^authMethods}}None{{/authMethods}}) { {{>responseState}} diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache index 50f982b426ed..2fb0c00725f1 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache @@ -1,10 +1,10 @@ {{>licenseInfo}} package {{modelPackage}} -sealed trait Authorization extends _root_.scala.Product with _root_.scala.Serializable +sealed trait _Authorization extends _root_.scala.Product with _root_.scala.Serializable -object Authorization { - final case class Basic(username: String, password: Option[String] = None) extends Authorization - final case class ApiKey(value: String) extends Authorization - final case class Bearer(token: String) extends Authorization +object _Authorization { + final case class Basic(username: String, password: Option[String] = None) extends _Authorization + final case class ApiKey(value: String) extends _Authorization + final case class Bearer(token: String) extends _Authorization } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache index 64fc617d83c5..7662ff022be5 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -13,7 +13,7 @@ import {{modelPackage}}.* abstract class BaseClient[F[*]: Concurrent]( val baseUrl: Uri, - defaultAuth: Option[Authorization] = None, + defaultAuth: Option[_Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, httpClient: Http4sClient[F] ) { @@ -32,10 +32,10 @@ abstract class BaseClient[F[*]: Concurrent]( method: String, path: String, body: Option[T] = None, - formBody : Option[UrlForm] = None, + formParameters: Option[Seq[(String, Any)]] = None, queryParameters: Seq[(String, Any)] = Nil, requestHeaders: Seq[(String, String)] = Nil, - auth: Option[Authorization] = None + auth: Option[_Authorization] = None )(handler: Response[F] => F[U])(implicit encoder: Encoder[T]): F[U] = { val m = Method.fromString(method) match { @@ -57,15 +57,20 @@ abstract class BaseClient[F[*]: Concurrent]( val request = Request[F](method = m, uri = uri, headers = headers) val reqAndMaybeAuth = auth.orElse(defaultAuth).fold(request) { - case Authorization.Basic(username, passwordOpt) => + case _Authorization.Basic(username, passwordOpt) => val userpass = s"$username:${passwordOpt.getOrElse("")}" - val token = Base64.getEncoder.encodeToString(userpass.getBytes(StandardCharsets.ISO_8859_1)) + val token = Base64.getEncoder.encodeToString( + userpass.getBytes(StandardCharsets.ISO_8859_1) + ) request.putHeaders(Header.Raw(CIString("Authorization"), s"Basic $token")) - case Authorization.Bearer(token) => + case _Authorization.Bearer(token) => request.putHeaders(Header.Raw(CIString("Authorization"), s"Bearer $token")) - case Authorization.ApiKey(apiKey) => + case _Authorization.ApiKey(apiKey) => request.putHeaders(Header.Raw(CIString("api_key"), apiKey)) } + val formBody = formParameters.map { x => + UrlForm(x.groupBy(_._1).map{case (k, v) => (k, v.mkString(","))}.toSeq*) + } import JsonSupports.* val reqAndMaybeAuthAndBody = diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache index f1e2085e5c27..c80db9ee5675 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache @@ -1 +1 @@ -{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}Option[{{dataType}}]{{/required}}{{^defaultValue}}{{^required}} = None{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#authMethods.0}})(implicit auth: {{#isApiKey}}Authorization.ApiKey{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}Authorization.Basic{{/isBasicBasic}}{{#isBasicBearer}}Authorization.Bearer{{/isBasicBearer}}{{/isBasic}}{{/authMethods.0}} \ No newline at end of file +{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}Option[{{dataType}}]{{/required}}{{^defaultValue}}{{^required}} = None{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#authMethods.0}}{{#isApiKey}})(implicit auth: _Authorization.ApiKey{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}})(implicit auth: _Authorization.Basic{{/isBasicBasic}}{{#isBasicBearer}})(implicit auth: _Authorization.Bearer{{/isBasicBearer}}{{/isBasic}}{{/authMethods.0}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramsCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramsCreation.mustache new file mode 100644 index 000000000000..361e7e889737 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramsCreation.mustache @@ -0,0 +1 @@ +{{#isContainer}}{{#required}}Some({{paramName}}.map("{{baseName}}" -> _)){{/required}}{{^required}}{{paramName}}.map(x => x.map("{{baseName}}" -> _)){{/required}}{{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache deleted file mode 100644 index c6604e546a69..000000000000 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/queryParams.mustache +++ /dev/null @@ -1,3 +0,0 @@ -{{#hasQueryParams}}val queryParameters = ({{#queryParams}} - {{#isContainer}}{{#required}}Some({{paramName}}.map("{{baseName}}" -> _)){{/required}}{{^required}}{{paramName}}.map(x => x.map("{{baseName}}" -> _)){{/required}}{{/isContainer}}{{^isContainer}}{{#required}}Some(Seq("{{baseName}}" -> {{{paramName}}})){{/required}}{{^required}}{{paramName}}.map("{{baseName}}" -> _).map(Seq(_)){{/required}}{{/isContainer}}{{^-last}} ++ {{/-last}}{{/queryParams}} - ).toSeq.flatten{{/hasQueryParams}}{{^hasQueryParams}}val queryParameters = Nil{{/hasQueryParams}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache index c959ec03ab1b..b8aa1645d66f 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache @@ -1,4 +1,5 @@ -{{#hasOnlyDefaultResponse}}{{#responses.0}}{{#dataType}}r => parseJson[F, {{>operationReturnType}}]("{{>operationReturnType}}", r){{/dataType}}{{^dataType}}r => Concurrent[F].pure(()){{/dataType}}{{/responses.0}}{{/hasOnlyDefaultResponse}} - {{^hasOnlyDefaultResponse}}{{#responses}}{{#is2xx}}{{>successResponsePart}}{{/is2xx}}{{^is2xx}} - {{#is3xx}}{{>successResponsePart}}{{/is3xx}} - {{^is3xx}}{{^isDefault}}{{>errorResponsePart}}{{/isDefault}}{{#isDefault}}{{#dataType}}case r => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}}{{/hasOnlyDefaultResponse}} \ No newline at end of file +{{#hasOnlyDefaultResponse}}{{#responses.0}}{{#dataType}}r => parseJson[F, {{>operationReturnType}}]("{{>operationReturnType}}", r){{/dataType}}{{^dataType}}r => Concurrent[F].pure(()){{/dataType}}{{/responses.0}}{{/hasOnlyDefaultResponse}}{{^hasOnlyDefaultResponse}}{{#responses}}{{#is2xx}} + {{>successResponsePart}}{{/is2xx}}{{^is2xx}}{{#is3xx}} + {{>successResponsePart}}{{/is3xx}}{{^is3xx}}{{^isDefault}} + {{>errorResponsePart}}{{/isDefault}}{{#isDefault}} + {{#dataType}}case r => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}}{{/hasOnlyDefaultResponse}} \ No newline at end of file From 9ab85ff8cbda579c4631d61504645b4c6e5f76a9 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Tue, 17 Sep 2024 16:29:54 -0400 Subject: [PATCH 10/18] add modelsOnly --- .../ScalaHttp4sScala3ClientCodegen.java | 17 +++++++++++++---- .../resources/scala-http4s-scala3/api.mustache | 3 +-- .../scala-http4s-scala3/baseClient.mustache | 5 ++--- .../scala-http4s-scala3/jsonSupports.mustache | 4 +++- .../scala-http4s-scala3/model.mustache | 11 +++++++---- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java index 086690432260..efdebf62a5cc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java @@ -42,10 +42,12 @@ public class ScalaHttp4sScala3ClientCodegen extends AbstractScalaCodegen impleme protected String groupId = "org.openapitools"; protected String artifactId = "http4s-client-scala3"; protected String artifactVersion = "1.0.0"; - protected boolean registerNonStandardStatusCodes = true; //TODO?? + protected boolean registerNonStandardStatusCodes = true; protected boolean removeOAuthSecurities = true; //TODO?? protected boolean excludeSbt = false; - public static final String EXCLUDE_SBT = "excludeSbt"; + protected boolean modelsOnly = false; + protected static final String EXCLUDE_SBT = "excludeSbt"; + protected static final String MODELS_ONLY = "modelsOnly"; protected String sourceFolder = "src" + File.separator + "main" + File.separator + "scala"; protected String projectName = artifactId; @@ -259,8 +261,6 @@ public void processOpts() { additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); - supportingFiles.add(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); - supportingFiles.add(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); supportingFiles.add(new SupportingFile("failedRequest.mustache", modelFileFolderRelative(), "_FailedRequest.scala")); supportingFiles.add(new SupportingFile("authModel.mustache", modelFileFolderRelative(), "_Authorization.scala")); supportingFiles.add(new SupportingFile("modelsPackage.mustache", modelFileFolderRelative(), "package.scala")); @@ -272,6 +272,15 @@ public void processOpts() { supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); } + if (additionalProperties.containsKey(MODELS_ONLY)) { + this.modelsOnly = convertPropertyToBoolean(MODELS_ONLY); + } + if (!modelsOnly) { + supportingFiles.add(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); + supportingFiles.add(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); + } else { + apiTemplateFiles.remove("api.mustache"); + } } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache index 0b3975223ee6..339ecc9bd37f 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache @@ -23,10 +23,9 @@ trait {{classname}}Endpoints[F[*]] { {{#operations}} class {{classname}}EndpointsImpl[F[*]: Concurrent]( override val baseUrl: Uri, - defaultAuth: Option[_Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, httpClient: Http4sClient[F] -) extends BaseClient[F](baseUrl, defaultAuth, defaultHeaders, httpClient) with {{classname}}Endpoints[F] { +) extends BaseClient[F](baseUrl, defaultHeaders, httpClient) with {{classname}}Endpoints[F] { import JsonSupports.* import io.circe.syntax.EncoderOps diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache index 7662ff022be5..7b4bfd535bc9 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache @@ -13,7 +13,6 @@ import {{modelPackage}}.* abstract class BaseClient[F[*]: Concurrent]( val baseUrl: Uri, - defaultAuth: Option[_Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, httpClient: Http4sClient[F] ) { @@ -52,11 +51,11 @@ abstract class BaseClient[F[*]: Concurrent]( ) val queryMap = queryParameters.groupBy(_._1).map { case (k, v) => k -> v.map(_._2.toString) } - val uri = (baseUrl / path).setQueryParams(queryMap) + val uri = Uri.unsafeFromString(s"$baseUrl$path").setQueryParams(queryMap) val request = Request[F](method = m, uri = uri, headers = headers) - val reqAndMaybeAuth = auth.orElse(defaultAuth).fold(request) { + val reqAndMaybeAuth = auth.fold(request) { case _Authorization.Basic(username, passwordOpt) => val userpass = s"$username:${passwordOpt.getOrElse("")}" val token = Base64.getEncoder.encodeToString( diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache index a4283468b665..6b6ae135e041 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache @@ -20,7 +20,9 @@ object JsonSupports { r: Response[F] )(implicit decoder: Decoder[T]): F[T] = r.attemptAs[T].value.flatMap { case Right(value) => Concurrent[F].pure(value) - case Left(error) => Concurrent[F].raiseError(_FailedRequest(r.status.code, s"Invalid json for class[" + className + "]")) + case Left(error) => Concurrent[F].raiseError( + _FailedRequest(r.status.code, s"Invalid json for class[$className]: error $error") + ) } } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache index 810e09336e14..719f67f0fb17 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache @@ -21,14 +21,17 @@ import {{import}} enum {{classname}}(val value: String) { {{#allowableValues}} {{#values}} - case {{#fnEnumEntry}}{{#lambda.camelcase}}{{.}}{{/lambda.camelcase}}{{/fnEnumEntry}} extends {{classname}}("{{.}}") + case {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}} extends {{classname}}("{{.}}") {{/values}} {{/allowableValues}} } object {{classname}} { given decoder{{classname}}: Decoder[{{classname}}] = - Decoder.decodeString.map(str => {{classname}}.valueOf(str)) + Decoder.decodeString.map(str => {{classname}}.values.find(_.value == str) + .getOrElse(throw java.lang.IllegalArgumentException(s"{{classname}} enum case not found: $str")) + ) + given encoder{{classname}}: Encoder[{{classname}}] = Encoder.encodeString.contramap[{{classname}}](_.value) } @@ -45,7 +48,7 @@ object {{classname}} { Json.fromFields{ Seq( {{#vars}} - {{#required}}Some("{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}" -> t.{{name}}.asJson){{/required}}{{^required}}t.{{name}}.map(v => "{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}" -> v.asJson){{/required}}{{^-last}},{{/-last}} + {{#required}}Some("{{baseName}}" -> t.{{name}}.asJson){{/required}}{{^required}}t.{{name}}.map(v => "{{baseName}}" -> v.asJson){{/required}}{{^-last}},{{/-last}} {{/vars}} ).flatten } @@ -53,7 +56,7 @@ object {{classname}} { given decoder{{classname}}: Decoder[{{classname}}] = Decoder.instance { c => for { {{#vars}} - {{name}} <- c.downField("{{#lambda.snakecase}}{{name}}{{/lambda.snakecase}}").as[{{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}] + {{name}} <- c.downField("{{baseName}}").as[{{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}] {{/vars}} } yield {{classname}}( {{#vars}} From 9cd4b2473370639709b700d4044ffa5964b5fe62 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Fri, 20 Sep 2024 15:51:09 -0400 Subject: [PATCH 11/18] add unit tests --- bin/configs/scala-http4s.yaml | 6 + docs/templating.md | 2 +- ...gen.java => ScalaHttp4sClientCodegen.java} | 26 +- .../org.openapitools.codegen.CodegenConfig | 2 +- .../api.mustache | 0 .../authModel.mustache | 0 .../baseClient.mustache | 0 .../bodyParamType.mustache | 0 .../build.sbt.mustache | 0 .../errorResponsePart.mustache | 0 .../failedRequest.mustache | 0 .../headerParamCreation.mustache | 0 .../jsonSupports.mustache | 0 .../licenseInfo.mustache | 0 .../methodParameters.mustache | 0 .../model.mustache | 0 .../modelsPackage.mustache | 0 .../operationReturnType.mustache | 0 .../paramsCreation.mustache | 0 .../project/build.properties.mustache | 0 .../responseState.mustache | 0 .../successResponsePart.mustache | 0 ...laHttp4sClientCodegenOptionsProvider.java} | 33 +- .../scala/ScalaHttp4sClientCodegenTest.java | 157 ++++ ...alaHttp4sScala3ClientCodegenModelTest.java | 31 - .../ScalaHttp4sScala3ClientCodegenTest.java | 19 - .../resources/3_0/scala-http4s/petstore.yaml | 805 ++++++++++++++++++ 27 files changed, 1004 insertions(+), 77 deletions(-) create mode 100644 bin/configs/scala-http4s.yaml rename modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/{ScalaHttp4sScala3ClientCodegen.java => ScalaHttp4sClientCodegen.java} (94%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/api.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/authModel.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/baseClient.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/bodyParamType.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/build.sbt.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/errorResponsePart.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/failedRequest.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/headerParamCreation.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/jsonSupports.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/licenseInfo.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/methodParameters.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/model.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/modelsPackage.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/operationReturnType.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/paramsCreation.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/project/build.properties.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/responseState.mustache (100%) rename modules/openapi-generator/src/main/resources/{scala-http4s-scala3 => scala-http4s}/successResponsePart.mustache (100%) rename modules/openapi-generator/src/test/java/org/openapitools/codegen/options/{ScalaHttp4sScala3ClientCodegenOptionsProvider.java => ScalaHttp4sClientCodegenOptionsProvider.java} (66%) create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java delete mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java delete mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java create mode 100644 modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml diff --git a/bin/configs/scala-http4s.yaml b/bin/configs/scala-http4s.yaml new file mode 100644 index 000000000000..eb0479e6d4e2 --- /dev/null +++ b/bin/configs/scala-http4s.yaml @@ -0,0 +1,6 @@ +generatorName: scala-http4s +outputDir: samples/client/petstore/scala-http4s +inputSpec: modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml +templateDir: modules/openapi-generator/src/main/resources/scala-http4s +additionalProperties: + artifactId: scala-http4s-client diff --git a/docs/templating.md b/docs/templating.md index 5465cbe81074..eab039bb2222 100644 --- a/docs/templating.md +++ b/docs/templating.md @@ -257,7 +257,7 @@ cd ~/.openapi-generator/example gradle assemble # or, regenerate the wrapper gradle wrapper --gradle-version 4.8 --distribution-type all -./gradlew assemblef +./gradlew assemble ``` You should see a log message showing our added dependency being downloaded: diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java similarity index 94% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java index efdebf62a5cc..a6d1687bb44c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sScala3ClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java @@ -21,6 +21,7 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; +import lombok.Getter; import org.openapitools.codegen.*; import org.openapitools.codegen.meta.features.*; import org.openapitools.codegen.model.ModelMap; @@ -35,16 +36,18 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ScalaHttp4sScala3ClientCodegen extends AbstractScalaCodegen implements CodegenConfig { - private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sScala3ClientCodegen.class); +public class ScalaHttp4sClientCodegen extends AbstractScalaCodegen implements CodegenConfig { + private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sClientCodegen.class); - protected String mainPackage = "org.openapitools.client"; + private String mainPackage = "org.openapitools.client"; protected String groupId = "org.openapitools"; - protected String artifactId = "http4s-client-scala3"; + protected String artifactId = "scala-http4s-client"; protected String artifactVersion = "1.0.0"; protected boolean registerNonStandardStatusCodes = true; - protected boolean removeOAuthSecurities = true; //TODO?? + protected boolean removeOAuthSecurities = true; + @Getter protected boolean excludeSbt = false; + @Getter protected boolean modelsOnly = false; protected static final String EXCLUDE_SBT = "excludeSbt"; protected static final String MODELS_ONLY = "modelsOnly"; @@ -57,14 +60,14 @@ public CodegenType getTag() { } public String getName() { - return "scala-http4s-scala3"; + return "scala-http4s"; } public String getHelp() { - return "Generates a scala-http4s-scala3 client."; + return "Generates a scala-http4s client."; } - public ScalaHttp4sScala3ClientCodegen() { + public ScalaHttp4sClientCodegen() { super(); modifyFeatureSet(features -> features @@ -96,7 +99,7 @@ public ScalaHttp4sScala3ClientCodegen() { supportsMixins = true; addOneOfInterfaceImports = true; - embeddedTemplateDir = templateDir = "scala-http4s-scala3"; + embeddedTemplateDir = templateDir = "scala-http4s"; modelTemplateFiles.put("model.mustache", ".scala"); apiTemplateFiles.put("api.mustache", ".scala"); @@ -271,6 +274,9 @@ public void processOpts() { if (!excludeSbt) { supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); + } else { + supportingFiles.remove(new SupportingFile("build.sbt.mustache", "", "build.sbt")); + supportingFiles.remove(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); } if (additionalProperties.containsKey(MODELS_ONLY)) { this.modelsOnly = convertPropertyToBoolean(MODELS_ONLY); @@ -279,6 +285,8 @@ public void processOpts() { supportingFiles.add(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); supportingFiles.add(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); } else { + supportingFiles.remove(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); + supportingFiles.remove(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); apiTemplateFiles.remove("api.mustache"); } } 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 90d8aa21ed62..fcfde2b56601 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 @@ -124,7 +124,7 @@ org.openapitools.codegen.languages.ScalaPekkoClientCodegen org.openapitools.codegen.languages.ScalaAkkaHttpServerCodegen org.openapitools.codegen.languages.ScalaFinchServerCodegen org.openapitools.codegen.languages.ScalaGatlingCodegen -org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen +org.openapitools.codegen.languages.ScalaHttp4sClientCodegen org.openapitools.codegen.languages.ScalaHttp4sServerCodegen org.openapitools.codegen.languages.ScalaLagomServerCodegen org.openapitools.codegen.languages.ScalaPlayFrameworkServerCodegen diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/api.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/api.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/api.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/authModel.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/authModel.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/authModel.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/baseClient.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/bodyParamType.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/bodyParamType.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/bodyParamType.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/bodyParamType.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/build.sbt.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/build.sbt.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/build.sbt.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/errorResponsePart.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/errorResponsePart.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/errorResponsePart.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/errorResponsePart.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/failedRequest.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/failedRequest.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/failedRequest.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/headerParamCreation.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/headerParamCreation.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/headerParamCreation.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/jsonSupports.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/jsonSupports.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/jsonSupports.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/licenseInfo.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/licenseInfo.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/licenseInfo.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/methodParameters.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/methodParameters.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/methodParameters.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/model.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/model.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/modelsPackage.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/modelsPackage.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/modelsPackage.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/modelsPackage.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/operationReturnType.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/operationReturnType.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/operationReturnType.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/operationReturnType.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramsCreation.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/paramsCreation.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/paramsCreation.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/paramsCreation.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/project/build.properties.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/project/build.properties.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/project/build.properties.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/project/build.properties.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/responseState.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/responseState.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/responseState.mustache diff --git a/modules/openapi-generator/src/main/resources/scala-http4s-scala3/successResponsePart.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/successResponsePart.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/scala-http4s-scala3/successResponsePart.mustache rename to modules/openapi-generator/src/main/resources/scala-http4s/successResponsePart.mustache diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sScala3ClientCodegenOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java similarity index 66% rename from modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sScala3ClientCodegenOptionsProvider.java rename to modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java index 8c2e00bc783f..0703f409c420 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sScala3ClientCodegenOptionsProvider.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java @@ -1,46 +1,49 @@ -/* package org.openapitools.codegen.options; import org.openapitools.codegen.CodegenConstants; -import org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen; - import com.google.common.collect.ImmutableMap; +import java.io.File; import java.util.Map; -public class ScalaHttp4sScala3ClientCodegenOptionsProvider implements OptionsProvider { +public class ScalaHttp4sClientCodegenOptionsProvider implements OptionsProvider { public static final String PROJECT_NAME_VALUE = "OpenAPI"; - public static final String MODEL_PACKAGE_VALUE = "package"; - 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 PACKAGE_NAME_VALUE = "org.openapitools.client"; + public static final String MODEL_PACKAGE_VALUE = PACKAGE_NAME_VALUE + ".models"; + public static final String API_PACKAGE_VALUE = PACKAGE_NAME_VALUE + ".apis"; + public static final String MODEL_PROPERTY_NAMING = "camelCase"; + public static final String SOURCE_FOLDER_VALUE = "src" + File.separator + "main" + File.separator + "scala"; 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 MODEL_PROPERTY_NAMING = "PascalCase"; public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false"; public static final String PREPEND_FORM_OR_BODY_PARAMETERS_VALUE = "true"; public static final String DATE_LIBRARY = "java8"; @Override public String getLanguage() { - return "scala-http4s-scala3"; + return "scala-http4s"; } @Override public Map createOptions() { ImmutableMap.Builder builder = new ImmutableMap.Builder(); - return builder.put(CodegenConstants.MODEL_PACKAGE, MODEL_PACKAGE_VALUE) + return builder + .put(CodegenConstants.PROJECT_NAME, PROJECT_NAME_VALUE) + .put(CodegenConstants.PACKAGE_NAME, PACKAGE_NAME_VALUE) + .put(CodegenConstants.MODEL_PACKAGE, MODEL_PACKAGE_VALUE) .put(CodegenConstants.API_PACKAGE, API_PACKAGE_VALUE) + .put(CodegenConstants.MODEL_PROPERTY_NAMING, MODEL_PROPERTY_NAMING) + .put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_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(CodegenConstants.MODEL_PROPERTY_NAMING, MODEL_PROPERTY_NAMING) .put("dateLibrary", DATE_LIBRARY) .put(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "true") + .put("excludeSbt", "false") + .put("modelsOnly", "false") .build(); } @@ -48,6 +51,4 @@ public Map createOptions() { public boolean isServer() { return false; } -} - -*/ +} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java new file mode 100644 index 000000000000..8fda0a18b0be --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java @@ -0,0 +1,157 @@ +package org.openapitools.codegen.scala; + +import io.swagger.v3.oas.models.media.DateTimeSchema; +import io.swagger.v3.oas.models.media.IntegerSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.parser.util.SchemaTypeUtil; +import org.jetbrains.annotations.NotNull; +import org.openapitools.codegen.*; +import org.openapitools.codegen.config.CodegenConfigurator; +import org.openapitools.codegen.languages.ScalaHttp4sClientCodegen; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +import static org.testng.Assert.*; + +public class ScalaHttp4sClientCodegenTest { + + @Test(description = "convert a simple java model") + public void simpleModelTest() { + ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); + final Schema model = new Schema() + .description("a sample model") + .addProperty("id", new IntegerSchema().format(SchemaTypeUtil.INTEGER64_FORMAT)) + .addProperty("name", new StringSchema()) + .addProperty("created_at", new DateTimeSchema()) + .addRequiredItem("id") + .addRequiredItem("name"); + CodegenModel cm = codegen.fromModel("sample", model); + + assertEquals(cm.name, "sample"); + assertEquals(cm.classname, "Sample"); + assertEquals(cm.description, "a sample model"); + assertEquals(cm.vars.size(), 3); + assertEquals(cm.vars.stream().filter(CodegenProperty::getRequired).count(), 2); + } + + @Test(description = "happy path test") + public void happyPathTest() throws IOException { + ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); + assertEquals(codegen.getName(), "scala-http4s"); + assertEquals(codegen.getTag(), CodegenType.CLIENT); + + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName(codegen.getName()) + .setInputSpec("src/test/resources/3_0/scala/petstore.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = getDefaultGenerator(); + + List files = generator.opts(clientOptInput).generate(); + + TestUtils.ensureContainsFile(files, output, "build.sbt"); + TestUtils.ensureContainsFile(files, output, "project/build.properties"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/package.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/_Authorization.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/_FailedRequest.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/ApiResponse.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/Category.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/Order.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/OrderStatus.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/Pet.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/PetStatus.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/Tag.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/models/User.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/apis/BaseClient.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/apis/JsonSupports.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/apis/PetApi.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/apis/StoreApi.scala"); + TestUtils.ensureContainsFile(files, output, "src/main/scala/org/openapitools/client/apis/UserApi.scala"); + + } + + @NotNull + private static DefaultGenerator getDefaultGenerator() { + DefaultGenerator generator = new DefaultGenerator(); + + generator.setGenerateMetadata(false); + + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true"); + return generator; + } + + @Test(description = "use Instant for date-time") + public void dateTimeToInstant() { + ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); + + final Schema schema = new Schema() + .description("Schema with date-time"); + schema.setType("string"); + schema.setFormat("date-time"); + String type = codegen.getTypeDeclaration(schema); + assertEquals(type, "Instant"); + } + + @Test + public void allowExcludeSbtFiles() { + ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); + assertFalse(codegen.isExcludeSbt()); + codegen.additionalProperties().put("excludeSbt", "true"); + assertEquals(codegen.additionalProperties().get("excludeSbt"), "true"); + codegen.processOpts(); + assertTrue(codegen.isExcludeSbt()); + } + + @Test + public void allowModelsOnly() { + ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); + assertFalse(codegen.isModelsOnly()); + codegen.additionalProperties().put("modelsOnly", "true"); + assertEquals(codegen.additionalProperties().get("modelsOnly"), "true"); + codegen.processOpts(); + assertTrue(codegen.isModelsOnly()); + } + + @Test + public void convertVarNameCamelCase() { + ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); + assertEquals(CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.camelCase.name(), codegen.getModelPropertyNaming()); + assertEquals(codegen.toVarName("name"), "name"); + assertEquals(codegen.toVarName("user-name"), "userName"); + assertEquals(codegen.toVarName("user_name"), "userName"); + assertEquals(codegen.toVarName("user|name"), "userName"); + assertEquals(codegen.toVarName("uSername"), "uSername"); + assertEquals(codegen.toVarName("USERNAME"), "USERNAME"); + assertEquals(codegen.toVarName("USER123NAME"), "USER123NAME"); + assertEquals(codegen.toVarName("1"), "`1`"); + assertEquals(codegen.toVarName("1a"), "`1a`"); + assertEquals(codegen.toVarName("1A"), "`1A`"); + assertEquals(codegen.toVarName("1AAAA"), "`1AAAA`"); + assertEquals(codegen.toVarName("1AAaa"), "`1aAaa`"); + } + + @Test + public void encodePath() { + ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); + assertEquals(codegen.encodePath("{user_name}"), "${userName}"); + assertEquals(codegen.encodePath("{userName}"), "${userName}"); + assertEquals(codegen.encodePath("{UserName}"), "${userName}"); + assertEquals(codegen.encodePath("user_name"), "user_name"); + assertEquals(codegen.encodePath("before/{UserName}/after"), "before/${userName}/after"); + } + +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java deleted file mode 100644 index fca5eb70249c..000000000000 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenModelTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.openapitools.codegen.scala.http4s.scala3; - -import org.openapitools.codegen.*; -import org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen; -import io.swagger.models.*; -import io.swagger.models.properties.*; - -import org.testng.Assert; -import org.testng.annotations.Test; - -@SuppressWarnings("static-method") -public class ScalaHttp4sScala3ClientCodegenModelTest { - - @Test(description = "convert a simple java model") - public void simpleModelTest() { - final Model model = new ModelImpl() - .description("a sample model") - .property("id", new LongProperty()) - .property("name", new StringProperty()) - .required("id") - .required("name"); - final ScalaHttp4sScala3ClientCodegen codegen = new ScalaHttp4sScala3ClientCodegen(); - - - - // TODO: Complete this test. - //Assert.fail("Not implemented."); - } - -} - diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java deleted file mode 100644 index 1665bcd5cf4c..000000000000 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/http4s/scala3/ScalaHttp4sScala3ClientCodegenTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.openapitools.codegen.scala.http4s.scala3; - -import org.openapitools.codegen.*; -import org.openapitools.codegen.languages.ScalaHttp4sScala3ClientCodegen; -import io.swagger.models.*; -import io.swagger.parser.SwaggerParser; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class ScalaHttp4sScala3ClientCodegenTest { - - ScalaHttp4sScala3ClientCodegen codegen = new ScalaHttp4sScala3ClientCodegen(); - - @Test - public void shouldSucceed() throws Exception { - // TODO: Complete this test. - //Assert.fail("Not implemented."); - } -} diff --git a/modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml b/modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml new file mode 100644 index 000000000000..cfd47219f210 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml @@ -0,0 +1,805 @@ +openapi: 3.0.0 +servers: + - url: 'http://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 + deprecated: true + 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: + '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: + - api_key: [] + 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: + - api_key: [] + 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: + - api_key: [] + 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 `api_key` + 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: + - api_key: [] + '/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: + - api_key: [] + 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: + - api_key: [] + /fake/parameter-name-mapping: + get: + tags: + - fake + summary: parameter name mapping test + operationId: getParameterNameMapping + parameters: + - name: _type + in: header + description: _type + required: true + schema: + type: integer + format: int64 + - name: type + in: query + description: type + required: true + schema: + type: string + - name: type_ + in: header + description: type_ + required: true + schema: + type: string + - name: http_debug_option + in: query + description: http debug option (to test parameter naming option) + required: true + schema: + type: string + responses: + 200: + description: OK +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 + 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 + deprecated: true + 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 + EnumTest: + properties: + emails: + items: + type: string + type: array + search: + enum: + - first_name + - last_name + - email + - full_name + type: string + sort_by: + items: + enum: + - first_name + - last_name + - email + type: string + type: array + type: object + PropertyNameMapping: + properties: + http_debug_operation: + type: string + _type: + type: string + type: + type: string + type_: + type: string From a75eb5259d21a22ff43a33574c5efeb3ff5d187f Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Mon, 23 Sep 2024 11:27:18 -0400 Subject: [PATCH 12/18] add petstore samples --- .../scala/ScalaHttp4sClientCodegenTest.java | 2 +- .../resources/3_0/scala-http4s/petstore.yaml | 91 ++------ .../scala-http4s/.openapi-generator-ignore | 23 ++ .../scala-http4s/.openapi-generator/FILES | 20 ++ .../scala-http4s/.openapi-generator/VERSION | 1 + .../client/petstore/scala-http4s/build.sbt | 35 +++ .../scala-http4s/project/build.properties | 1 + .../openapitools/client/apis/BaseClient.scala | 92 ++++++++ .../client/apis/JsonSupports.scala | 39 ++++ .../org/openapitools/client/apis/PetApi.scala | 218 ++++++++++++++++++ .../openapitools/client/apis/StoreApi.scala | 118 ++++++++++ .../openapitools/client/apis/UserApi.scala | 197 ++++++++++++++++ .../client/models/ApiResponse.scala | 53 +++++ .../openapitools/client/models/Category.scala | 48 ++++ ...FindPetsByStatusStatusParameterInner.scala | 37 +++ .../openapitools/client/models/Order.scala | 69 ++++++ .../client/models/OrderStatus.scala | 37 +++ .../org/openapitools/client/models/Pet.scala | 69 ++++++ .../client/models/PetStatus.scala | 37 +++ .../org/openapitools/client/models/Tag.scala | 48 ++++ .../org/openapitools/client/models/User.scala | 78 +++++++ .../client/models/_Authorization.scala | 20 ++ .../client/models/_FailedRequest.scala | 43 ++++ .../openapitools/client/models/package.scala | 42 ++++ 24 files changed, 1340 insertions(+), 78 deletions(-) create mode 100644 samples/client/petstore/scala-http4s/.openapi-generator-ignore create mode 100644 samples/client/petstore/scala-http4s/.openapi-generator/FILES create mode 100644 samples/client/petstore/scala-http4s/.openapi-generator/VERSION create mode 100644 samples/client/petstore/scala-http4s/build.sbt create mode 100644 samples/client/petstore/scala-http4s/project/build.properties create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala create mode 100644 samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java index 8fda0a18b0be..3042ec6ad4ba 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java @@ -50,7 +50,7 @@ public void happyPathTest() throws IOException { final CodegenConfigurator configurator = new CodegenConfigurator() .setGeneratorName(codegen.getName()) - .setInputSpec("src/test/resources/3_0/scala/petstore.yaml") + .setInputSpec("src/test/resources/3_0/scala-http4s/petstore.yaml") .setOutputDir(output.getAbsolutePath().replace("\\", "/")); final ClientOptInput clientOptInput = configurator.toClientOptInput(); diff --git a/modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml b/modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml index cfd47219f210..947376fc0082 100644 --- a/modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/scala-http4s/petstore.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 servers: - - url: 'http://petstore.swagger.io/v2' + - url: 'https://petstore.swagger.io/v2' info: description: >- This is a sample server Petstore server. For this sample, you can use the api key @@ -85,7 +85,6 @@ paths: required: true style: form explode: false - deprecated: true schema: type: array items: @@ -200,6 +199,8 @@ paths: type: integer format: int64 responses: + '200': + description: successful operation '405': description: Invalid input security: @@ -392,7 +393,7 @@ paths: default: description: successful operation security: - - api_key: [] + - auth_cookie: [] requestBody: content: application/json: @@ -411,7 +412,7 @@ paths: default: description: successful operation security: - - api_key: [] + - auth_cookie: [] requestBody: $ref: '#/components/requestBodies/UserArray' /user/createWithList: @@ -425,7 +426,7 @@ paths: default: description: successful operation security: - - api_key: [] + - auth_cookie: [] requestBody: $ref: '#/components/requestBodies/UserArray' /user/login: @@ -455,7 +456,7 @@ paths: headers: Set-Cookie: description: >- - Cookie authentication key for use with the `api_key` + Cookie authentication key for use with the `auth_cookie` apiKey authentication. schema: type: string @@ -490,7 +491,7 @@ paths: default: description: successful operation security: - - api_key: [] + - auth_cookie: [] '/user/{username}': get: tags: @@ -538,7 +539,7 @@ paths: '404': description: User not found security: - - api_key: [] + - auth_cookie: [] requestBody: content: application/json: @@ -565,42 +566,7 @@ paths: '404': description: User not found security: - - api_key: [] - /fake/parameter-name-mapping: - get: - tags: - - fake - summary: parameter name mapping test - operationId: getParameterNameMapping - parameters: - - name: _type - in: header - description: _type - required: true - schema: - type: integer - format: int64 - - name: type - in: query - description: type - required: true - schema: - type: string - - name: type_ - in: header - description: type_ - required: true - schema: - type: string - - name: http_debug_option - in: query - description: http debug option (to test parameter naming option) - required: true - schema: - type: string - responses: - 200: - description: OK + - auth_cookie: [] externalDocs: description: Find out more about Swagger url: 'http://swagger.io' @@ -638,6 +604,10 @@ components: type: apiKey name: api_key in: header + auth_cookie: + type: apiKey + name: AUTH_KEY + in: cookie schemas: Order: title: Pet Order @@ -752,7 +722,6 @@ components: status: type: string description: pet status in the store - deprecated: true enum: - available - pending @@ -771,35 +740,3 @@ components: type: string message: type: string - EnumTest: - properties: - emails: - items: - type: string - type: array - search: - enum: - - first_name - - last_name - - email - - full_name - type: string - sort_by: - items: - enum: - - first_name - - last_name - - email - type: string - type: array - type: object - PropertyNameMapping: - properties: - http_debug_operation: - type: string - _type: - type: string - type: - type: string - type_: - type: string diff --git a/samples/client/petstore/scala-http4s/.openapi-generator-ignore b/samples/client/petstore/scala-http4s/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/scala-http4s/.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-http4s/.openapi-generator/FILES b/samples/client/petstore/scala-http4s/.openapi-generator/FILES new file mode 100644 index 000000000000..de2e01d8246f --- /dev/null +++ b/samples/client/petstore/scala-http4s/.openapi-generator/FILES @@ -0,0 +1,20 @@ +.openapi-generator-ignore +build.sbt +project/build.properties +src/main/scala/org/openapitools/client/apis/BaseClient.scala +src/main/scala/org/openapitools/client/apis/JsonSupports.scala +src/main/scala/org/openapitools/client/apis/PetApi.scala +src/main/scala/org/openapitools/client/apis/StoreApi.scala +src/main/scala/org/openapitools/client/apis/UserApi.scala +src/main/scala/org/openapitools/client/models/ApiResponse.scala +src/main/scala/org/openapitools/client/models/Category.scala +src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala +src/main/scala/org/openapitools/client/models/Order.scala +src/main/scala/org/openapitools/client/models/OrderStatus.scala +src/main/scala/org/openapitools/client/models/Pet.scala +src/main/scala/org/openapitools/client/models/PetStatus.scala +src/main/scala/org/openapitools/client/models/Tag.scala +src/main/scala/org/openapitools/client/models/User.scala +src/main/scala/org/openapitools/client/models/_Authorization.scala +src/main/scala/org/openapitools/client/models/_FailedRequest.scala +src/main/scala/org/openapitools/client/models/package.scala diff --git a/samples/client/petstore/scala-http4s/.openapi-generator/VERSION b/samples/client/petstore/scala-http4s/.openapi-generator/VERSION new file mode 100644 index 000000000000..17f2442ff3bc --- /dev/null +++ b/samples/client/petstore/scala-http4s/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.9.0-SNAPSHOT diff --git a/samples/client/petstore/scala-http4s/build.sbt b/samples/client/petstore/scala-http4s/build.sbt new file mode 100644 index 000000000000..52c89e8f943d --- /dev/null +++ b/samples/client/petstore/scala-http4s/build.sbt @@ -0,0 +1,35 @@ +scalaVersion := "3.3.3" + +val CirceVersion = "0.14.9" +val Http4sVersion = "0.23.26" + +libraryDependencies ++= Seq( + "org.http4s" %% "http4s-ember-client" % Http4sVersion, + "org.http4s" %% "http4s-circe" % Http4sVersion, + "org.http4s" %% "http4s-dsl" % Http4sVersion, + "org.http4s" %% "http4s-client" % Http4sVersion, + "io.circe" %% "circe-core" % CirceVersion, + "io.circe" %% "circe-generic" % CirceVersion, + "io.circe" %% "circe-parser" % CirceVersion, + "org.scalatest" %% "scalatest" % "3.2.19" % "test" +) + +scalacOptions := Seq( + "-encoding", + "UTF-8", + "-deprecation", + "-unchecked", + "-feature", + "-language:existentials,experimental.macros,higherKinds,implicitConversions,postfixOps,adhocExtensions", + "-Yretain-trees", + "-Xmax-inlines:100", + "-Ykind-projector:underscores", + "-source:future" +) + +lazy val root = project + .in(file(".")) + .settings( + name := "scala-http4s-client", + publishArtifact := false + ) \ No newline at end of file diff --git a/samples/client/petstore/scala-http4s/project/build.properties b/samples/client/petstore/scala-http4s/project/build.properties new file mode 100644 index 000000000000..4d5f78cc412a --- /dev/null +++ b/samples/client/petstore/scala-http4s/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.9 \ No newline at end of file diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala new file mode 100644 index 000000000000..7eb841fd099c --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.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 + * Contact: team@openapitools.org + * + * 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.apis + +import cats.effect.Concurrent +import io.circe.Encoder +import org.http4s.{Header, Headers, Method, Request, Response, Uri, UrlForm} +import org.http4s.client.Client as Http4sClient +import org.http4s.QueryParamEncoder.* +import org.typelevel.ci.CIString +import java.util.Base64 +import java.nio.charset.StandardCharsets +import org.openapitools.client.models.* + +abstract class BaseClient[F[*]: Concurrent]( + val baseUrl: Uri, + defaultHeaders: Seq[(String, String)] = Nil, + httpClient: Http4sClient[F] +) { + + val ApiVersion: String = "" + + private lazy val defaultApiHeaders = Seq( + ("X-Apidoc-Version", ApiVersion) + ) + + protected def apiHeaders: Seq[(String, String)] = defaultApiHeaders + + protected def modifyRequest(request: Request[F]): Request[F] = request + + protected def _executeRequest[T, U]( + method: String, + path: String, + body: Option[T] = None, + formParameters: Option[Seq[(String, Any)]] = None, + queryParameters: Seq[(String, Any)] = Nil, + requestHeaders: Seq[(String, String)] = Nil, + auth: Option[_Authorization] = None + )(handler: Response[F] => F[U])(implicit encoder: Encoder[T]): F[U] = { + + val m = Method.fromString(method) match { + case Right(m) => m + case Left(e) => sys.error(e.toString) + } + + val headers = Headers( + ( + apiHeaders ++ + defaultHeaders ++ + requestHeaders + ).groupBy(_._1).map { case (k, l) => Header.Raw(CIString(k), l.last._2) }.toList + ) + + val queryMap = queryParameters.groupBy(_._1).map { case (k, v) => k -> v.map(_._2.toString) } + val uri = Uri.unsafeFromString(s"$baseUrl$path").setQueryParams(queryMap) + + val request = Request[F](method = m, uri = uri, headers = headers) + + val reqAndMaybeAuth = auth.fold(request) { + case _Authorization.Basic(username, passwordOpt) => + val userpass = s"$username:${passwordOpt.getOrElse("")}" + val token = Base64.getEncoder.encodeToString( + userpass.getBytes(StandardCharsets.ISO_8859_1) + ) + request.putHeaders(Header.Raw(CIString("Authorization"), s"Basic $token")) + case _Authorization.Bearer(token) => + request.putHeaders(Header.Raw(CIString("Authorization"), s"Bearer $token")) + case _Authorization.ApiKey(apiKey) => + request.putHeaders(Header.Raw(CIString("api_key"), apiKey)) + } + val formBody = formParameters.map { x => + UrlForm(x.groupBy(_._1).map{case (k, v) => (k, v.mkString(","))}.toSeq*) + } + + import JsonSupports.* + val reqAndMaybeAuthAndBody = + if (formBody.nonEmpty) formBody.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity) + else body.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity) + + httpClient.run(modifyRequest(reqAndMaybeAuthAndBody)).use(handler) + } + +} diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala new file mode 100644 index 000000000000..3ef9445133d5 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala @@ -0,0 +1,39 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.apis + +import cats.effect.* +import cats.implicits.* +import io.circe.{Decoder, Encoder} +import org.http4s.{EntityDecoder, EntityEncoder, Response} +import org.http4s.circe as http4sCirce +import org.openapitools.client.models.* + +object JsonSupports { + + implicit def circeJsonEncoder[F[*]: Concurrent, A](implicit encoder: Encoder[A]): EntityEncoder[F, A] = + http4sCirce.jsonEncoderOf[F, A] + implicit def circeJsonDecoder[F[*]: Concurrent, A](implicit decoder: Decoder[A]): EntityDecoder[F, A] = + http4sCirce.jsonOf[F, A] + + def parseJson[F[*]: Concurrent, T]( + className: String, + r: Response[F] + )(implicit decoder: Decoder[T]): F[T] = r.attemptAs[T].value.flatMap { + case Right(value) => Concurrent[F].pure(value) + case Left(error) => Concurrent[F].raiseError( + _FailedRequest(r.status.code, s"Invalid json for class[$className]: error $error") + ) + } + +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala new file mode 100644 index 000000000000..a7bea389d96f --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala @@ -0,0 +1,218 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.apis + +import cats.effect.Concurrent +import io.circe.Encoder +import org.http4s.Uri +import org.http4s.client.Client as Http4sClient +import org.openapitools.client.models.ApiResponse +import java.io.File +import org.openapitools.client.models.FindPetsByStatusStatusParameterInner +import org.openapitools.client.models.Pet +import scala.collection.immutable.Seq +import org.openapitools.client.models.* + +trait PetApiEndpoints[F[*]] { + + def addPet(pet: Pet): F[Pet] + def deletePet(petId: Long, apiKey: Option[String] = None): F[Unit] + def findPetsByStatus(status: Seq[FindPetsByStatusStatusParameterInner]): F[Seq[Pet]] + def findPetsByTags(tags: Seq[String]): F[Seq[Pet]] + def getPetById(petId: Long)(implicit auth: _Authorization.ApiKey): F[Pet] + def updatePet(pet: Pet): F[Pet] + def updatePetWithForm(petId: Long, name: Option[String] = None, status: Option[String] = None): F[Unit] + def uploadFile(petId: Long, additionalMetadata: Option[String] = None, file: Option[File] = None): F[ApiResponse] + +} + +class PetApiEndpointsImpl[F[*]: Concurrent]( + override val baseUrl: Uri, + defaultHeaders: Seq[(String, String)] = Nil, + httpClient: Http4sClient[F] +) extends BaseClient[F](baseUrl, defaultHeaders, httpClient) with PetApiEndpoints[F] { + + import JsonSupports.* + import io.circe.syntax.EncoderOps + import cats.implicits.toFlatMapOps + + override def addPet(pet: Pet): F[Pet] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Pet, Pet]( + method = "POST", + path = s"/pet", + body = Some(pet), + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, Pet]("Pet", r) + case r if r.status.code == 405 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def deletePet(petId: Long, apiKey: Option[String] = None): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json"), + apiKey.map(x => "api_key" -> x) + ).flatten + + _executeRequest[Unit, Unit]( + method = "DELETE", + path = s"/pet/${petId}", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def findPetsByStatus(status: Seq[FindPetsByStatusStatusParameterInner]): F[Seq[Pet]] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + val queryParameters = ( + Some(status.map("status" -> _)) + ).toSeq.flatten + + _executeRequest[Unit, Seq[Pet]]( + method = "GET", + path = s"/pet/findByStatus", + body = None, + formParameters = None, + queryParameters = queryParameters, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, Seq[Pet]]("Seq[Pet]", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def findPetsByTags(tags: Seq[String]): F[Seq[Pet]] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + val queryParameters = ( + Some(tags.map("tags" -> _)) + ).toSeq.flatten + + _executeRequest[Unit, Seq[Pet]]( + method = "GET", + path = s"/pet/findByTags", + body = None, + formParameters = None, + queryParameters = queryParameters, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, Seq[Pet]]("Seq[Pet]", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def getPetById(petId: Long)(implicit auth: _Authorization.ApiKey): F[Pet] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Unit, Pet]( + method = "GET", + path = s"/pet/${petId}", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + + case r if r.status.code == 200 => parseJson[F, Pet]("Pet", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 404 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def updatePet(pet: Pet): F[Pet] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Pet, Pet]( + method = "PUT", + path = s"/pet", + body = Some(pet), + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, Pet]("Pet", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 404 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 405 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def updatePetWithForm(petId: Long, name: Option[String] = None, status: Option[String] = None): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/x-www-form-urlencoded") + ).flatten + val formParameters = Some(( + name.map("name" -> _).map(Seq(_)) ++ + status.map("status" -> _).map(Seq(_)) + ).toSeq.flatten) + + _executeRequest[Unit, Unit]( + method = "POST", + path = s"/pet/${petId}", + body = None, + formParameters = formParameters, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => Concurrent[F].pure(()) + case r if r.status.code == 405 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def uploadFile(petId: Long, additionalMetadata: Option[String] = None, file: Option[File] = None): F[ApiResponse] = { + val requestHeaders = Seq( + Some("Content-Type" -> "multipart/form-data") + ).flatten + val formParameters = Some(( + additionalMetadata.map("additionalMetadata" -> _).map(Seq(_)) ++ + file.map("file" -> _).map(Seq(_)) + ).toSeq.flatten) + + _executeRequest[Unit, ApiResponse]( + method = "POST", + path = s"/pet/${petId}/uploadImage", + body = None, + formParameters = formParameters, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, ApiResponse]("ApiResponse", r) + } + } + +} + + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala new file mode 100644 index 000000000000..68d325f20183 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala @@ -0,0 +1,118 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.apis + +import cats.effect.Concurrent +import io.circe.Encoder +import org.http4s.Uri +import org.http4s.client.Client as Http4sClient +import org.openapitools.client.models.Order +import org.openapitools.client.models.* + +trait StoreApiEndpoints[F[*]] { + + def deleteOrder(orderId: String): F[Unit] + def getInventory()(implicit auth: _Authorization.ApiKey): F[Map[String, Int]] + def getOrderById(orderId: Long): F[Order] + def placeOrder(order: Order): F[Order] + +} + +class StoreApiEndpointsImpl[F[*]: Concurrent]( + override val baseUrl: Uri, + defaultHeaders: Seq[(String, String)] = Nil, + httpClient: Http4sClient[F] +) extends BaseClient[F](baseUrl, defaultHeaders, httpClient) with StoreApiEndpoints[F] { + + import JsonSupports.* + import io.circe.syntax.EncoderOps + import cats.implicits.toFlatMapOps + + override def deleteOrder(orderId: String): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Unit, Unit]( + method = "DELETE", + path = s"/store/order/${orderId}", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 404 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def getInventory()(implicit auth: _Authorization.ApiKey): F[Map[String, Int]] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Unit, Map[String, Int]]( + method = "GET", + path = s"/store/inventory", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + + case r if r.status.code == 200 => parseJson[F, Map[String, Int]]("Map[String, Int]", r) + } + } + + override def getOrderById(orderId: Long): F[Order] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Unit, Order]( + method = "GET", + path = s"/store/order/${orderId}", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, Order]("Order", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 404 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def placeOrder(order: Order): F[Order] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Order, Order]( + method = "POST", + path = s"/store/order", + body = Some(order), + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, Order]("Order", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + +} + + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala new file mode 100644 index 000000000000..349e7ca3195e --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala @@ -0,0 +1,197 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.apis + +import cats.effect.Concurrent +import io.circe.Encoder +import org.http4s.Uri +import org.http4s.client.Client as Http4sClient +import java.time.Instant +import scala.collection.immutable.Seq +import org.openapitools.client.models.User +import org.openapitools.client.models.* + +trait UserApiEndpoints[F[*]] { + + def createUser(user: User)(implicit auth: _Authorization.ApiKey): F[Unit] + def createUsersWithArrayInput(user: Seq[User])(implicit auth: _Authorization.ApiKey): F[Unit] + def createUsersWithListInput(user: Seq[User])(implicit auth: _Authorization.ApiKey): F[Unit] + def deleteUser(username: String)(implicit auth: _Authorization.ApiKey): F[Unit] + def getUserByName(username: String): F[User] + def loginUser(username: String, password: String): F[String] + def logoutUser()(implicit auth: _Authorization.ApiKey): F[Unit] + def updateUser(username: String, user: User)(implicit auth: _Authorization.ApiKey): F[Unit] + +} + +class UserApiEndpointsImpl[F[*]: Concurrent]( + override val baseUrl: Uri, + defaultHeaders: Seq[(String, String)] = Nil, + httpClient: Http4sClient[F] +) extends BaseClient[F](baseUrl, defaultHeaders, httpClient) with UserApiEndpoints[F] { + + import JsonSupports.* + import io.circe.syntax.EncoderOps + import cats.implicits.toFlatMapOps + + override def createUser(user: User)(implicit auth: _Authorization.ApiKey): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[User, Unit]( + method = "POST", + path = s"/user", + body = Some(user), + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + r => Concurrent[F].pure(()) + } + } + + override def createUsersWithArrayInput(user: Seq[User])(implicit auth: _Authorization.ApiKey): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Seq[User], Unit]( + method = "POST", + path = s"/user/createWithArray", + body = Some(user), + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + r => Concurrent[F].pure(()) + } + } + + override def createUsersWithListInput(user: Seq[User])(implicit auth: _Authorization.ApiKey): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Seq[User], Unit]( + method = "POST", + path = s"/user/createWithList", + body = Some(user), + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + r => Concurrent[F].pure(()) + } + } + + override def deleteUser(username: String)(implicit auth: _Authorization.ApiKey): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Unit, Unit]( + method = "DELETE", + path = s"/user/${username}", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 404 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def getUserByName(username: String): F[User] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Unit, User]( + method = "GET", + path = s"/user/${username}", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, User]("User", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 404 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def loginUser(username: String, password: String): F[String] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + val queryParameters = ( + Some(Seq("username" -> username)) ++ + Some(Seq("password" -> password)) + ).toSeq.flatten + + _executeRequest[Unit, String]( + method = "GET", + path = s"/user/login", + body = None, + formParameters = None, + queryParameters = queryParameters, + requestHeaders = requestHeaders, + auth = None) { + + case r if r.status.code == 200 => parseJson[F, String]("String", r) + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + + override def logoutUser()(implicit auth: _Authorization.ApiKey): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[Unit, Unit]( + method = "GET", + path = s"/user/logout", + body = None, + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + r => Concurrent[F].pure(()) + } + } + + override def updateUser(username: String, user: User)(implicit auth: _Authorization.ApiKey): F[Unit] = { + val requestHeaders = Seq( + Some("Content-Type" -> "application/json") + ).flatten + + _executeRequest[User, Unit]( + method = "PUT", + path = s"/user/${username}", + body = Some(user), + formParameters = None, + queryParameters = Nil, + requestHeaders = requestHeaders, + auth = Some(auth)) { + + case r if r.status.code == 400 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + case r if r.status.code == 404 => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)) + } + } + +} + + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala new file mode 100644 index 000000000000..d5bb4e10420a --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala @@ -0,0 +1,53 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + + +/** +* Describes the result of uploading an image resource +* @param code +* @param `type` +* @param message +*/ +case class ApiResponse( + code: Option[Int], + `type`: Option[String], + message: Option[String] +) + +object ApiResponse { + given encoderApiResponse: Encoder[ApiResponse] = Encoder.instance { t => + Json.fromFields{ + Seq( + t.code.map(v => "code" -> v.asJson), + t.`type`.map(v => "type" -> v.asJson), + t.message.map(v => "message" -> v.asJson) + ).flatten + } + } + given decoderApiResponse: Decoder[ApiResponse] = Decoder.instance { c => + for { + code <- c.downField("code").as[Option[Int]] + `type` <- c.downField("type").as[Option[String]] + message <- c.downField("message").as[Option[String]] + } yield ApiResponse( + code = code, + `type` = `type`, + message = message + ) + } +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala new file mode 100644 index 000000000000..01d466b27e5d --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala @@ -0,0 +1,48 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + + +/** +* A category for a pet +* @param id +* @param name +*/ +case class Category( + id: Option[Long], + name: Option[String] +) + +object Category { + given encoderCategory: Encoder[Category] = Encoder.instance { t => + Json.fromFields{ + Seq( + t.id.map(v => "id" -> v.asJson), + t.name.map(v => "name" -> v.asJson) + ).flatten + } + } + given decoderCategory: Decoder[Category] = Decoder.instance { c => + for { + id <- c.downField("id").as[Option[Long]] + name <- c.downField("name").as[Option[String]] + } yield Category( + id = id, + name = name + ) + } +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala new file mode 100644 index 000000000000..2b8b26d49bd4 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala @@ -0,0 +1,37 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + + +/** +* +*/ +enum FindPetsByStatusStatusParameterInner(val value: String) { + case Available extends FindPetsByStatusStatusParameterInner("available") + case Pending extends FindPetsByStatusStatusParameterInner("pending") + case Sold extends FindPetsByStatusStatusParameterInner("sold") +} + +object FindPetsByStatusStatusParameterInner { + given decoderFindPetsByStatusStatusParameterInner: Decoder[FindPetsByStatusStatusParameterInner] = + Decoder.decodeString.map(str => FindPetsByStatusStatusParameterInner.values.find(_.value == str) + .getOrElse(throw java.lang.IllegalArgumentException(s"FindPetsByStatusStatusParameterInner enum case not found: $str")) + ) + + given encoderFindPetsByStatusStatusParameterInner: Encoder[FindPetsByStatusStatusParameterInner] = + Encoder.encodeString.contramap[FindPetsByStatusStatusParameterInner](_.value) +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala new file mode 100644 index 000000000000..f3ce2870e8b5 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala @@ -0,0 +1,69 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + +import java.time.Instant + +/** +* An order for a pets from the pet store +* @param id +* @param petId +* @param quantity +* @param shipDate +* @param status +* @param complete +*/ +case class Order( + id: Option[Long], + petId: Option[Long], + quantity: Option[Int], + shipDate: Option[Instant], + status: Option[OrderStatus], + complete: Option[Boolean] +) + +object Order { + given encoderOrder: Encoder[Order] = Encoder.instance { t => + Json.fromFields{ + Seq( + t.id.map(v => "id" -> v.asJson), + t.petId.map(v => "petId" -> v.asJson), + t.quantity.map(v => "quantity" -> v.asJson), + t.shipDate.map(v => "shipDate" -> v.asJson), + t.status.map(v => "status" -> v.asJson), + t.complete.map(v => "complete" -> v.asJson) + ).flatten + } + } + given decoderOrder: Decoder[Order] = Decoder.instance { c => + for { + id <- c.downField("id").as[Option[Long]] + petId <- c.downField("petId").as[Option[Long]] + quantity <- c.downField("quantity").as[Option[Int]] + shipDate <- c.downField("shipDate").as[Option[Instant]] + status <- c.downField("status").as[Option[OrderStatus]] + complete <- c.downField("complete").as[Option[Boolean]] + } yield Order( + id = id, + petId = petId, + quantity = quantity, + shipDate = shipDate, + status = status, + complete = complete + ) + } +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala new file mode 100644 index 000000000000..7c49aada4d71 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala @@ -0,0 +1,37 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + + +/** +* Order Status +*/ +enum OrderStatus(val value: String) { + case Placed extends OrderStatus("placed") + case Approved extends OrderStatus("approved") + case Delivered extends OrderStatus("delivered") +} + +object OrderStatus { + given decoderOrderStatus: Decoder[OrderStatus] = + Decoder.decodeString.map(str => OrderStatus.values.find(_.value == str) + .getOrElse(throw java.lang.IllegalArgumentException(s"OrderStatus enum case not found: $str")) + ) + + given encoderOrderStatus: Encoder[OrderStatus] = + Encoder.encodeString.contramap[OrderStatus](_.value) +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala new file mode 100644 index 000000000000..37ee288cca11 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala @@ -0,0 +1,69 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + +import scala.collection.immutable.Seq + +/** +* A pet for sale in the pet store +* @param id +* @param category +* @param name +* @param photoUrls +* @param tags +* @param status +*/ +case class Pet( + id: Option[Long], + category: Option[Category], + name: String, + photoUrls: Seq[String], + tags: Option[Seq[Tag]], + status: Option[PetStatus] +) + +object Pet { + given encoderPet: Encoder[Pet] = Encoder.instance { t => + Json.fromFields{ + Seq( + t.id.map(v => "id" -> v.asJson), + t.category.map(v => "category" -> v.asJson), + Some("name" -> t.name.asJson), + Some("photoUrls" -> t.photoUrls.asJson), + t.tags.map(v => "tags" -> v.asJson), + t.status.map(v => "status" -> v.asJson) + ).flatten + } + } + given decoderPet: Decoder[Pet] = Decoder.instance { c => + for { + id <- c.downField("id").as[Option[Long]] + category <- c.downField("category").as[Option[Category]] + name <- c.downField("name").as[String] + photoUrls <- c.downField("photoUrls").as[Seq[String]] + tags <- c.downField("tags").as[Option[Seq[Tag]]] + status <- c.downField("status").as[Option[PetStatus]] + } yield Pet( + id = id, + category = category, + name = name, + photoUrls = photoUrls, + tags = tags, + status = status + ) + } +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala new file mode 100644 index 000000000000..a51bec519150 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala @@ -0,0 +1,37 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + + +/** +* pet status in the store +*/ +enum PetStatus(val value: String) { + case Available extends PetStatus("available") + case Pending extends PetStatus("pending") + case Sold extends PetStatus("sold") +} + +object PetStatus { + given decoderPetStatus: Decoder[PetStatus] = + Decoder.decodeString.map(str => PetStatus.values.find(_.value == str) + .getOrElse(throw java.lang.IllegalArgumentException(s"PetStatus enum case not found: $str")) + ) + + given encoderPetStatus: Encoder[PetStatus] = + Encoder.encodeString.contramap[PetStatus](_.value) +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala new file mode 100644 index 000000000000..204dc791c6fc --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala @@ -0,0 +1,48 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + + +/** +* A tag for a pet +* @param id +* @param name +*/ +case class Tag( + id: Option[Long], + name: Option[String] +) + +object Tag { + given encoderTag: Encoder[Tag] = Encoder.instance { t => + Json.fromFields{ + Seq( + t.id.map(v => "id" -> v.asJson), + t.name.map(v => "name" -> v.asJson) + ).flatten + } + } + given decoderTag: Decoder[Tag] = Decoder.instance { c => + for { + id <- c.downField("id").as[Option[Long]] + name <- c.downField("name").as[Option[String]] + } yield Tag( + id = id, + name = name + ) + } +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala new file mode 100644 index 000000000000..2e05b4faf8c1 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala @@ -0,0 +1,78 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.syntax.* +import io.circe.{ Decoder, Encoder } + + +/** +* A User who is purchasing from the pet store +* @param id +* @param username +* @param firstName +* @param lastName +* @param email +* @param password +* @param phone +* @param userStatus User Status +*/ +case class User( + id: Option[Long], + username: Option[String], + firstName: Option[String], + lastName: Option[String], + email: Option[String], + password: Option[String], + phone: Option[String], + userStatus: Option[Int] +) + +object User { + given encoderUser: Encoder[User] = Encoder.instance { t => + Json.fromFields{ + Seq( + t.id.map(v => "id" -> v.asJson), + t.username.map(v => "username" -> v.asJson), + t.firstName.map(v => "firstName" -> v.asJson), + t.lastName.map(v => "lastName" -> v.asJson), + t.email.map(v => "email" -> v.asJson), + t.password.map(v => "password" -> v.asJson), + t.phone.map(v => "phone" -> v.asJson), + t.userStatus.map(v => "userStatus" -> v.asJson) + ).flatten + } + } + given decoderUser: Decoder[User] = Decoder.instance { c => + for { + id <- c.downField("id").as[Option[Long]] + username <- c.downField("username").as[Option[String]] + firstName <- c.downField("firstName").as[Option[String]] + lastName <- c.downField("lastName").as[Option[String]] + email <- c.downField("email").as[Option[String]] + password <- c.downField("password").as[Option[String]] + phone <- c.downField("phone").as[Option[String]] + userStatus <- c.downField("userStatus").as[Option[Int]] + } yield User( + id = id, + username = username, + firstName = firstName, + lastName = lastName, + email = email, + password = password, + phone = phone, + userStatus = userStatus + ) + } +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.scala new file mode 100644 index 000000000000..65302b137253 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.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 + * Contact: team@openapitools.org + * + * 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.models + +sealed trait _Authorization extends _root_.scala.Product with _root_.scala.Serializable + +object _Authorization { + final case class Basic(username: String, password: Option[String] = None) extends _Authorization + final case class ApiKey(value: String) extends _Authorization + final case class Bearer(token: String) extends _Authorization +} diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala new file mode 100644 index 000000000000..8ca4ae307428 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala @@ -0,0 +1,43 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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.models + +import io.circe.* +import io.circe.Decoder.* +import io.circe.Encoder.* +import io.circe.syntax.* + +case class _FailedRequest(code: Int, message: String) extends Exception(s"Server return status code: $code; message: $message") + +object _FailedRequest { + + given encoderFailedRequest: Encoder[_FailedRequest] = Encoder.instance { t => + Json.fromFields{ + Seq( + "code" -> t.code.asJson, + "message" -> t.message.asJson + ) + } + } + + given decodeFailedRequest: Decoder[_FailedRequest] = Decoder.instance { c => + for { + code <- c.downField("code").as[Int] + message <- c.downField("message").as[String] + } yield _FailedRequest( + code = code, + message = message + ) + } + +} + diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala new file mode 100644 index 000000000000..42cc740ee438 --- /dev/null +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala @@ -0,0 +1,42 @@ +/** + * 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 + * Contact: team@openapitools.org + * + * 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 + +import io.circe.{Decoder, Encoder} + +package object models { + + given decodeUUID: Decoder[_root_.java.util.UUID] = + Decoder.decodeString.map(str => _root_.java.util.UUID.fromString(str)) + + given encodeUUID: Encoder[_root_.java.util.UUID] = + Encoder.encodeString.contramap[_root_.java.util.UUID](uuid => uuid.toString) + + given decodeInstant: Decoder[_root_.java.time.Instant] = + Decoder.decodeString.map(str => _root_.java.time.OffsetDateTime.parse(str).toInstant) + + given encodeInstant: Encoder[_root_.java.time.Instant] = + Encoder.encodeString.contramap[_root_.java.time.Instant](_.toString) + + given decodeLocalDate: Decoder[_root_.java.time.LocalDate] = + Decoder.decodeString.map(str => _root_.java.time.LocalDate.parse(str)) + + given encodeLocalDate: Encoder[_root_.java.time.LocalDate] = + Encoder.encodeString.contramap[_root_.java.time.LocalDate](_.toString) + + given decodeJson: Decoder[io.circe.Json] = + Decoder.decodeString.map(str => io.circe.Json.fromString(str)) + + given encodeJson: Encoder[io.circe.Json] = + Encoder.encodeString.contramap[io.circe.Json](_.toString) + +} \ No newline at end of file From f5e37912487e4290743b6970b065e4ff2c9bc7a3 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Mon, 23 Sep 2024 12:28:48 -0400 Subject: [PATCH 13/18] add doc --- docs/generators/scala-http4s.md | 272 ++++++++++++++++++ .../languages/ScalaHttp4sClientCodegen.java | 11 +- ...alaHttp4sClientCodegenOptionsProvider.java | 2 +- .../scala/ScalaHttp4sClientCodegenTest.java | 10 +- 4 files changed, 284 insertions(+), 11 deletions(-) create mode 100644 docs/generators/scala-http4s.md diff --git a/docs/generators/scala-http4s.md b/docs/generators/scala-http4s.md new file mode 100644 index 000000000000..2d8169f54a16 --- /dev/null +++ b/docs/generators/scala-http4s.md @@ -0,0 +1,272 @@ +--- +title: Documentation for the scala-http4s Generator +--- + +## METADATA + +| Property | Value | Notes | +| -------- | ----- | ----- | +| generator name | scala-http4s | 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-http4s client. | | + +## 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| +| packageName | main package for the generated classes, parent package for api package and model package | |null| +| projectName | project name / artifactId for for the generated project | |null| +| excludeSbt | exclude sbt from generation | |null| +| excludeApi | exclude apis from generation | |null| +| prependFormOrBodyParameters | Add form or body parameters to the beginning of the parameter list. | |false| +| 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| +| 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| +| 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 | +| ---------- | ------- | +|ArrayBuffer|scala.collection.mutable.ArrayBuffer| +|Date|java.util.Date| +|File|java.io.File| +|HashMap|scala.collection.immutable.HashMap| +|Instant|java.time.Instant| +|Json|io.circe.Json| +|LocalDate|java.time.LocalDate| +|LocalDateTime|java.time.LocalDateTime| +|LocalTime|java.time.LocalTime| +|Map|scala.collection.immutable.Map| +|OffsetDateTime|java.time.OffsetDateTime| +|Seq|scala.collection.immutable.Seq| +|Timestamp|java.sql.Timestamp| +|URI|java.net.URI| +|UUID|java.util.UUID| +|ZonedDateTime|java.time.ZonedDateTime| + + +## INSTANTIATION TYPES + +| Type/Alias | Instantiated By | +| ---------- | --------------- | +|array|Seq| +|list|List| +|map|Map| +|seq|Seq| +|set|Set| + + +## LANGUAGE PRIMITIVES + +
    +
  • Any
  • +
  • AnyRef
  • +
  • AnyVal
  • +
  • BigDecimal
  • +
  • Boolean
  • +
  • Double
  • +
  • Float
  • +
  • Int
  • +
  • Integer
  • +
  • Long
  • +
  • Object
  • +
  • String
  • +
+ +## RESERVED WORDS + +
    +
  • abstract
  • +
  • assert
  • +
  • break
  • +
  • byte
  • +
  • case
  • +
  • catch
  • +
  • char
  • +
  • class
  • +
  • const
  • +
  • continue
  • +
  • def
  • +
  • default
  • +
  • do
  • +
  • double
  • +
  • else
  • +
  • enum
  • +
  • extends
  • +
  • false
  • +
  • final
  • +
  • finally
  • +
  • float
  • +
  • for
  • +
  • forsome
  • +
  • goto
  • +
  • if
  • +
  • implements
  • +
  • implicit
  • +
  • import
  • +
  • instanceof
  • +
  • int
  • +
  • interface
  • +
  • lazy
  • +
  • long
  • +
  • match
  • +
  • native
  • +
  • new
  • +
  • null
  • +
  • object
  • +
  • override
  • +
  • package
  • +
  • private
  • +
  • protected
  • +
  • public
  • +
  • return
  • +
  • sealed
  • +
  • short
  • +
  • static
  • +
  • strictfp
  • +
  • super
  • +
  • switch
  • +
  • synchronized
  • +
  • this
  • +
  • throw
  • +
  • throws
  • +
  • trait
  • +
  • transient
  • +
  • true
  • +
  • try
  • +
  • type
  • +
  • val
  • +
  • var
  • +
  • void
  • +
  • volatile
  • +
  • while
  • +
  • with
  • +
  • yield
  • +
+ +## 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/ScalaHttp4sClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java index a6d1687bb44c..220c868932b0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java @@ -48,9 +48,9 @@ public class ScalaHttp4sClientCodegen extends AbstractScalaCodegen implements Co @Getter protected boolean excludeSbt = false; @Getter - protected boolean modelsOnly = false; + protected boolean excludeApi = false; protected static final String EXCLUDE_SBT = "excludeSbt"; - protected static final String MODELS_ONLY = "modelsOnly"; + protected static final String EXCLUDE_API = "excludeApi"; protected String sourceFolder = "src" + File.separator + "main" + File.separator + "scala"; protected String projectName = artifactId; @@ -278,12 +278,13 @@ public void processOpts() { supportingFiles.remove(new SupportingFile("build.sbt.mustache", "", "build.sbt")); supportingFiles.remove(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); } - if (additionalProperties.containsKey(MODELS_ONLY)) { - this.modelsOnly = convertPropertyToBoolean(MODELS_ONLY); + if (additionalProperties.containsKey(EXCLUDE_API)) { + this.excludeApi = convertPropertyToBoolean(EXCLUDE_API); } - if (!modelsOnly) { + if (!excludeApi) { supportingFiles.add(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); supportingFiles.add(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); + apiTemplateFiles.put("api.mustache", ".scala"); } else { supportingFiles.remove(new SupportingFile("baseClient.mustache", apisFileFolderRelative(), "BaseClient.scala")); supportingFiles.remove(new SupportingFile("jsonSupports.mustache", apisFileFolderRelative(), "JsonSupports.scala")); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java index 0703f409c420..dfaa57f503b9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java @@ -43,7 +43,7 @@ public Map createOptions() { .put("dateLibrary", DATE_LIBRARY) .put(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "true") .put("excludeSbt", "false") - .put("modelsOnly", "false") + .put("excludeApi", "false") .build(); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java index 3042ec6ad4ba..419f629819d9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scala/ScalaHttp4sClientCodegenTest.java @@ -117,13 +117,13 @@ public void allowExcludeSbtFiles() { } @Test - public void allowModelsOnly() { + public void allowExcludeApiFiles() { ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen(); - assertFalse(codegen.isModelsOnly()); - codegen.additionalProperties().put("modelsOnly", "true"); - assertEquals(codegen.additionalProperties().get("modelsOnly"), "true"); + assertFalse(codegen.isExcludeApi()); + codegen.additionalProperties().put("excludeApi", "true"); + assertEquals(codegen.additionalProperties().get("excludeApi"), "true"); codegen.processOpts(); - assertTrue(codegen.isModelsOnly()); + assertTrue(codegen.isExcludeApi()); } @Test From a0791ad24ca5e1754efc68c929b856ce0de3336a Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Thu, 26 Sep 2024 10:47:43 -0400 Subject: [PATCH 14/18] add new samle to .github/workflows/samples-scala.yaml --- .github/workflows/samples-scala.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/samples-scala.yaml b/.github/workflows/samples-scala.yaml index eb1a3ac43cff..5b937b0a42bb 100644 --- a/.github/workflows/samples-scala.yaml +++ b/.github/workflows/samples-scala.yaml @@ -23,6 +23,7 @@ jobs: - 'samples/client/petstore/java/okhttp-gson' - samples/client/petstore/scalaz - samples/client/petstore/scala-pekko + - samples/client/petstore/scala-http4s #- samples/client/petstore/scala-sttp # won't pass while the same tests in circleci pass # servers - samples/server/petstore/scala-lagom-server From 59930ccc7fdbb67ec81d810fe0a243bd880fa813 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Thu, 26 Sep 2024 15:55:22 -0400 Subject: [PATCH 15/18] update build.sbt template --- docs/generators/scala-http4s.md | 28 +++++++++---------- .../languages/ScalaHttp4sClientCodegen.java | 23 +++++++-------- .../resources/scala-http4s/build.sbt.mustache | 11 ++++---- ...alaHttp4sClientCodegenOptionsProvider.java | 4 +-- .../scala-http4s/.openapi-generator/FILES | 1 - .../client/petstore/scala-http4s/build.sbt | 11 ++++---- .../openapitools/client/apis/BaseClient.scala | 2 +- 7 files changed, 39 insertions(+), 41 deletions(-) diff --git a/docs/generators/scala-http4s.md b/docs/generators/scala-http4s.md index 2d8169f54a16..f4d7a7f648ef 100644 --- a/docs/generators/scala-http4s.md +++ b/docs/generators/scala-http4s.md @@ -16,20 +16,20 @@ title: Documentation for the scala-http4s Generator ## 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| -| packageName | main package for the generated classes, parent package for api package and model package | |null| -| projectName | project name / artifactId for for the generated project | |null| -| excludeSbt | exclude sbt from generation | |null| -| excludeApi | exclude apis from generation | |null| -| prependFormOrBodyParameters | Add form or body parameters to the beginning of the parameter list. | |false| -| 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| -| 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| -| 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| +| Option | Description | Values | Default | +|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------ | ------- | +| allowUnicodeIdentifiers | boolean, toggles whether unicode identifiers are allowed in names or not, default is false | |false| +| packageName | main package for the generated classes, parent package for api package and model package | |null| +| artifactId | project name / artifactId for for the generated project | |null| +| excludeSbt | exclude sbt from generation | |null| +| excludeApi | exclude apis from generation | |null| +| prependFormOrBodyParameters | Add form or body parameters to the beginning of the parameter list. | |false| +| 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| +| 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| +| 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 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java index 220c868932b0..6c11d384059c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaHttp4sClientCodegen.java @@ -39,7 +39,7 @@ public class ScalaHttp4sClientCodegen extends AbstractScalaCodegen implements CodegenConfig { private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sClientCodegen.class); - private String mainPackage = "org.openapitools.client"; + protected String packageName = "org.openapitools.client"; protected String groupId = "org.openapitools"; protected String artifactId = "scala-http4s-client"; protected String artifactVersion = "1.0.0"; @@ -52,7 +52,6 @@ public class ScalaHttp4sClientCodegen extends AbstractScalaCodegen implements Co protected static final String EXCLUDE_SBT = "excludeSbt"; protected static final String EXCLUDE_API = "excludeApi"; protected String sourceFolder = "src" + File.separator + "main" + File.separator + "scala"; - protected String projectName = artifactId; @Override public CodegenType getTag() { @@ -104,8 +103,8 @@ public ScalaHttp4sClientCodegen() { modelTemplateFiles.put("model.mustache", ".scala"); apiTemplateFiles.put("api.mustache", ".scala"); - setApiPackage(mainPackage+ ".apis"); - setModelPackage(mainPackage + ".models"); + setApiPackage(packageName + ".apis"); + setModelPackage(packageName + ".models"); setReservedWordsLowerCase( Arrays.asList( @@ -188,9 +187,11 @@ public ScalaHttp4sClientCodegen() { typeMapping.put("OffsetDateTime", "OffsetDateTime"); typeMapping.put("uuid", "UUID"); + additionalProperties.put(CodegenConstants.GROUP_ID, groupId); + additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); + additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); - additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName); additionalProperties.put("infoUrl", "http://org.openapitools"); additionalProperties.put("infoEmail", "team@openapitools.org"); additionalProperties.put("licenseInfo", "Apache 2.0"); @@ -246,20 +247,20 @@ public void processOpts() { } if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) { - mainPackage = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME); - setApiPackage(mainPackage + ".apis"); - setModelPackage(mainPackage + ".models"); + packageName = (String) additionalProperties.get(CodegenConstants.PACKAGE_NAME); + setApiPackage(packageName + ".apis"); + setModelPackage(packageName + ".models"); additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage()); additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage()); } else { - additionalProperties.put(CodegenConstants.PACKAGE_NAME, mainPackage); + additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName); } if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { //you can set your own source folder, i.e. target/scala-3.3.3/src_managed/main this.sourceFolder = (String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER); } - if (additionalProperties.containsKey(CodegenConstants.PROJECT_NAME)) { - this.projectName = (String) additionalProperties.get(CodegenConstants.PROJECT_NAME); + if (additionalProperties.containsKey(CodegenConstants.ARTIFACT_ID)) { + this.artifactId = (String) additionalProperties.get(CodegenConstants.ARTIFACT_ID); } additionalProperties.put("fnEnumEntry", new EnumEntryLambda()); diff --git a/modules/openapi-generator/src/main/resources/scala-http4s/build.sbt.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/build.sbt.mustache index f5ae72dd6898..af5c5a47f385 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s/build.sbt.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s/build.sbt.mustache @@ -1,5 +1,9 @@ scalaVersion := "3.3.3" +version := "{{artifactVersion}}" +name := "{{artifactId}}" +organization := "{{groupId}}" + val CirceVersion = "0.14.9" val Http4sVersion = "0.23.26" @@ -27,9 +31,4 @@ scalacOptions := Seq( "-source:future" ) -lazy val root = project - .in(file(".")) - .settings( - name := "{{projectName}}", - publishArtifact := false - ) \ No newline at end of file +Compile / publishArtifact := false \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java index dfaa57f503b9..bacc31603dab 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/ScalaHttp4sClientCodegenOptionsProvider.java @@ -7,7 +7,7 @@ import java.util.Map; public class ScalaHttp4sClientCodegenOptionsProvider implements OptionsProvider { - public static final String PROJECT_NAME_VALUE = "OpenAPI"; + public static final String ARTIFACT_ID_VALUE = "scala-http4s-client"; public static final String PACKAGE_NAME_VALUE = "org.openapitools.client"; public static final String MODEL_PACKAGE_VALUE = PACKAGE_NAME_VALUE + ".models"; public static final String API_PACKAGE_VALUE = PACKAGE_NAME_VALUE + ".apis"; @@ -29,7 +29,7 @@ public String getLanguage() { public Map createOptions() { ImmutableMap.Builder builder = new ImmutableMap.Builder(); return builder - .put(CodegenConstants.PROJECT_NAME, PROJECT_NAME_VALUE) + .put(CodegenConstants.ARTIFACT_ID, ARTIFACT_ID_VALUE) .put(CodegenConstants.PACKAGE_NAME, PACKAGE_NAME_VALUE) .put(CodegenConstants.MODEL_PACKAGE, MODEL_PACKAGE_VALUE) .put(CodegenConstants.API_PACKAGE, API_PACKAGE_VALUE) diff --git a/samples/client/petstore/scala-http4s/.openapi-generator/FILES b/samples/client/petstore/scala-http4s/.openapi-generator/FILES index de2e01d8246f..d9a14d04d59f 100644 --- a/samples/client/petstore/scala-http4s/.openapi-generator/FILES +++ b/samples/client/petstore/scala-http4s/.openapi-generator/FILES @@ -1,4 +1,3 @@ -.openapi-generator-ignore build.sbt project/build.properties src/main/scala/org/openapitools/client/apis/BaseClient.scala diff --git a/samples/client/petstore/scala-http4s/build.sbt b/samples/client/petstore/scala-http4s/build.sbt index 52c89e8f943d..c74943d3ae78 100644 --- a/samples/client/petstore/scala-http4s/build.sbt +++ b/samples/client/petstore/scala-http4s/build.sbt @@ -1,5 +1,9 @@ scalaVersion := "3.3.3" +version := "1.0.0" +name := "scala-http4s-client" +organization := "org.openapitools" + val CirceVersion = "0.14.9" val Http4sVersion = "0.23.26" @@ -27,9 +31,4 @@ scalacOptions := Seq( "-source:future" ) -lazy val root = project - .in(file(".")) - .settings( - name := "scala-http4s-client", - publishArtifact := false - ) \ No newline at end of file +Compile / publishArtifact := false \ No newline at end of file diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala index 7eb841fd099c..c225349695b2 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala @@ -27,7 +27,7 @@ abstract class BaseClient[F[*]: Concurrent]( httpClient: Http4sClient[F] ) { - val ApiVersion: String = "" + val ApiVersion: String = "1.0.0" private lazy val defaultApiHeaders = Seq( ("X-Apidoc-Version", ApiVersion) From 4c5c1caa26dae55d806065102c1bdb35cc30b079 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Fri, 27 Sep 2024 14:20:58 -0400 Subject: [PATCH 16/18] simply the baseclient --- .../src/main/resources/scala-http4s/baseClient.mustache | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache index 7b4bfd535bc9..15e0de1fb299 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache @@ -23,11 +23,9 @@ abstract class BaseClient[F[*]: Concurrent]( ("X-Apidoc-Version", ApiVersion) ) - protected def apiHeaders: Seq[(String, String)] = defaultApiHeaders - protected def modifyRequest(request: Request[F]): Request[F] = request - protected def _executeRequest[T, U]( + def _executeRequest[T, U]( method: String, path: String, body: Option[T] = None, @@ -44,7 +42,7 @@ abstract class BaseClient[F[*]: Concurrent]( val headers = Headers( ( - apiHeaders ++ + defaultApiHeaders ++ defaultHeaders ++ requestHeaders ).groupBy(_._1).map { case (k, l) => Header.Raw(CIString(k), l.last._2) }.toList From 85e633084bea29c1c988e8c143562f145d87dbec Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Tue, 1 Oct 2024 09:41:09 -0400 Subject: [PATCH 17/18] add None to optional field --- .../main/resources/scala-http4s/model.mustache | 2 +- .../openapitools/client/apis/BaseClient.scala | 6 ++---- .../openapitools/client/models/ApiResponse.scala | 6 +++--- .../openapitools/client/models/Category.scala | 4 ++-- .../org/openapitools/client/models/Order.scala | 12 ++++++------ .../org/openapitools/client/models/Pet.scala | 8 ++++---- .../org/openapitools/client/models/Tag.scala | 4 ++-- .../org/openapitools/client/models/User.scala | 16 ++++++++-------- 8 files changed, 28 insertions(+), 30 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache index 719f67f0fb17..a2510731f839 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache @@ -39,7 +39,7 @@ object {{classname}} { {{^isEnum}} case class {{classname}}( {{#vars}} - {{name}}: {{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}{{^-last}},{{/-last}} + {{name}}: {{^required}}Option[{{{dataType}}}] = None{{/required}}{{#required}}{{{dataType}}}{{/required}}{{^-last}},{{/-last}} {{/vars}} ) diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala index c225349695b2..4aec799461c6 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala @@ -33,11 +33,9 @@ abstract class BaseClient[F[*]: Concurrent]( ("X-Apidoc-Version", ApiVersion) ) - protected def apiHeaders: Seq[(String, String)] = defaultApiHeaders - protected def modifyRequest(request: Request[F]): Request[F] = request - protected def _executeRequest[T, U]( + def _executeRequest[T, U]( method: String, path: String, body: Option[T] = None, @@ -54,7 +52,7 @@ abstract class BaseClient[F[*]: Concurrent]( val headers = Headers( ( - apiHeaders ++ + defaultApiHeaders ++ defaultHeaders ++ requestHeaders ).groupBy(_._1).map { case (k, l) => Header.Raw(CIString(k), l.last._2) }.toList diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala index d5bb4e10420a..b9e0289bbe61 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala @@ -23,9 +23,9 @@ import io.circe.{ Decoder, Encoder } * @param message */ case class ApiResponse( - code: Option[Int], - `type`: Option[String], - message: Option[String] + code: Option[Int] = None, + `type`: Option[String] = None, + message: Option[String] = None ) object ApiResponse { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala index 01d466b27e5d..8e09f1c7d84f 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala @@ -22,8 +22,8 @@ import io.circe.{ Decoder, Encoder } * @param name */ case class Category( - id: Option[Long], - name: Option[String] + id: Option[Long] = None, + name: Option[String] = None ) object Category { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala index f3ce2870e8b5..22aca356042a 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala @@ -27,12 +27,12 @@ import java.time.Instant * @param complete */ case class Order( - id: Option[Long], - petId: Option[Long], - quantity: Option[Int], - shipDate: Option[Instant], - status: Option[OrderStatus], - complete: Option[Boolean] + id: Option[Long] = None, + petId: Option[Long] = None, + quantity: Option[Int] = None, + shipDate: Option[Instant] = None, + status: Option[OrderStatus] = None, + complete: Option[Boolean] = None ) object Order { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala index 37ee288cca11..5ad6536500fc 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala @@ -27,12 +27,12 @@ import scala.collection.immutable.Seq * @param status */ case class Pet( - id: Option[Long], - category: Option[Category], + id: Option[Long] = None, + category: Option[Category] = None, name: String, photoUrls: Seq[String], - tags: Option[Seq[Tag]], - status: Option[PetStatus] + tags: Option[Seq[Tag]] = None, + status: Option[PetStatus] = None ) object Pet { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala index 204dc791c6fc..52228889ebaa 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala @@ -22,8 +22,8 @@ import io.circe.{ Decoder, Encoder } * @param name */ case class Tag( - id: Option[Long], - name: Option[String] + id: Option[Long] = None, + name: Option[String] = None ) object Tag { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala index 2e05b4faf8c1..1497c781b1cc 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala @@ -28,14 +28,14 @@ import io.circe.{ Decoder, Encoder } * @param userStatus User Status */ case class User( - id: Option[Long], - username: Option[String], - firstName: Option[String], - lastName: Option[String], - email: Option[String], - password: Option[String], - phone: Option[String], - userStatus: Option[Int] + 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, + userStatus: Option[Int] = None ) object User { From 0f482ed5b84a3b24235133cffcee33f1054dfc42 Mon Sep 17 00:00:00 2001 From: Jenny Leahy Date: Wed, 2 Oct 2024 09:53:27 -0400 Subject: [PATCH 18/18] tweek auth model and format --- .../resources/scala-http4s/authModel.mustache | 6 +- .../scala-http4s/baseClient.mustache | 4 +- .../scala-http4s/licenseInfo.mustache | 21 ++++--- .../resources/scala-http4s/model.mustache | 11 ++-- .../openapitools/client/apis/BaseClient.scala | 25 ++++---- .../client/apis/JsonSupports.scala | 21 ++++--- .../org/openapitools/client/apis/PetApi.scala | 21 ++++--- .../openapitools/client/apis/StoreApi.scala | 21 ++++--- .../openapitools/client/apis/UserApi.scala | 21 ++++--- .../client/models/ApiResponse.scala | 40 ++++++------- .../openapitools/client/models/Category.scala | 36 ++++++----- ...FindPetsByStatusStatusParameterInner.scala | 28 ++++----- .../openapitools/client/models/Order.scala | 52 ++++++++-------- .../client/models/OrderStatus.scala | 28 ++++----- .../org/openapitools/client/models/Pet.scala | 52 ++++++++-------- .../client/models/PetStatus.scala | 28 ++++----- .../org/openapitools/client/models/Tag.scala | 36 ++++++----- .../org/openapitools/client/models/User.scala | 60 +++++++++---------- .../client/models/_Authorization.scala | 27 ++++----- .../client/models/_FailedRequest.scala | 21 ++++--- .../openapitools/client/models/package.scala | 21 ++++--- 21 files changed, 276 insertions(+), 304 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s/authModel.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/authModel.mustache index 2fb0c00725f1..4fbef837d271 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s/authModel.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s/authModel.mustache @@ -1,10 +1,10 @@ {{>licenseInfo}} package {{modelPackage}} -sealed trait _Authorization extends _root_.scala.Product with _root_.scala.Serializable +sealed trait _Authorization object _Authorization { final case class Basic(username: String, password: Option[String] = None) extends _Authorization - final case class ApiKey(value: String) extends _Authorization - final case class Bearer(token: String) extends _Authorization + final case class ApiKey(name: String, value: String) extends _Authorization + final case class Bearer(token: String) extends _Authorization } diff --git a/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache index 15e0de1fb299..aa325edc0a6e 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache @@ -62,8 +62,8 @@ abstract class BaseClient[F[*]: Concurrent]( request.putHeaders(Header.Raw(CIString("Authorization"), s"Basic $token")) case _Authorization.Bearer(token) => request.putHeaders(Header.Raw(CIString("Authorization"), s"Bearer $token")) - case _Authorization.ApiKey(apiKey) => - request.putHeaders(Header.Raw(CIString("api_key"), apiKey)) + case _Authorization.ApiKey(name, value) => + request.putHeaders(Header.Raw(CIString(name), value)) } val formBody = formParameters.map { x => UrlForm(x.groupBy(_._1).map{case (k, v) => (k, v.mkString(","))}.toSeq*) diff --git a/modules/openapi-generator/src/main/resources/scala-http4s/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/licenseInfo.mustache index 16ba21d203d0..b9d17f0cd440 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s/licenseInfo.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s/licenseInfo.mustache @@ -1,11 +1,10 @@ -/** - * {{{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 +/** {{{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-http4s/model.mustache b/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache index a2510731f839..912925158182 100644 --- a/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-http4s/model.mustache @@ -3,7 +3,7 @@ package {{modelPackage}} import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} {{#imports}} import {{import}} @@ -11,12 +11,11 @@ import {{import}} {{#models}} {{#model}} -/** -* {{{description}}} +/** {{{description}}} {{#vars}} -* @param {{name}} {{{description}}} + * @param {{name}} {{{description}}} {{/vars}} -*/ + */ {{#isEnum}} enum {{classname}}(val value: String) { {{#allowableValues}} @@ -39,7 +38,7 @@ object {{classname}} { {{^isEnum}} case class {{classname}}( {{#vars}} - {{name}}: {{^required}}Option[{{{dataType}}}] = None{{/required}}{{#required}}{{{dataType}}}{{/required}}{{^-last}},{{/-last}} + {{name}}: {{^required}}Option[{{{dataType}}}] = None{{/required}}{{#required}}{{{dataType}}}{{/required}}{{^-last}},{{/-last}} {{/vars}} ) diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala index 4aec799461c6..aafc08df2b37 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala @@ -1,14 +1,13 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.apis import cats.effect.Concurrent @@ -72,8 +71,8 @@ abstract class BaseClient[F[*]: Concurrent]( request.putHeaders(Header.Raw(CIString("Authorization"), s"Basic $token")) case _Authorization.Bearer(token) => request.putHeaders(Header.Raw(CIString("Authorization"), s"Bearer $token")) - case _Authorization.ApiKey(apiKey) => - request.putHeaders(Header.Raw(CIString("api_key"), apiKey)) + case _Authorization.ApiKey(name, value) => + request.putHeaders(Header.Raw(CIString(name), value)) } val formBody = formParameters.map { x => UrlForm(x.groupBy(_._1).map{case (k, v) => (k, v.mkString(","))}.toSeq*) diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala index 3ef9445133d5..70dd487ea314 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/JsonSupports.scala @@ -1,14 +1,13 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.apis import cats.effect.* diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala index a7bea389d96f..e562c0edfb3c 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/PetApi.scala @@ -1,14 +1,13 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.apis import cats.effect.Concurrent diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala index 68d325f20183..a0c33365cc0a 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/StoreApi.scala @@ -1,14 +1,13 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.apis import cats.effect.Concurrent diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala index 349e7ca3195e..749cb93d4b80 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/UserApi.scala @@ -1,14 +1,13 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.apis import cats.effect.Concurrent diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala index b9e0289bbe61..6d46c256b8ae 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/ApiResponse.scala @@ -1,31 +1,29 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} -/** -* Describes the result of uploading an image resource -* @param code -* @param `type` -* @param message -*/ +/** Describes the result of uploading an image resource + * @param code + * @param `type` + * @param message + */ case class ApiResponse( - code: Option[Int] = None, - `type`: Option[String] = None, - message: Option[String] = None + code: Option[Int] = None, + `type`: Option[String] = None, + message: Option[String] = None ) object ApiResponse { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala index 8e09f1c7d84f..79e91d6e1fe0 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Category.scala @@ -1,29 +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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} -/** -* A category for a pet -* @param id -* @param name -*/ +/** A category for a pet + * @param id + * @param name + */ case class Category( - id: Option[Long] = None, - name: Option[String] = None + id: Option[Long] = None, + name: Option[String] = None ) object Category { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala index 2b8b26d49bd4..e2534970a07e 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/FindPetsByStatusStatusParameterInner.scala @@ -1,24 +1,22 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} -/** -* -*/ +/** + */ enum FindPetsByStatusStatusParameterInner(val value: String) { case Available extends FindPetsByStatusStatusParameterInner("available") case Pending extends FindPetsByStatusStatusParameterInner("pending") diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala index 22aca356042a..a9649cc85cab 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Order.scala @@ -1,38 +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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} import java.time.Instant -/** -* An order for a pets from the pet store -* @param id -* @param petId -* @param quantity -* @param shipDate -* @param status -* @param complete -*/ +/** An order for a pets from the pet store + * @param id + * @param petId + * @param quantity + * @param shipDate + * @param status + * @param complete + */ case class Order( - id: Option[Long] = None, - petId: Option[Long] = None, - quantity: Option[Int] = None, - shipDate: Option[Instant] = None, - status: Option[OrderStatus] = None, - complete: Option[Boolean] = None + id: Option[Long] = None, + petId: Option[Long] = None, + quantity: Option[Int] = None, + shipDate: Option[Instant] = None, + status: Option[OrderStatus] = None, + complete: Option[Boolean] = None ) object Order { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala index 7c49aada4d71..96a360806ea3 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/OrderStatus.scala @@ -1,24 +1,22 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} -/** -* Order Status -*/ +/** Order Status + */ enum OrderStatus(val value: String) { case Placed extends OrderStatus("placed") case Approved extends OrderStatus("approved") diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala index 5ad6536500fc..2800dcbdb4bb 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Pet.scala @@ -1,38 +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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} import scala.collection.immutable.Seq -/** -* A pet for sale in the pet store -* @param id -* @param category -* @param name -* @param photoUrls -* @param tags -* @param status -*/ +/** A pet for sale in the pet store + * @param id + * @param category + * @param name + * @param photoUrls + * @param tags + * @param status + */ case class Pet( - id: Option[Long] = None, - category: Option[Category] = None, - name: String, - photoUrls: Seq[String], - tags: Option[Seq[Tag]] = None, - status: Option[PetStatus] = None + id: Option[Long] = None, + category: Option[Category] = None, + name: String, + photoUrls: Seq[String], + tags: Option[Seq[Tag]] = None, + status: Option[PetStatus] = None ) object Pet { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala index a51bec519150..5300aff9dc6d 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/PetStatus.scala @@ -1,24 +1,22 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} -/** -* pet status in the store -*/ +/** pet status in the store + */ enum PetStatus(val value: String) { case Available extends PetStatus("available") case Pending extends PetStatus("pending") diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala index 52228889ebaa..a6a7cd083c5d 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/Tag.scala @@ -1,29 +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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} -/** -* A tag for a pet -* @param id -* @param name -*/ +/** A tag for a pet + * @param id + * @param name + */ case class Tag( - id: Option[Long] = None, - name: Option[String] = None + id: Option[Long] = None, + name: Option[String] = None ) object Tag { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala index 1497c781b1cc..a1cedbc53f92 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/User.scala @@ -1,41 +1,39 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* import io.circe.syntax.* -import io.circe.{ Decoder, Encoder } +import io.circe.{Decoder, Encoder} -/** -* A User who is purchasing from the pet store -* @param id -* @param username -* @param firstName -* @param lastName -* @param email -* @param password -* @param phone -* @param userStatus User Status -*/ +/** A User who is purchasing from the pet store + * @param id + * @param username + * @param firstName + * @param lastName + * @param email + * @param password + * @param phone + * @param userStatus User Status + */ case class User( - id: Option[Long] = 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, - userStatus: Option[Int] = None + 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, + userStatus: Option[Int] = None ) object User { diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.scala index 65302b137253..9c711757a899 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_Authorization.scala @@ -1,20 +1,19 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models -sealed trait _Authorization extends _root_.scala.Product with _root_.scala.Serializable +sealed trait _Authorization object _Authorization { final case class Basic(username: String, password: Option[String] = None) extends _Authorization - final case class ApiKey(value: String) extends _Authorization - final case class Bearer(token: String) extends _Authorization + final case class ApiKey(name: String, value: String) extends _Authorization + final case class Bearer(token: String) extends _Authorization } diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala index 8ca4ae307428..e883a521dc2f 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/_FailedRequest.scala @@ -1,14 +1,13 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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.models import io.circe.* diff --git a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala index 42cc740ee438..b3a8d920c308 100644 --- a/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala +++ b/samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/models/package.scala @@ -1,14 +1,13 @@ -/** - * 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 - * Contact: team@openapitools.org - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ +/** 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 + * Contact: team@openapitools.org + * + * 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 import io.circe.{Decoder, Encoder}