[Scala][Client]Add Http4s scala3 client codegen (#19658)

* fix attemp

* use java.time.Instant

* fix client gen

* Tweeks

* header and form

* use scala 3 enum

* more tweeks

* add additional properties; add auth

* add form media type

* add modelsOnly

* add unit tests

* add petstore samples

* add doc

* add new samle to .github/workflows/samples-scala.yaml

* update build.sbt template

* simply the baseclient

* add None to optional field

* tweek auth model and format

---------

Co-authored-by: Jenny Leahy <jennyleahy@JENNYLEAHY.localdomain>
This commit is contained in:
Jenny G. L. 2024-10-05 05:00:19 -04:00 committed by GitHub
parent e5dee54797
commit 9791e6f537
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 3372 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -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|
| 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. |<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|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). |<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
| 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
<ul class="column-ul">
<li>Any</li>
<li>AnyRef</li>
<li>AnyVal</li>
<li>BigDecimal</li>
<li>Boolean</li>
<li>Double</li>
<li>Float</li>
<li>Int</li>
<li>Integer</li>
<li>Long</li>
<li>Object</li>
<li>String</li>
</ul>
## RESERVED WORDS
<ul class="column-ul">
<li>abstract</li>
<li>assert</li>
<li>break</li>
<li>byte</li>
<li>case</li>
<li>catch</li>
<li>char</li>
<li>class</li>
<li>const</li>
<li>continue</li>
<li>def</li>
<li>default</li>
<li>do</li>
<li>double</li>
<li>else</li>
<li>enum</li>
<li>extends</li>
<li>false</li>
<li>final</li>
<li>finally</li>
<li>float</li>
<li>for</li>
<li>forsome</li>
<li>goto</li>
<li>if</li>
<li>implements</li>
<li>implicit</li>
<li>import</li>
<li>instanceof</li>
<li>int</li>
<li>interface</li>
<li>lazy</li>
<li>long</li>
<li>match</li>
<li>native</li>
<li>new</li>
<li>null</li>
<li>object</li>
<li>override</li>
<li>package</li>
<li>private</li>
<li>protected</li>
<li>public</li>
<li>return</li>
<li>sealed</li>
<li>short</li>
<li>static</li>
<li>strictfp</li>
<li>super</li>
<li>switch</li>
<li>synchronized</li>
<li>this</li>
<li>throw</li>
<li>throws</li>
<li>trait</li>
<li>transient</li>
<li>true</li>
<li>try</li>
<li>type</li>
<li>val</li>
<li>var</li>
<li>void</li>
<li>volatile</li>
<li>while</li>
<li>with</li>
<li>yield</li>
</ul>
## 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

View File

@ -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 class CodegenOperation {
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 class CodegenOperation {
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 class CodegenOperation {
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 class CodegenOperation {
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,

View File

@ -0,0 +1,459 @@
/*
* 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.Operation;
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;
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.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ScalaHttp4sClientCodegen extends AbstractScalaCodegen implements CodegenConfig {
private final Logger LOGGER = LoggerFactory.getLogger(ScalaHttp4sClientCodegen.class);
protected String packageName = "org.openapitools.client";
protected String groupId = "org.openapitools";
protected String artifactId = "scala-http4s-client";
protected String artifactVersion = "1.0.0";
protected boolean registerNonStandardStatusCodes = true;
protected boolean removeOAuthSecurities = true;
@Getter
protected boolean excludeSbt = false;
@Getter
protected boolean excludeApi = false;
protected static final String EXCLUDE_SBT = "excludeSbt";
protected static final String EXCLUDE_API = "excludeApi";
protected String sourceFolder = "src" + File.separator + "main" + File.separator + "scala";
@Override
public CodegenType getTag() {
return CodegenType.CLIENT;
}
public String getName() {
return "scala-http4s";
}
public String getHelp() {
return "Generates a scala-http4s client.";
}
public ScalaHttp4sClientCodegen() {
super();
modifyFeatureSet(features -> features
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom))
.securityFeatures(EnumSet.of(
SecurityFeature.BasicAuth,
SecurityFeature.ApiKey,
SecurityFeature.BearerToken
))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling
)
.excludeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism,
SchemaSupportFeature.not
)
.excludeParameterFeatures(
ParameterFeature.Cookie,
ParameterFeature.FormMultipart
)
);
useOneOfInterfaces = true;
supportsMultipleInheritance = true;
supportsInheritance = true;
supportsMixins = true;
addOneOfInterfaceImports = true;
embeddedTemplateDir = templateDir = "scala-http4s";
modelTemplateFiles.put("model.mustache", ".scala");
apiTemplateFiles.put("api.mustache", ".scala");
setApiPackage(packageName + ".apis");
setModelPackage(packageName + ".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")
);
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");
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", "LocalDate");
typeMapping.put("date-time", "Instant");
typeMapping.put("offset-date-time", "OffsetDateTime");
typeMapping.put("file", "File");
typeMapping.put("array", "Seq");
typeMapping.put("list", "List");
typeMapping.put("map", "Map");
typeMapping.put("object", "Json");
typeMapping.put("binary", "File");
typeMapping.put("Date", "LocalDate");
typeMapping.put("DateTime", "Instant");
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("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");
importMapping = new HashMap<>();
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");
importMapping.put("HashMap", "scala.collection.immutable.HashMap");
importMapping.put("Seq", "scala.collection.immutable.Seq");
importMapping.put("ArrayBuffer", "scala.collection.mutable.ArrayBuffer");
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");
instantiationTypes.put("array", "Seq");
instantiationTypes.put("seq", "Seq");
instantiationTypes.put("list", "List");
instantiationTypes.put("map", "Map");
//this option allows inline enums to be separate own models
inlineSchemaOption.put("RESOLVE_INLINE_ENUMS", "true");
}
@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 use java8";
LOGGER.error(error);
throw new RuntimeException(error);
}
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
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, 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.ARTIFACT_ID)) {
this.artifactId = (String) additionalProperties.get(CodegenConstants.ARTIFACT_ID);
}
additionalProperties.put("fnEnumEntry", new EnumEntryLambda());
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"));
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"));
} else {
supportingFiles.remove(new SupportingFile("build.sbt.mustache", "", "build.sbt"));
supportingFiles.remove(new SupportingFile("project/build.properties.mustache", "project", "build.properties"));
}
if (additionalProperties.containsKey(EXCLUDE_API)) {
this.excludeApi = convertPropertyToBoolean(EXCLUDE_API);
}
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"));
apiTemplateFiles.remove("api.mustache");
}
}
@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 apisFileFolderRelative() {
return sourceFolder + File.separator + apiPackage().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<ModelMap> allModels) {
if (registerNonStandardStatusCodes) {
try {
OperationMap opsMap = objs.getOperations();
HashSet<Integer> 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<CodegenSecurity> fromSecurity(Map<String, SecurityScheme> schemes) {
final List<CodegenSecurity> 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 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<Server> 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);
}
@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 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("\"", "");
}
}

View File

@ -125,6 +125,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.ScalaHttp4sClientCodegen
org.openapitools.codegen.languages.ScalaHttp4sServerCodegen
org.openapitools.codegen.languages.ScalaLagomServerCodegen
org.openapitools.codegen.languages.ScalaPlayFrameworkServerCodegen

View File

@ -0,0 +1,60 @@
{{>licenseInfo}}
package {{apiPackage}}
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}}): F[{{>operationReturnType}}]
{{/operation}}
}
{{/operations}}
{{#operations}}
class {{classname}}EndpointsImpl[F[*]: Concurrent](
override val baseUrl: Uri,
defaultHeaders: Seq[(String, String)] = Nil,
httpClient: Http4sClient[F]
) extends BaseClient[F](baseUrl, defaultHeaders, httpClient) with {{classname}}Endpoints[F] {
import JsonSupports.*
import io.circe.syntax.EncoderOps
import cats.implicits.toFlatMapOps
{{#operation}}
override def {{operationId}}({{>methodParameters}}): F[{{>operationReturnType}}] = {
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}},
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}}
}
}
{{/operation}}
}
{{/operations}}

View File

@ -0,0 +1,10 @@
{{>licenseInfo}}
package {{modelPackage}}
sealed trait _Authorization
object _Authorization {
final case class Basic(username: String, password: Option[String] = None) extends _Authorization
final case class ApiKey(name: String, value: String) extends _Authorization
final case class Bearer(token: String) extends _Authorization
}

View File

@ -0,0 +1,80 @@
{{>licenseInfo}}
package {{apiPackage}}
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 {{modelPackage}}.*
abstract class BaseClient[F[*]: Concurrent](
val baseUrl: Uri,
defaultHeaders: Seq[(String, String)] = Nil,
httpClient: Http4sClient[F]
) {
val ApiVersion: String = "{{artifactVersion}}"
private lazy val defaultApiHeaders = Seq(
("X-Apidoc-Version", ApiVersion)
)
protected def modifyRequest(request: Request[F]): Request[F] = request
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(
(
defaultApiHeaders ++
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(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*)
}
import JsonSupports.*
val reqAndMaybeAuthAndBody =
if (formBody.nonEmpty) formBody.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity)
else body.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity)
httpClient.run(modifyRequest(reqAndMaybeAuthAndBody)).use(handler)
}
}

View File

@ -0,0 +1 @@
{{#hasBodyParam}}{{#allParams}}{{#isBodyParam}}{{dataType}}{{/isBodyParam}}{{/allParams}}{{/hasBodyParam}}{{^hasBodyParam}}Unit{{/hasBodyParam}}

View File

@ -0,0 +1,34 @@
scalaVersion := "3.3.3"
version := "{{artifactVersion}}"
name := "{{artifactId}}"
organization := "{{groupId}}"
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"
)
Compile / publishArtifact := false

View File

@ -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}}

View File

@ -0,0 +1,33 @@
{{>licenseInfo}}
package {{modelPackage}}
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
)
}
}

View File

@ -0,0 +1,4 @@
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

View File

@ -0,0 +1,29 @@
{{>licenseInfo}}
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 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")
)
}
}

View File

@ -0,0 +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.
*/

View File

@ -0,0 +1 @@
{{#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}}

View File

@ -0,0 +1,70 @@
{{>licenseInfo}}
package {{modelPackage}}
import io.circe.*
import io.circe.syntax.*
import io.circe.{Decoder, Encoder}
{{#imports}}
import {{import}}
{{/imports}}
{{#models}}
{{#model}}
/** {{{description}}}
{{#vars}}
* @param {{name}} {{{description}}}
{{/vars}}
*/
{{#isEnum}}
enum {{classname}}(val value: String) {
{{#allowableValues}}
{{#values}}
case {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}} extends {{classname}}("{{.}}")
{{/values}}
{{/allowableValues}}
}
object {{classname}} {
given decoder{{classname}}: Decoder[{{classname}}] =
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)
}
{{/isEnum}}
{{^isEnum}}
case class {{classname}}(
{{#vars}}
{{name}}: {{^required}}Option[{{{dataType}}}] = None{{/required}}{{#required}}{{{dataType}}}{{/required}}{{^-last}},{{/-last}}
{{/vars}}
)
object {{classname}} {
given encoder{{classname}}: Encoder[{{classname}}] = Encoder.instance { t =>
Json.fromFields{
Seq(
{{#vars}}
{{#required}}Some("{{baseName}}" -> t.{{name}}.asJson){{/required}}{{^required}}t.{{name}}.map(v => "{{baseName}}" -> v.asJson){{/required}}{{^-last}},{{/-last}}
{{/vars}}
).flatten
}
}
given decoder{{classname}}: Decoder[{{classname}}] = Decoder.instance { c =>
for {
{{#vars}}
{{name}} <- c.downField("{{baseName}}").as[{{^required}}Option[{{{dataType}}}]{{/required}}{{#required}}{{{dataType}}}{{/required}}]
{{/vars}}
} yield {{classname}}(
{{#vars}}
{{name}} = {{name}}{{^-last}},{{/-last}}
{{/vars}}
)
}
}
{{/isEnum}}
{{/model}}
{{/models}}

View File

@ -0,0 +1,32 @@
{{>licenseInfo}}
package {{packageName}}
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)
}

View File

@ -0,0 +1 @@
{{{returnType}}}{{^returnType}}Unit{{/returnType}}

View File

@ -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}}

View File

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

View File

@ -0,0 +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}}

View File

@ -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}}

View File

@ -0,0 +1,54 @@
package org.openapitools.codegen.options;
import org.openapitools.codegen.CodegenConstants;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.util.Map;
public class ScalaHttp4sClientCodegenOptionsProvider implements OptionsProvider {
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";
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 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";
}
@Override
public Map<String, String> createOptions() {
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
return builder
.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)
.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.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE)
.put(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS, PREPEND_FORM_OR_BODY_PARAMETERS_VALUE)
.put("dateLibrary", DATE_LIBRARY)
.put(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "true")
.put("excludeSbt", "false")
.put("excludeApi", "false")
.build();
}
@Override
public boolean isServer() {
return false;
}
}

View File

@ -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-http4s/petstore.yaml")
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
final ClientOptInput clientOptInput = configurator.toClientOptInput();
DefaultGenerator generator = getDefaultGenerator();
List<File> 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<Object>()
.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 allowExcludeApiFiles() {
ScalaHttp4sClientCodegen codegen = new ScalaHttp4sClientCodegen();
assertFalse(codegen.isExcludeApi());
codegen.additionalProperties().put("excludeApi", "true");
assertEquals(codegen.additionalProperties().get("excludeApi"), "true");
codegen.processOpts();
assertTrue(codegen.isExcludeApi());
}
@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");
}
}

View File

@ -0,0 +1,742 @@
openapi: 3.0.0
servers:
- url: 'https://petstore.swagger.io/v2'
info:
description: >-
This is a sample server Petstore server. For this sample, you can use the api key
`special-key` to test the authorization filters.
version: 1.0.0
title: OpenAPI Petstore
license:
name: Apache-2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
tags:
- name: pet
description: Everything about your Pets
- name: store
description: Access to Petstore orders
- name: user
description: Operations about user
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
description: ''
operationId: addPet
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
$ref: '#/components/requestBodies/Pet'
put:
tags:
- pet
summary: Update an existing pet
description: ''
operationId: updatePet
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid ID supplied
'404':
description: Pet not found
'405':
description: Validation exception
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
$ref: '#/components/requestBodies/Pet'
/pet/findByStatus:
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
style: form
explode: false
schema:
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
responses:
'200':
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid status value
security:
- petstore_auth:
- 'read:pets'
/pet/findByTags:
get:
tags:
- pet
summary: Finds Pets by tags
description: >-
Multiple tags can be provided with comma separated strings. Use tag1,
tag2, tag3 for testing.
operationId: findPetsByTags
parameters:
- name: tags
in: query
description: Tags to filter by
required: true
style: form
explode: false
schema:
type: array
items:
type: string
responses:
'200':
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid tag value
security:
- petstore_auth:
- 'read:pets'
deprecated: true
'/pet/{petId}':
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
schema:
type: integer
format: int64
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid ID supplied
'404':
description: Pet not found
security:
- api_key: []
post:
tags:
- pet
summary: Updates a pet in the store with form data
description: ''
operationId: updatePetWithForm
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
schema:
type: integer
format: int64
responses:
'200':
description: successful operation
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
name:
description: Updated name of the pet
type: string
status:
description: Updated status of the pet
type: string
delete:
tags:
- pet
summary: Deletes a pet
description: ''
operationId: deletePet
parameters:
- name: api_key
in: header
required: false
schema:
type: string
- name: petId
in: path
description: Pet id to delete
required: true
schema:
type: integer
format: int64
responses:
'400':
description: Invalid pet value
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
'/pet/{petId}/uploadImage':
post:
tags:
- pet
summary: uploads an image
description: ''
operationId: uploadFile
parameters:
- name: petId
in: path
description: ID of pet to update
required: true
schema:
type: integer
format: int64
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
additionalMetadata:
description: Additional data to pass to server
type: string
file:
description: file to upload
type: string
format: binary
/store/inventory:
get:
tags:
- store
summary: Returns pet inventories by status
description: Returns a map of status codes to quantities
operationId: getInventory
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: object
additionalProperties:
type: integer
format: int32
security:
- api_key: []
/store/order:
post:
tags:
- store
summary: Place an order for a pet
description: ''
operationId: placeOrder
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Order'
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid Order
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
description: order placed for purchasing the pet
required: true
'/store/order/{orderId}':
get:
tags:
- store
summary: Find purchase order by ID
description: >-
For valid response try integer IDs with value <= 5 or > 10. Other values
will generate exceptions
operationId: getOrderById
parameters:
- name: orderId
in: path
description: ID of pet that needs to be fetched
required: true
schema:
type: integer
format: int64
minimum: 1
maximum: 5
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Order'
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid ID supplied
'404':
description: Order not found
delete:
tags:
- store
summary: Delete purchase order by ID
description: >-
For valid response try integer IDs with value < 1000. Anything above
1000 or nonintegers will generate API errors
operationId: deleteOrder
parameters:
- name: orderId
in: path
description: ID of the order that needs to be deleted
required: true
schema:
type: string
responses:
'400':
description: Invalid ID supplied
'404':
description: Order not found
/user:
post:
tags:
- user
summary: Create user
description: This can only be done by the logged in user.
operationId: createUser
responses:
default:
description: successful operation
security:
- auth_cookie: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/User'
description: Created user object
required: true
/user/createWithArray:
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithArrayInput
responses:
default:
description: successful operation
security:
- auth_cookie: []
requestBody:
$ref: '#/components/requestBodies/UserArray'
/user/createWithList:
post:
tags:
- user
summary: Creates list of users with given input array
description: ''
operationId: createUsersWithListInput
responses:
default:
description: successful operation
security:
- auth_cookie: []
requestBody:
$ref: '#/components/requestBodies/UserArray'
/user/login:
get:
tags:
- user
summary: Logs user into the system
description: ''
operationId: loginUser
parameters:
- name: username
in: query
description: The user name for login
required: true
schema:
type: string
pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
- name: password
in: query
description: The password for login in clear text
required: true
schema:
type: string
responses:
'200':
description: successful operation
headers:
Set-Cookie:
description: >-
Cookie authentication key for use with the `auth_cookie`
apiKey authentication.
schema:
type: string
example: AUTH_KEY=abcde12345; Path=/; HttpOnly
X-Rate-Limit:
description: calls per hour allowed by the user
schema:
type: integer
format: int32
X-Expires-After:
description: date in UTC when token expires
schema:
type: string
format: date-time
content:
application/xml:
schema:
type: string
application/json:
schema:
type: string
'400':
description: Invalid username/password supplied
/user/logout:
get:
tags:
- user
summary: Logs out current logged in user session
description: ''
operationId: logoutUser
responses:
default:
description: successful operation
security:
- auth_cookie: []
'/user/{username}':
get:
tags:
- user
summary: Get user by user name
description: ''
operationId: getUserByName
parameters:
- name: username
in: path
description: The name that needs to be fetched. Use user1 for testing.
required: true
schema:
type: string
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/User'
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Invalid username supplied
'404':
description: User not found
put:
tags:
- user
summary: Updated user
description: This can only be done by the logged in user.
operationId: updateUser
parameters:
- name: username
in: path
description: name that need to be deleted
required: true
schema:
type: string
responses:
'400':
description: Invalid user supplied
'404':
description: User not found
security:
- auth_cookie: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/User'
description: Updated user object
required: true
delete:
tags:
- user
summary: Delete user
description: This can only be done by the logged in user.
operationId: deleteUser
parameters:
- name: username
in: path
description: The name that needs to be deleted
required: true
schema:
type: string
responses:
'400':
description: Invalid username supplied
'404':
description: User not found
security:
- auth_cookie: []
externalDocs:
description: Find out more about Swagger
url: 'http://swagger.io'
components:
requestBodies:
UserArray:
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
description: List of user object
required: true
Pet:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
description: Pet object that needs to be added to the store
required: true
securitySchemes:
petstore_auth:
type: oauth2
flows:
implicit:
authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog'
scopes:
'write:pets': modify pets in your account
'read:pets': read your pets
api_key:
type: apiKey
name: api_key
in: header
auth_cookie:
type: apiKey
name: AUTH_KEY
in: cookie
schemas:
Order:
title: Pet Order
description: An order for a pets from the pet store
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
default: false
xml:
name: Order
Category:
title: Pet category
description: A category for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
xml:
name: Category
User:
title: a User
description: A User who is purchasing from the pet store
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
format: int32
description: User Status
xml:
name: User
Tag:
title: Pet Tag
description: A tag for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
title: a Pet
description: A pet for sale in the pet store
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
wrapped: true
items:
$ref: '#/components/schemas/Tag'
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet
ApiResponse:
title: An uploaded response
description: Describes the result of uploading an image resource
type: object
properties:
code:
type: integer
format: int32
type:
type: string
message:
type: string

View File

@ -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

View File

@ -0,0 +1,19 @@
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

View File

@ -0,0 +1 @@
7.9.0-SNAPSHOT

View File

@ -0,0 +1,34 @@
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"
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"
)
Compile / publishArtifact := false

View File

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

View File

@ -0,0 +1,89 @@
/** 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 = "1.0.0"
private lazy val defaultApiHeaders = Seq(
("X-Apidoc-Version", ApiVersion)
)
protected def modifyRequest(request: Request[F]): Request[F] = request
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(
(
defaultApiHeaders ++
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(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*)
}
import JsonSupports.*
val reqAndMaybeAuthAndBody =
if (formBody.nonEmpty) formBody.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity)
else body.fold(reqAndMaybeAuth)(reqAndMaybeAuth.withEntity)
httpClient.run(modifyRequest(reqAndMaybeAuthAndBody)).use(handler)
}
}

View File

@ -0,0 +1,38 @@
/** 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")
)
}
}

View File

@ -0,0 +1,217 @@
/** 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)
}
}
}

View File

@ -0,0 +1,117 @@
/** 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))
}
}
}

View File

@ -0,0 +1,196 @@
/** 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))
}
}
}

View File

@ -0,0 +1,51 @@
/** OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
* 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] = None,
`type`: Option[String] = None,
message: Option[String] = None
)
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
)
}
}

View File

@ -0,0 +1,46 @@
/** 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] = None,
name: Option[String] = None
)
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
)
}
}

View File

@ -0,0 +1,35 @@
/** OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
* 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)
}

View File

@ -0,0 +1,67 @@
/** OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
* 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] = None,
petId: Option[Long] = None,
quantity: Option[Int] = None,
shipDate: Option[Instant] = None,
status: Option[OrderStatus] = None,
complete: Option[Boolean] = None
)
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
)
}
}

View File

@ -0,0 +1,35 @@
/** OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
* 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)
}

View File

@ -0,0 +1,67 @@
/** OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
* 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] = None,
category: Option[Category] = None,
name: String,
photoUrls: Seq[String],
tags: Option[Seq[Tag]] = None,
status: Option[PetStatus] = None
)
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
)
}
}

View File

@ -0,0 +1,35 @@
/** OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
* 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)
}

View File

@ -0,0 +1,46 @@
/** 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] = None,
name: Option[String] = None
)
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
)
}
}

View File

@ -0,0 +1,76 @@
/** 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] = 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 {
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
)
}
}

View File

@ -0,0 +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.
*/
package org.openapitools.client.models
sealed trait _Authorization
object _Authorization {
final case class Basic(username: String, password: Option[String] = None) extends _Authorization
final case class ApiKey(name: String, value: String) extends _Authorization
final case class Bearer(token: String) extends _Authorization
}

View File

@ -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.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
)
}
}

View File

@ -0,0 +1,41 @@
/** OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
* 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)
}