New generator for Scala3 + sttp4 + jsoniter-scala (#21908)

* started impl

* seems to work

* generated docs

* fix class name duplicates on case-insensitive filesystems

* wip

* added script to rebuild all projects

* dropped bash slop

* fixed directory structure

* ok, now it makes sense

* update sttp, serialize query params, fix enums serialization, fix not required files params

* fix compile errors after sttp upgrade, fix missing enum import in operations, fix file response. change serialization from Map to Seq to avoid deleting duplicate keys

* auth support

* fix enums entries, multipart support(partially), header, path and cookie serialization support

* support of option fields

* add samples, update workflow

* add new files

* fix

* remove tab

* remove tab

* update doc

* update header

* update readme

---------

Co-authored-by: Łukasz Biały <lukasz.marcin.bialy@gmail.com>
Co-authored-by: Kamil-Lontkowski <kamillont14@gmail.com>
This commit is contained in:
William Cheng
2025-09-06 13:36:44 +08:00
committed by GitHub
parent 2a556ff24e
commit 1d739f1598
43 changed files with 4237 additions and 1 deletions

View File

@@ -24,6 +24,7 @@ jobs:
- samples/client/petstore/scalaz
- samples/client/petstore/scala-pekko
- samples/client/petstore/scala-http4s
- samples/client/petstore/scala-sttp4-jsoniter
#- samples/client/petstore/scala-sttp
#- samples/client/petstore/scala-sttp-circe
# servers

View File

@@ -1099,6 +1099,7 @@ Here is a list of template creators:
* Scala (Akka): @cchafer
* Scala (sttp): @chameleon82
* Scala (sttp4): @flsh86
* Scala (scala-sttp4-jsoniter): @lbialy
* Scala (Pekko): @mickaelmagniez
* Scala (http4s): @JennyLeahy
* Swift: @tkqubo

View File

@@ -0,0 +1,6 @@
generatorName: scala-sttp4-jsoniter
outputDir: samples/client/petstore/scala-sttp4-jsoniter
inputSpec: modules/openapi-generator/src/test/resources/3_0/scala/scala-sttp4-jsoniter/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/scala-sttp4-jsoniter
additionalProperties:
hideGenerationTimestamp: "true"

View File

@@ -65,6 +65,7 @@ The following generators are available:
* [scala-pekko](generators/scala-pekko.md)
* [scala-sttp](generators/scala-sttp.md)
* [scala-sttp4 (beta)](generators/scala-sttp4.md)
* [scala-sttp4-jsoniter (beta)](generators/scala-sttp4-jsoniter.md)
* [scalaz](generators/scalaz.md)
* [swift-combine](generators/swift-combine.md)
* [swift5](generators/swift5.md)

View File

@@ -0,0 +1,260 @@
---
title: Documentation for the scala-sttp4-jsoniter Generator
---
## METADATA
| Property | Value | Notes |
| -------- | ----- | ----- |
| generator name | scala-sttp4-jsoniter | pass this to the generate command after -g |
| generator stability | BETA | |
| generator type | CLIENT | |
| generator language | Scala | |
| generator default templating engine | mustache | |
| helpTxt | Generates a Scala client library (beta) based on Sttp4 and Jsoniter-Scala. | |
## CONFIG OPTIONS
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|apiPackage|package for generated api classes| |null|
|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|
|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|<dl><dt>**false**</dt><dd>No changes to the enum's are made, this is the default option.</dd><dt>**true**</dt><dd>With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.</dd></dl>|false|
|jsoniterVersion|The version of jsoniter-scala library| |2.31.1|
|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|
|mainPackage|Top-level package name, which defines 'apiPackage', 'modelPackage', 'invokerPackage'| |org.openapitools.client|
|modelPackage|package for generated models| |null|
|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |camelCase|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|separateErrorChannel|Whether to return response as F[Either[ResponseError[ErrorType], ReturnType]]] or to flatten response's error raising them through enclosing monad (F[ReturnType]).| |true|
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|sourceFolder|source folder for generated code| |null|
|sttpClientVersion|The version of sttp client| |4.0.0-RC1|
## IMPORT MAPPING
| Type/Alias | Imports |
| ---------- | ------- |
|Array|java.util.List|
|ArrayList|java.util.ArrayList|
|BigDecimal|scala.math.BigDecimal|
|Date|java.util.Date|
|DateTime|org.joda.time.*|
|File|java.io.File|
|HashMap|java.util.HashMap|
|ListBuffer|scala.collection.mutable.ListBuffer|
|ListSet|scala.collection.immutable.ListSet|
|LocalDate|org.joda.time.*|
|LocalDateTime|org.joda.time.*|
|LocalTime|org.joda.time.*|
|Seq|scala.collection.immutable.Seq|
|Set|scala.collection.immutable.Set|
|Timestamp|java.sql.Timestamp|
|URI|java.net.URI|
|UUID|java.util.UUID|
|io.circe.Json|com.github.plokhotnyuk.jsoniter_scala.circe.JsoniterScalaCodec.*|
## INSTANTIATION TYPES
| Type/Alias | Instantiated By |
| ---------- | --------------- |
|array|ListBuffer|
|map|Map|
|set|Set|
## LANGUAGE PRIMITIVES
<ul class="column-ul">
<li>Any</li>
<li>Array</li>
<li>Boolean</li>
<li>Byte</li>
<li>Double</li>
<li>Float</li>
<li>Int</li>
<li>List</li>
<li>Long</li>
<li>Map</li>
<li>Object</li>
<li>Seq</li>
<li>String</li>
<li>boolean</li>
</ul>
## RESERVED WORDS
<ul class="column-ul">
<li>Either</li>
<li>Method</li>
<li>Request</li>
<li>abstract</li>
<li>case</li>
<li>catch</li>
<li>class</li>
<li>clone</li>
<li>def</li>
<li>do</li>
<li>else</li>
<li>enum</li>
<li>export</li>
<li>extends</li>
<li>false</li>
<li>final</li>
<li>finally</li>
<li>for</li>
<li>forSome</li>
<li>given</li>
<li>if</li>
<li>implicit</li>
<li>import</li>
<li>lazy</li>
<li>match</li>
<li>new</li>
<li>null</li>
<li>object</li>
<li>override</li>
<li>package</li>
<li>private</li>
<li>protected</li>
<li>return</li>
<li>sealed</li>
<li>super</li>
<li>then</li>
<li>this</li>
<li>throw</li>
<li>trait</li>
<li>true</li>
<li>try</li>
<li>type</li>
<li>using</li>
<li>val</li>
<li>var</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

@@ -1,6 +1,5 @@
/*
* 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.
@@ -529,6 +528,21 @@ public abstract class AbstractScalaCodegen extends DefaultCodegen {
if (identifier.matches("[a-zA-Z_$][\\w_$]+") && !isReservedWord(identifier)) {
return identifier;
}
// below code block only for scala-sttp4-jsoniter for backward copmatibility
if (this instanceof ScalaSttp4JsoniterClientCodegen) {
if (identifier.matches("[0-9]*")) {
return escapeReservedWord(identifier);
}
if (!capitalized || StringUtils.isNumeric(name)) {
// starts with a small letter, could be a keyword or a number
return escapeReservedWord(identifier);
} else {
// no keywords start with large letter
return identifier;
}
}
return escapeReservedWord(identifier);
}

View File

@@ -0,0 +1,730 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
*
* 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 org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.meta.features.*;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
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 static org.openapitools.codegen.languages.AbstractJavaCodegen.DATE_LIBRARY;
import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ScalaSttp4JsoniterClientCodegen extends AbstractScalaCodegen implements CodegenConfig {
private static final StringProperty STTP_CLIENT_VERSION = new StringProperty("sttpClientVersion",
"The version of " +
"sttp client",
"4.0.0-RC1");
private static final BooleanProperty USE_SEPARATE_ERROR_CHANNEL = new BooleanProperty("separateErrorChannel",
"Whether to return response as " +
"F[Either[ResponseError[ErrorType], ReturnType]]] or to flatten " +
"response's error raising them through enclosing monad (F[ReturnType]).",
true);
private static final StringProperty JSONITER_VERSION = new StringProperty("jsoniterVersion",
"The version of jsoniter-scala " +
"library",
"2.31.1");
public static final String DEFAULT_PACKAGE_NAME = "org.openapitools.client";
private static final PackageProperty PACKAGE_PROPERTY = new PackageProperty();
private static final List<Property<?>> properties = Arrays.asList(
STTP_CLIENT_VERSION, USE_SEPARATE_ERROR_CHANNEL, JSONITER_VERSION, PACKAGE_PROPERTY);
private static final String jsonClassBaseName = "Json";
private static final String jsonValueClass = "io.circe.Json";
private static final String jsonAstCodecImport = "com.github.plokhotnyuk.jsoniter_scala.circe.JsoniterScalaCodec.*";
private static final Set<String> NO_JSON_CODEC_TYPES = new HashSet<>(Arrays.asList(
"UUID", "URI", "URL", "File", "Path", jsonClassBaseName, jsonValueClass, "BigDecimal"
));
private final Logger LOGGER = LoggerFactory.getLogger(ScalaSttp4JsoniterClientCodegen.class);
protected String groupId = "org.openapitools";
protected String artifactId = "openapi-client";
protected String artifactVersion = "1.0.0";
protected boolean registerNonStandardStatusCodes = true;
protected boolean renderJavadoc = true;
protected boolean removeOAuthSecurities = true;
protected Map<String, String> jsonCodecNeedingTypes = new HashMap<>();
Map<String, ModelsMap> enumRefs = new HashMap<>();
private final Map<String, String> apiNameMappings = new HashMap<>();
private final Set<String> uniqueApiNames = new HashSet<>();
public ScalaSttp4JsoniterClientCodegen() {
super();
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.BETA)
.build();
modifyFeatureSet(features -> features
.includeDocumentationFeatures(DocumentationFeature.Readme)
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom))
.securityFeatures(EnumSet.of(
SecurityFeature.BasicAuth,
SecurityFeature.ApiKey,
SecurityFeature.BearerToken))
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects)
.excludeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism)
.includeClientModificationFeatures(
ClientModificationFeature.BasePath,
ClientModificationFeature.UserAgent));
outputFolder = "generated-code/scala-sttp4-jsoniter";
modelTemplateFiles.put("model.mustache", ".scala");
apiTemplateFiles.put("api.mustache", ".scala");
embeddedTemplateDir = templateDir = "scala-sttp4-jsoniter";
// Scala 3 reserved words
reservedWords.addAll(Arrays.asList("enum", "export", "given", "then", "using", "Request", "Method", "Either"));
importMapping.put(jsonValueClass, jsonAstCodecImport);
importMapping.put("BigDecimal", "scala.math.BigDecimal");
additionalProperties.put(CodegenConstants.GROUP_ID, groupId);
additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId);
additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion);
additionalProperties.put("jsonCodecNeedingTypes", jsonCodecNeedingTypes.entrySet());
if (renderJavadoc) {
additionalProperties.put("javadocRenderer", new JavadocLambda());
}
additionalProperties.put("fnCapitalize", new CapitalizeLambda());
additionalProperties.put("fnCamelize", new CamelizeLambda(false));
additionalProperties.put("fnEnumEntry", new EnumEntryLambda());
additionalProperties.put("fnCodecName", new CodecNameLambda());
additionalProperties.put("fnHandleDownload", new HandleDownloadLambda());
additionalProperties.put("fnEnumLeaf", new EnumLeafLambda());
// TODO: there is no specific sttp mapping. All Scala Type mappings should be in
// AbstractScala
typeMapping = new HashMap<>();
typeMapping.put("array", "Seq");
typeMapping.put("set", "Set");
typeMapping.put("boolean", "Boolean");
typeMapping.put("string", "String");
typeMapping.put("int", "Int");
typeMapping.put("integer", "Int");
typeMapping.put("long", "Long");
typeMapping.put("float", "Float");
typeMapping.put("byte", "Byte");
typeMapping.put("short", "Short");
typeMapping.put("char", "Char");
typeMapping.put("double", "Double");
typeMapping.put("file", "File");
typeMapping.put("binary", "File");
typeMapping.put("number", "Double");
typeMapping.put("decimal", "BigDecimal");
typeMapping.put("ByteArray", "Array[Byte]");
// actually, these two *are* jsoniter+circe AST specific
typeMapping.put("object", jsonValueClass);
typeMapping.put("AnyType", jsonValueClass);
instantiationTypes.put("array", "ListBuffer");
instantiationTypes.put("map", "Map");
// remove DATE_LIBRARY option, we don't need it
cliOptions.removeIf(option -> option.getOpt().equals(DATE_LIBRARY));
properties.stream()
.map(Property::toCliOptions)
.flatMap(Collection::stream)
.forEach(option -> cliOptions.add(option));
}
@Override
public void processOpts() {
super.processOpts();
properties.forEach(p -> p.updateAdditionalProperties(additionalProperties));
invokerPackage = PACKAGE_PROPERTY.getInvokerPackage(additionalProperties);
apiPackage = PACKAGE_PROPERTY.getApiPackage(additionalProperties);
modelPackage = PACKAGE_PROPERTY.getModelPackage(additionalProperties);
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt"));
final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator);
supportingFiles.add(new SupportingFile("jsonSupport.mustache", invokerFolder, "JsonSupport.scala"));
supportingFiles.add(new SupportingFile("additionalTypeSerializers.mustache", invokerFolder,
"AdditionalTypeSerializers.scala"));
supportingFiles.add(new SupportingFile("helpers.mustache", invokerFolder,
"Helpers.scala"));
supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties"));
}
@Override
public String getName() {
return "scala-sttp4-jsoniter";
}
@Override
public String getHelp() {
return "Generates a Scala client library (beta) based on Sttp4 and Jsoniter-Scala.";
}
@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.
StringBuffer buf = new StringBuffer(path.length());
Matcher matcher = Pattern.compile("[{](.*?)[}]").matcher(path);
while (matcher.find()) {
matcher.appendReplacement(buf, "\\${" + toParamName(matcher.group(0)).replace("`", "") + "PathParam}");
}
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 CodegenType getTag() {
return CodegenType.CLIENT;
}
@Override
public String escapeReservedWord(String name) {
if (this.reservedWordsMappings().containsKey(name)) {
return this.reservedWordsMappings().get(name);
}
return "`" + name + "`";
}
@Override
public String toApiName(String name) {
// first come, first served
// if a tag name is already mapped, use that mapping
if (apiNameMappings.containsKey(name)) {
return apiNameMappings.get(name);
}
String generatedApiName = super.toApiName(name);
String lowerCasedApiName = generatedApiName.toLowerCase(Locale.ROOT);
// check if the name is unique (case-insensitive)
// if it's unique, add it to the mappings and return the generated name
if (!uniqueApiNames.contains(lowerCasedApiName)) {
uniqueApiNames.add(lowerCasedApiName);
apiNameMappings.put(name, generatedApiName);
return generatedApiName;
} else {
// if the name is not unique, generate a new name with a unique suffix
int i = 0;
while (true) {
String nextGeneratedApiName = super.toApiName(name + i);
String lowerCasedNextGeneratedApiName = nextGeneratedApiName.toLowerCase(Locale.ROOT);
if (!uniqueApiNames.contains(lowerCasedNextGeneratedApiName)) {
uniqueApiNames.add(lowerCasedNextGeneratedApiName);
apiNameMappings.put(name, nextGeneratedApiName);
return nextGeneratedApiName;
}
i++;
}
}
}
@Override
public ModelsMap postProcessModels(ModelsMap objs) {
return objs;
}
/**
* Invoked by {@link DefaultGenerator} after all models have been
* post-processed,
* allowing for a last pass of codegen-specific model cleanup.
*
* @param objs Current state of codegen object model.
* @return An in-place modified state of the codegen object model.
*/
@Override
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
final Map<String, ModelsMap> processed = super.postProcessAllModels(objs);
postProcessUpdateImports(processed);
return processed;
}
/**
* Update/clean up model imports
* <p>
* append '._" if the import is a Enum class, otherwise
* remove model imports to avoid warnings for importing class in the same
* package in Scala
*
* @param models processed models to be further processed
*/
@SuppressWarnings("unchecked")
private void postProcessUpdateImports(final Map<String, ModelsMap> models) {
final String prefix = modelPackage() + ".";
enumRefs = getEnumRefs(models);
for (String openAPIName : models.keySet()) {
CodegenModel model = ModelUtils.getModelByName(openAPIName, models);
if (model == null) {
LOGGER.warn(
"Expected to retrieve model {} by name, but no model was found. Check your -Dmodels inclusions.",
openAPIName);
continue;
}
ModelsMap objs = models.get(openAPIName);
List<Map<String, String>> imports = objs.getImports();
if (imports == null || imports.isEmpty()) {
continue;
}
List<Map<String, String>> newImports = new ArrayList<>();
boolean foundJsonImport = false;
for (Map<String, String> anImport : imports) {
String importPath = anImport.get("import");
Map<String, String> item = new HashMap<>();
// remove any imports for io.circe.Json, it's a FQCN
// but on the first encounter, add the import for the
// jsoniter-scala circe AST codec as it will be necessary
// for all places where io.circe.Json is used as request body
// or response body
if (importPath.contains(jsonValueClass)) {
if (!foundJsonImport) {
foundJsonImport = true;
item.put("import", jsonAstCodecImport);
newImports.add(item);
}
continue;
}
if (importPath.startsWith(prefix)) {
if (isEnumClass(importPath, enumRefs)) {
item.put("import", importPath.concat(".*"));
newImports.add(item);
}
} else {
item.put("import", importPath);
newImports.add(item);
}
}
// reset imports
objs.setImports(newImports);
}
}
private Map<String, ModelsMap> getEnumRefs(final Map<String, ModelsMap> models) {
Map<String, ModelsMap> enums = new HashMap<>();
for (String key : models.keySet()) {
CodegenModel model = ModelUtils.getModelByName(key, models);
if (model != null && model.isEnum) {
ModelsMap objs = models.get(key);
enums.put(key, objs);
}
}
return enums;
}
private boolean isEnumClass(final String importPath, final Map<String, ModelsMap> enumModels) {
if (enumModels == null || enumModels.isEmpty()) {
return false;
}
for (ModelsMap objs : enumModels.values()) {
List<ModelMap> models = objs.getModels();
if (models == null || models.isEmpty()) {
continue;
}
for (final Map<String, Object> model : models) {
String enumImportPath = (String) model.get("importPath");
if (enumImportPath != null && enumImportPath.equals(importPath)) {
return true;
}
}
}
return false;
}
@Override
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
OperationMap ops = objs.getOperations();
for (CodegenOperation operation : ops.getOperation()) {
if (operation.returnType != null && !NO_JSON_CODEC_TYPES.contains(operation.returnType)) {
String identifier = formatIdentifier(operation.returnType, false) + "Codec";
String type = operation.returnType;
jsonCodecNeedingTypes.put(identifier, type);
}
if (operation.bodyParam != null && !NO_JSON_CODEC_TYPES.contains(operation.bodyParam.dataType)) {
String identifier = formatIdentifier(operation.bodyParam.dataType, false) + "Codec";
String type = operation.bodyParam.dataType;
jsonCodecNeedingTypes.put(identifier, type);
}
}
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);
}
}
// update imports for enum class
List<Map<String, String>> newImports = new ArrayList<>();
List<Map<String, String>> imports = objs.getImports();
if (imports != null && !imports.isEmpty()) {
for (Map<String, String> anImport : imports) {
String importPath = anImport.get("import");
Map<String, String> item = new HashMap<>();
if (isEnumClass(importPath, enumRefs)) {
item.put("import", importPath.concat(".*"));
Map<String, String> enumClassImport = new HashMap<>();
enumClassImport.put("import", importPath);
newImports.add(item);
newImports.add(enumClassImport);
} else {
item.put("import", importPath);
newImports.add(item);
}
}
}
objs.setImports(newImports);
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 toEnumName(CodegenProperty property) {
String identifier = formatIdentifier(property.baseName, true);
if (identifier.startsWith("`") && identifier.endsWith("`")) {
// is it numeric?
String unescaped = identifier.substring(1, identifier.length() - 1);
if (StringUtils.isNumeric(unescaped)) {
return identifier; // keep backticks
}
// remove backticks because there are no capitalized reserved words in Scala
return identifier.substring(1, identifier.length() - 1);
} else {
return identifier;
}
}
@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;
}
}
/**
* Update datatypeWithEnum for array container
*
* @param property Codegen property
*/
@Override
protected void updateDataTypeWithEnumForArray(CodegenProperty property) {
CodegenProperty baseItem = property.items;
while (baseItem != null && (Boolean.TRUE.equals(baseItem.isMap)
|| Boolean.TRUE.equals(baseItem.isArray))) {
baseItem = baseItem.items;
}
if (baseItem != null) {
// set datetypeWithEnum as only the inner type is enum
property.datatypeWithEnum = toEnumName(baseItem);
// naming the enum with respect to the language enum naming convention
// e.g. remove [], {} from array/map of enum
property.enumName = toEnumName(property);
property._enum = baseItem._enum;
updateCodegenPropertyEnum(property);
}
}
public static abstract class Property<T> {
final String name;
final String description;
final T defaultValue;
public Property(String name, String description, T defaultValue) {
this.name = name;
this.description = description;
this.defaultValue = defaultValue;
}
public abstract List<CliOption> toCliOptions();
public abstract void updateAdditionalProperties(Map<String, Object> additionalProperties);
public abstract T getValue(Map<String, Object> additionalProperties);
public void setValue(Map<String, Object> additionalProperties, T value) {
additionalProperties.put(name, value);
}
}
public static class StringProperty extends Property<String> {
public StringProperty(String name, String description, String defaultValue) {
super(name, description, defaultValue);
}
@Override
public List<CliOption> toCliOptions() {
return Collections.singletonList(CliOption.newString(name, description).defaultValue(defaultValue));
}
@Override
public void updateAdditionalProperties(Map<String, Object> additionalProperties) {
if (!additionalProperties.containsKey(name)) {
additionalProperties.put(name, defaultValue);
}
}
@Override
public String getValue(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(name, defaultValue).toString();
}
}
public static class BooleanProperty extends Property<Boolean> {
public BooleanProperty(String name, String description, Boolean defaultValue) {
super(name, description, defaultValue);
}
@Override
public List<CliOption> toCliOptions() {
return Collections.singletonList(CliOption.newBoolean(name, description, defaultValue));
}
@Override
public void updateAdditionalProperties(Map<String, Object> additionalProperties) {
Boolean value = getValue(additionalProperties);
additionalProperties.put(name, value);
}
@Override
public Boolean getValue(Map<String, Object> additionalProperties) {
return Boolean.valueOf(additionalProperties.getOrDefault(name, defaultValue.toString()).toString());
}
}
public static class PackageProperty extends StringProperty {
public PackageProperty() {
super("mainPackage", "Top-level package name, which defines 'apiPackage', 'modelPackage', " +
"'invokerPackage'", DEFAULT_PACKAGE_NAME);
}
@Override
public void updateAdditionalProperties(Map<String, Object> additionalProperties) {
String mainPackage = getValue(additionalProperties);
if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
String apiPackage = mainPackage + ".api";
additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage);
}
if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
String modelPackage = mainPackage + ".model";
additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage);
}
if (!additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
String invokerPackage = mainPackage + ".core";
additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
}
}
public String getApiPackage(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(CodegenConstants.API_PACKAGE, DEFAULT_PACKAGE_NAME + ".api")
.toString();
}
public String getModelPackage(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(CodegenConstants.MODEL_PACKAGE, DEFAULT_PACKAGE_NAME + ".model")
.toString();
}
public String getInvokerPackage(Map<String, Object> additionalProperties) {
return additionalProperties.getOrDefault(CodegenConstants.INVOKER_PACKAGE, DEFAULT_PACKAGE_NAME + ".core")
.toString();
}
}
private static class JavadocLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
final String[] lines = fragment.split("\\r?\\n");
final StringBuilder sb = new StringBuilder();
sb.append(" /**\n");
for (String line : lines) {
sb.append(" * ").append(line).append("\n");
}
sb.append(" */\n");
return sb.toString();
}
}
private static class CapitalizeLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
return StringUtils.capitalize(fragment);
}
}
private static class EnumEntryLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
if (fragment.isBlank()) {
return "NotPresent";
}
return "`" + fragment + "`";
}
}
private static class EnumLeafLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
if (fragment.isBlank()) {
return "NotPresent";
}
return fragment.replace("`", "");
}
}
private class CodecNameLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
// remove backticks because this is used as prefix for Codec generation
return formatIdentifier(fragment, false).replace("`", "") + "Codec";
}
}
private static class HandleDownloadLambda extends CustomLambda {
@Override
public String formatFragment(String fragment) {
if (fragment.equals("asJson[File]")) {
return "asFile(File.createTempFile(\"download\", \".tmp\")).mapWithMetadata((result, metadata) => result.left.map(errStr => ResponseException.DeserializationException(errStr, new Exception(errStr), metadata)))";
} else {
return fragment;
}
}
}
}

View File

@@ -138,6 +138,7 @@ org.openapitools.codegen.languages.ScalaLagomServerCodegen
org.openapitools.codegen.languages.ScalaPlayFrameworkServerCodegen
org.openapitools.codegen.languages.ScalaSttpClientCodegen
org.openapitools.codegen.languages.ScalaSttp4ClientCodegen
org.openapitools.codegen.languages.ScalaSttp4JsoniterClientCodegen
org.openapitools.codegen.languages.ScalazClientCodegen
org.openapitools.codegen.languages.SpringCodegen
org.openapitools.codegen.languages.StaticDocCodegen

View File

@@ -0,0 +1,115 @@
# {{artifactId}}
{{appName}}
- API version: {{appVersion}}
{{^hideGenerationTimestamp}}
- Build date: {{generatedDate}}
{{/hideGenerationTimestamp}}
- Generator version: {{generatorVersion}}
{{{appDescriptionWithNewLines}}}
{{#infoUrl}}
For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
{{/infoUrl}}
*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)*
## Requirements
Building the API client library requires:
1. Java 1.7+
2. Maven/Gradle/SBT
## Installation
To install the API client library to your local Maven repository, simply execute:
```shell
mvn clean install
```
To deploy it to a remote Maven repository instead, configure the settings of the repository and execute:
```shell
mvn clean deploy
```
Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information.
### Maven users
Add this dependency to your project's POM:
```xml
<dependency>
<groupId>{{{groupId}}}</groupId>
<artifactId>{{{artifactId}}}</artifactId>
<version>{{{artifactVersion}}}</version>
<scope>compile</scope>
</dependency>
```
### Gradle users
Add this dependency to your project's build file:
```groovy
compile "{{{groupId}}}:{{{artifactId}}}:{{{artifactVersion}}}"
```
### SBT users
```scala
libraryDependencies += "{{{groupId}}}" % "{{{artifactId}}}" % "{{{artifactVersion}}}"
```
## Getting Started
## Documentation for API Endpoints
All URIs are relative to *{{basePath}}*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | **{{operationId}}** | **{{httpMethod}}** {{path}} | {{summary}}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
## Documentation for Models
{{#models}}{{#model}} - [{{classname}}]({{modelDocPath}}{{classname}}.md)
{{/model}}{{/models}}
<a id="documentation-for-authorization"></a>
## Documentation for Authorization
{{^authMethods}}Endpoints do not require authorization.{{/authMethods}}
{{#hasAuthMethods}}Authentication schemes defined for the API:{{/hasAuthMethods}}
{{#authMethods}}
<a id="{{name}}"></a>
### {{name}}
{{#isApiKey}}- **Type**: API key
- **API key parameter name**: {{keyParamName}}
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
{{/isApiKey}}
{{#isBasicBasic}}- **Type**: HTTP basic authentication
{{/isBasicBasic}}
{{#isBasicBearer}}- **Type**: HTTP Bearer Token authentication{{#bearerFormat}} ({{{.}}}){{/bearerFormat}}
{{/isBasicBearer}}
{{#isHttpSignature}}- **Type**: HTTP signature authentication
{{/isHttpSignature}}
{{#isOAuth}}- **Type**: OAuth
- **Flow**: {{flow}}
- **Authorization URL**: {{authorizationUrl}}
- **Scopes**: {{^scopes}}N/A{{/scopes}}
{{#scopes}} - {{scope}}: {{description}}
{{/scopes}}
{{/isOAuth}}
{{/authMethods}}
## Author
{{#apiInfo}}{{#apis}}{{#-last}}{{infoEmail}}
{{/-last}}{{/apis}}{{/apiInfo}}

View File

@@ -0,0 +1,20 @@
package {{invokerPackage}}
import java.net.{URI, URISyntaxException}
import com.github.plokhotnyuk.jsoniter_scala.core.*
trait AdditionalTypeSerializers:
implicit final lazy val URICodec: JsonValueCodec[URI] = new JsonValueCodec[URI]:
def nullValue: URI = null
def decodeValue(in: JsonReader, default: URI): URI =
try
val uriString = in.readString(null)
if (uriString != null) new URI(uriString) else default
catch
case e: URISyntaxException =>
in.decodeError(s"Invalid URI syntax: ${e.getMessage}")
def encodeValue(uri: URI, out: JsonWriter): Unit =
if (uri != null) out.writeVal(uri.toString)
else out.writeNull()

View File

@@ -0,0 +1,74 @@
{{>licenseInfo}}
package {{package}}
{{#imports}}
import {{import}}
{{/imports}}
import {{invokerPackage}}.JsonSupport.{*, given}
import {{invokerPackage}}.FormSerializable
import {{invokerPackage}}.FormStyleFormat
import {{invokerPackage}}.HeaderSerializable
import {{invokerPackage}}.ApiKeyLocation
import {{invokerPackage}}.PathStyleFormat
import {{invokerPackage}}.PathSerializable
import {{invokerPackage}}.CookieSerializable
import {{invokerPackage}}.Helpers.*
import sttp.client4.jsoniter.*
import sttp.client4.*
import sttp.model.Method
{{#operations}}
object {{classname}}:
def apply(baseUrl: String = "{{{basePath}}}"): {{classname}}[{{invokerPackage}}.Authorization.NoAuthorization.type] = {{classname}}(baseUrl, {{invokerPackage}}.Authorization.NoAuthorization)
def withBasicAuth(baseUrl: String, username: String, password: String): {{classname}}[{{invokerPackage}}.Authorization.BasicAuth] =
{{classname}}(baseUrl, {{invokerPackage}}.Authorization.BasicAuth(username, password))
def withApiKeyAuth(baseUrl: String, apiKey: String): {{classname}}[{{invokerPackage}}.Authorization.ApiKey] =
{{classname}}(baseUrl, {{invokerPackage}}.Authorization.ApiKey(apiKey))
def withBearerTokenAuth(baseUrl: String, token: String): {{classname}}[{{invokerPackage}}.Authorization.BearerToken] =
{{classname}}(baseUrl, {{invokerPackage}}.Authorization.BearerToken(token))
case class {{classname}}[Auth <: {{invokerPackage}}.Authorization] private (baseUrl: String, authConfig: {{invokerPackage}}.Authorization):
def withBasicAuth(username: String, password: String): {{classname}}[{{invokerPackage}}.Authorization.BasicAuth] =
copy(authConfig = {{invokerPackage}}.Authorization.BasicAuth(username, password))
def withApiKeyAuth(apiKey: String): {{classname}}[{{invokerPackage}}.Authorization.ApiKey] =
copy(authConfig = {{invokerPackage}}.Authorization.ApiKey(apiKey))
def withNoAuth: {{classname}}[{{invokerPackage}}.Authorization.NoAuthorization.type] =
copy(authConfig = {{invokerPackage}}.Authorization.NoAuthorization)
def withBearerTokenAuth(token: String): {{classname}}[{{invokerPackage}}.Authorization.BearerToken] =
copy(authConfig = {{invokerPackage}}.Authorization.BearerToken(token))
{{#operation}}
{{#javadocRenderer}}
{{>javadoc}}
{{/javadocRenderer}}
def {{operationId}}{{>methodParameters}}: sttp.client4.Request[{{#separateErrorChannel}}Either[ResponseException[String], {{>operationReturnType}}]{{/separateErrorChannel}}{{^separateErrorChannel}}{{>operationReturnType}}{{/separateErrorChannel}}] =
{{#pathParams}} val {{#fnEnumLeaf}}{{paramName}}PathParam{{/fnEnumLeaf}} = PathSerializable.serialize("{{baseName}}", {{{paramName}}}{{#style}}, PathStyleFormat.{{style.toUpperCase}}{{/style}}{{^style}}, PathStyleFormat.SIMPLE{{/style}}, {{isExplode}})
{{/pathParams}}
val requestURL =
uri"$baseUrl{{{path}}}"{{#queryParams}}
.addParams(FormSerializable.serialize("{{baseName}}", {{{paramName}}}{{#style}}, FormStyleFormat.{{style.toUpperCase}}{{/style}}{{^style}}, FormStyleFormat.FORM{{/style}}, {{isExplode}}): _*){{/queryParams}}
basicRequest
.method(Method.{{httpMethod.toUpperCase}}, requestURL)
.contentType({{#consumes.0}}"{{{mediaType}}}"{{/consumes.0}}{{^consumes}}"application/json"{{/consumes}}){{#headerParams}}
.headers(HeaderSerializable.serialize("{{baseName}}", {{paramName}}, {{isExplode}})){{/headerParams}}{{#authMethods}}{{#cookieParams}}
.cookies(CookieSerializable.serialize("{{baseName}}", {{paramName}}, {{isExplode}})){{/cookieParams}}
.auth(authConfig{{#isApiKey}}, {{invokerPackage}}.ApiKeyLocation.{{#isKeyInQuery}}QUERY{{/isKeyInQuery}}{{#isKeyInHeader}}HEADER{{/isKeyInHeader}}{{#isKeyInCookie}}COOKIE{{/isKeyInCookie}}, "{{keyParamName}}"{{/isApiKey}}){{/authMethods}}{{#formParams.0}}{{^isMultipart}}
.body({{#formParams}}
{{>paramFormCreation}}{{^-last}} ++ {{/-last}}{{/formParams}},
"utf-8"
){{/isMultipart}}{{#isMultipart}}
.multipartBody(Seq({{#formParams}}
{{>paramMultipartCreation}}{{/formParams}}
).flatten){{/isMultipart}}{{/formParams.0}}{{#bodyParam}}
{{^isFile}}.body(asJson({{paramName}})){{/isFile}}{{#isFile}}.fileBody({{paramName}}){{/isFile}}{{/bodyParam}}
.response({{#separateErrorChannel}}{{^returnType}}asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))){{/returnType}}{{#fnHandleDownload}}{{#returnType}}asJson[{{>operationReturnType}}]{{/returnType}}{{/fnHandleDownload}}{{/separateErrorChannel}}{{^separateErrorChannel}}{{^returnType}}asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))).getRight{{/returnType}}{{#returnType}}asJson[{{>operationReturnType}}].getRight{{/returnType}}{{/separateErrorChannel}})
{{/operation}}
{{/operations}}
end {{classname}}

View File

@@ -0,0 +1,19 @@
version := "{{artifactVersion}}"
name := "{{artifactId}}"
organization := "{{groupId}}"
scalaVersion := "3.3.4"
libraryDependencies ++= Seq(
"com.softwaremill.sttp.client4" %% "core" % "{{sttpClientVersion}}",
"com.softwaremill.sttp.client4" %% "jsoniter" % "{{sttpClientVersion}}",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "{{jsoniterVersion}}",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "{{jsoniterVersion}}" % "compile-internal",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-circe" % "{{jsoniterVersion}}"
)
scalacOptions := Seq(
"-unchecked",
"-deprecation",
"-feature"
)

View File

@@ -0,0 +1,394 @@
{{>licenseInfo}}
package {{invokerPackage}}
import scala.deriving.*
import scala.compiletime.*
import java.io.File
import com.github.plokhotnyuk.jsoniter_scala.core.{JsonValueCodec, writeToString}
type Primitive = String | Short | Int | Long | Float | Double | BigDecimal |
Boolean
enum Authorization:
case NoAuthorization
case BasicAuth(username: String, password: String)
case ApiKey(apiKey: String)
case BearerToken(token: String)
enum ApiKeyLocation:
case HEADER
case COOKIE
case QUERY
case NOAPIKEY
enum FormStyleFormat:
case FORM
case SPACEDELIMITED
case PIPEDELIMITED
case DEEPOBJECT
enum PathStyleFormat:
case SIMPLE
case LABEL
case MATRIX
inline def allLabels[T <: Tuple]: List[String] =
constValueTuple[T].toList.asInstanceOf[List[String]]
private inline def checkFields[T <: Tuple]: Unit =
inline erasedValue[T] match {
case _: EmptyTuple => ()
case _: (t *: ts) =>
inline erasedValue[t] match
case _: Primitive => checkFields[ts]
case _: Option[Primitive] => checkFields[ts]
case _ => error("Cannot derive structure, structure must consist only of primitive fields")
}
private val flattenKeyVals: Primitive | Option[Primitive] => Option[Primitive] = {
case p: Primitive => Some(p)
case opt: Option[Primitive] => opt
}
trait FormSerializable[T]:
inline def serialize(
name: String,
obj: T,
inline format: FormStyleFormat = FormStyleFormat.FORM,
inline explode: Boolean = true
): Seq[(String, String)]
object FormSerializable:
inline def serialize[T](
name: String,
obj: T,
inline format: FormStyleFormat = FormStyleFormat.FORM,
inline explode: Boolean = true
): Seq[(String, String)] =
summonFrom {
case t: FormSerializable[T] => t.serialize(name, obj, format, explode)
case _ =>
inline obj match
case primitive: Primitive =>
serializePrimitive(name, primitive, format, explode)
case array: Seq[Primitive] =>
serializeArray(name, array, format, explode)
case optPrimitive: Option[Primitive] =>
optPrimitive.map(value => serializePrimitive(name, value, format, explode))
.getOrElse(Seq.empty[(String, String)])
case optArray: Option[Seq[Primitive]] =>
optArray.map(serializeArray(name, _, format, explode))
.getOrElse(Seq.empty[(String, String)])
case freeObj: Map[String, Primitive] =>
freeObj.map((key, value) => (key, value.toString)).toSeq
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, format, explode)
}.getOrElse(Seq.empty[(String, String)])
case mirror: Mirror.SumOf[t] => optObj.map(v => (name, writeToString(v)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))).toSeq
case obj =>
inline summonInline[Mirror.Of[T]] match
case _: Mirror.SumOf[T] =>
Seq((name, writeToString(obj)(summonInline[JsonValueCodec[T]])))
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes] // Stripe ma IDGAF bo używają deepObject np. tak lines[0][tax_amounts][0][amount] - mimo tego że spec na to nie pozwala
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, format, explode)
}
private inline def serializePrimitive(
paramName: String,
value: Primitive,
inline format: FormStyleFormat,
inline explode: Boolean
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
Seq(paramName -> value.toString) // for primitve values explode does not change anything
case FormStyleFormat.SPACEDELIMITED =>
error("FormStyleFormat.SpaceDelimited does not support primitive values")
case FormStyleFormat.PIPEDELIMITED =>
error("FormStyleFormat.PipeDelimited does not support primitive values")
case FormStyleFormat.DEEPOBJECT =>
error("FormStyleFormat.DeepObject does not support primitive values")
}
private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline format: FormStyleFormat,
inline explode: Boolean
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString(","))
case FormStyleFormat.SPACEDELIMITED =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString(" ")) // Sttp will encode space as +, from https://swagger.io/docs/specification/v3_0/serialization/#query-parameters it is not clear if it should be + or %20
case FormStyleFormat.PIPEDELIMITED =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString("|"))
case FormStyleFormat.DEEPOBJECT =>
error("FormStyleFormat.DeepObject does not support arrays")
}
private inline def serializeModel(
paramName: String,
keyValPairs: Seq[(String, Primitive)],
inline format: FormStyleFormat,
inline explode: Boolean
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
inline if explode then keyValPairs.map((key, value) => (key, value.toString))
else Seq(paramName -> keyValPairs.flatMap((key, value) => Seq(key, value.toString)).mkString(","))
case FormStyleFormat.SPACEDELIMITED =>
error("FormStyleFormat.SpaceDelimited does not support objects")
case FormStyleFormat.PIPEDELIMITED =>
error("FormStyleFormat.PipeDelimited does not support objects")
case FormStyleFormat.DEEPOBJECT =>
inline if explode then keyValPairs.map((key, value) => (s"$paramName[$key]", value.toString))
else error("FormStyleFormat.DeepObject does not support explode=false")
}
end FormSerializable
trait HeaderSerializable[T]:
inline def serialize(
name: String,
obj: T,
inline explode: Boolean = true
): Map[String, String]
object HeaderSerializable:
inline def serialize[T](
name: String,
obj: T,
inline explode: Boolean = true
): Map[String, String] =
summonFrom {
case t: HeaderSerializable[T] => t.serialize(name, obj, explode)
case _ => inline obj match
case primitive: Primitive => Map(name -> primitive.toString)
case optPrimitive: Option[Primitive] => optPrimitive.map(v => Map(name -> v.toString)).getOrElse(Map.empty[String, String])
case seqPrimitive: Seq[Primitive] => Map(name -> seqPrimitive.map(_.toString).mkString(","))
case optSeqPrimitive: Option[Seq[Primitive]] => optSeqPrimitive.map(v => Map(name -> v.map(_.toString).mkString(","))).getOrElse(Map.empty[String, String])
case mapPrimitive: Map[String, Primitive] => mapPrimitive.map((k, v) => (k, v.toString))
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get.toString))
inline if explode then
Map(name ->keyVals.map((k, v) => s"$k=$v").mkString(","))
else
Map(name -> keyVals.flatMap((k, v) => Seq(k, v)).mkString(","))
}.getOrElse(Map.empty[String, String])
case mirror: Mirror.SumOf[t] => optObj.map(v => Map(name -> writeToString(v)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))).getOrElse(Map.empty[String, String])
case obj: T =>
inline summonInline[Mirror.Of[T]] match
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get.toString))
inline if explode then
Map(name ->keyVals.map((k, v) => s"$k=$v").mkString(","))
else
Map(name -> keyVals.flatMap((k, v) => Seq(k, v)).mkString(","))
case mirror: Mirror.SumOf[T] => Map(name -> writeToString(obj)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))
}
end HeaderSerializable
trait PathSerializer[T]:
inline def serialize[T](name: String, obj: T, inline style: PathStyleFormat, inline explode: Boolean): String
object PathSerializable:
inline def serialize[T](name: String, obj: T, inline style: PathStyleFormat, inline explode: Boolean): String =
summonFrom {
case t: PathSerializer[T] => t.serialize(name, obj, style, explode)
case _ =>
inline obj match
case primitive: Primitive =>
serializePrimitive(name, primitive, style, explode)
case array: Seq[Primitive] =>
serializeArray(name, array, style, explode)
case optPrimitive: Option[Primitive] =>
optPrimitive.map(value => serializePrimitive(name, value, style, explode))
.getOrElse("")
case optArray: Option[Seq[Primitive]] =>
optArray.map(serializeArray(name, _, style, explode))
.getOrElse("")
case freeObj: Map[String, Primitive] =>
serializeModel(name, freeObj.map((key, value) => (key, value.toString)).toSeq, style, explode)
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, style, explode)
}.getOrElse("")
case mirror: Mirror.SumOf[t] => optObj.map(writeToString(_)(summonInline[JsonValueCodec[mirror.MirroredMonoType]])).getOrElse("")
case obj =>
inline summonInline[Mirror.Of[T]] match
case _: Mirror.SumOf[T] =>
writeToString(obj)(summonInline[JsonValueCodec[T]])
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, style, explode)
}
private inline def serializePrimitive(
paramName: String,
value: Primitive,
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE => value.toString
case PathStyleFormat.LABEL => s".${value.toString}"
case PathStyleFormat.MATRIX => s";$paramName=${value.toString}"
private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE => values.map(_.toString).mkString(",")
case PathStyleFormat.LABEL => inline if explode then values.map(_.toString).mkString(".", ".", "") else values.map(_.toString).mkString(".", ",", "")
case PathStyleFormat.MATRIX => inline if explode then values.map(v => s";$paramName=${v.toString}").mkString else s";$paramName=" + values.map(_.toString).mkString(",")
private inline def serializeModel(
paramName: String,
keyValPairs: Seq[(String, Primitive)],
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE =>
inline if explode then keyValPairs.map((k, v) => s"$k=${v.toString}").mkString(",")
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(",")
case PathStyleFormat.LABEL =>
inline if explode then keyValPairs.map((k, v) => s"$k=${v.toString}").mkString(".", ".", "")
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(".", ",", "")
case PathStyleFormat.MATRIX =>
inline if explode then keyValPairs.map((k, v) => s";$k=${v.toString}").mkString
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(s";$paramName=", ",", "")
end PathSerializable
trait CookieSerializable[T]:
inline def serialize(
name: String,
obj: T,
inline explode: Boolean = true
): Seq[(String, String)]
object CookieSerializable:
inline def serialize[T](
name: String,
obj: T,
inline explode: Boolean = true
): Seq[(String, String)] =
summonFrom {
case t: CookieSerializable[T] => t.serialize(name, obj, explode)
case _ =>
inline obj match
case primitive: Primitive =>
serializePrimitive(name, primitive, explode)
case array: Seq[Primitive] =>
serializeArray(name, array, explode)
case optPrimitive: Option[Primitive] =>
optPrimitive.map(value => serializePrimitive(name, value, explode))
.getOrElse(Seq.empty[(String, String)])
case optArray: Option[Seq[Primitive]] =>
optArray.map(serializeArray(name, _, explode))
.getOrElse(Seq.empty[(String, String)])
case freeObj: Map[String, Primitive] =>
serializeModel(name, freeObj.map((key, value) => (key, value.toString)).toSeq, explode)
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, explode)
}.getOrElse(Seq.empty[(String, String)])
case mirror: Mirror.SumOf[t] => optObj.map(v => (name, writeToString(v)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))).toSeq
case obj =>
inline summonInline[Mirror.Of[T]] match
case _: Mirror.SumOf[T] =>
Seq(name -> writeToString(obj)(summonInline[JsonValueCodec[T]]))
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, explode)
}
private inline def serializePrimitive(
paramName: String,
value: Primitive,
inline explode: Boolean
): Seq[(String, String)] = Seq(paramName -> value.toString)
private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline explode: Boolean
): Seq[(String, String)] =
inline if explode then error("Not supported")
else Seq(paramName -> values.map(_.toString).mkString(","))
private inline def serializeModel(
paramName: String,
keyValPairs: Seq[(String, Primitive)],
inline explode: Boolean
): Seq[(String, String)] =
inline if explode then error("Not supported")
else Seq(paramName -> keyValPairs.map((k, v) => s"$k,$v").mkString(","))
end CookieSerializable
object Helpers:
extension (request: sttp.client4.Request[?])
def fileBody(file: Option[File] | File): sttp.client4.Request[?] =
file match
case f: File => request.body(f)
case f: Option[File] => f.map(request.body(_)).getOrElse(request)
def auth(authConfig: Authorization, location: ApiKeyLocation = ApiKeyLocation.NOAPIKEY, keyParamName: String = ""): sttp.client4.Request[?] =
authConfig match
case Authorization.NoAuthorization => request
case Authorization.BasicAuth(username, password) => request.auth.basic(username, password)
case Authorization.BearerToken(token) => request.auth.bearer(token)
case Authorization.ApiKey(apiKey) =>location match
case ApiKeyLocation.HEADER => request.header(keyParamName, apiKey)
case ApiKeyLocation.COOKIE => request.cookie(keyParamName, apiKey)
case ApiKeyLocation.QUERY => request.copy(uri = request.uri.addParam(keyParamName, apiKey))
case ApiKeyLocation.NOAPIKEY => request // since it can be called multiple times in request (when there are for example 2 auth methods) we want to make this call idempotent

View File

@@ -0,0 +1,25 @@
{{#notes}}
{{{.}}}
{{/notes}}
Expected answers:
{{#responses}}
code {{code}} : {{{dataType}}} {{#message}}({{{.}}}){{/message}}
{{#headers}}
{{#-first}}
Headers :
{{/-first}}
{{{baseName}}} - {{{description}}}
{{/headers}}
{{/responses}}
{{#authMethods.0}}
Available security schemes:
{{#authMethods}}
{{name}} ({{type}})
{{/authMethods}}
{{/authMethods.0}}
{{#allParams}}
@param {{{paramName}}} {{{description}}}
{{/allParams}}

View File

@@ -0,0 +1,22 @@
{{>licenseInfo}}
package {{invokerPackage}}
{{#models.0}}
import {{modelPackage}}.*
{{/models.0}}
import java.time.*
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import com.github.plokhotnyuk.jsoniter_scala.core.*
import com.github.plokhotnyuk.jsoniter_scala.circe.JsoniterScalaCodec.*
object JsonSupport extends AdditionalTypeSerializers:
inline given CodecMakerConfig = CodecMakerConfig.withAllowRecursiveTypes(true)
inline def deriveJsonCodec[A](using inline config: CodecMakerConfig): JsonValueCodec[A] =
JsonCodecMaker.make(config)
{{#jsonCodecNeedingTypes}}
given {{key}}: JsonValueCodec[{{value}}] = deriveJsonCodec
{{/jsonCodecNeedingTypes}}

View File

@@ -0,0 +1,11 @@
/**
* {{{appName}}}
* {{{appDescription}}}
*
* {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}}
* {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@@ -0,0 +1 @@
{{#allParams.0}}({{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}{{#isContainer}}{{dataType}}{{/isContainer}}{{^isContainer}}Option[{{dataType}}]{{/isContainer}}{{/required}}{{^defaultValue}}{{^required}}{{^isContainer}} = scala.None{{/isContainer}}{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}){{/allParams.0}}{{#authMethods.0}}(using Auth <:< {{#authMethods}}{{#isApiKey}}{{invokerPackage}}.Authorization.ApiKey{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}}{{invokerPackage}}.Authorization.BasicAuth{{/isBasicBasic}}{{#isBasicBearer}}{{invokerPackage}}.Authorization.BearerToken{{/isBasicBearer}}{{/isBasic}}{{^-last}} | {{/-last}}{{/authMethods}}){{/authMethods.0}}

View File

@@ -0,0 +1,113 @@
{{>licenseInfo}}
package {{package}}
{{#imports}}
import {{import}}
{{/imports}}
import com.github.plokhotnyuk.jsoniter_scala.macros.named
{{#models}}
{{#model}}
{{#description}}
{{#javadocRenderer}}
{{#title}}
{{{.}}}
{{/title}}
{{{description}}}
{{/javadocRenderer}}
{{/description}}
{{^isEnum}}
case class {{classname}}(
{{#vars}}
{{#description}}
/* {{{.}}} */
{{/description}}
@named("{{baseName}}") {{{name}}}: {{^required}}Option[{{/required}}{{^isEnum}}{{dataType}}{{/isEnum}}{{#isEnum}}{{^isArray}}{{classname}}Enums.{{datatypeWithEnum}}{{/isArray}}{{#isArray}}Seq[{{classname}}Enums.{{datatypeWithEnum}}]{{/isArray}}{{/isEnum}}{{^required}}] = scala.None{{/required}}{{^-last}},{{/-last}}
{{/vars}}
)
{{/isEnum}}
{{#isEnum}}
enum {{classname}}:
{{#allowableValues}}
{{#values}}
case {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}}
{{/values}}
{{/allowableValues}}
object {{classname}}:
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import com.github.plokhotnyuk.jsoniter_scala.core.*
{{#isString}}
given {{#fnCodecName}}{{classname}}{{/fnCodecName}}: JsonValueCodec[{{classname}}] = JsonCodecMaker.make {
CodecMakerConfig{{#allowableValues}}
.withAdtLeafClassNameMapper { x =>
JsonCodecMaker.simpleClassName(x) match
{{#values}}
case "{{#fnEnumLeaf}}{{.}}{{/fnEnumLeaf}}" => "{{.}}"
{{/values}}
}{{/allowableValues}}
.withDiscriminatorFieldName(scala.None)
}
{{/isString}}
{{#isNumber}}
given {{#fnCodecName}}{{datatypeWithEnum}}{{/fnCodecName}}: JsonValueCodec[{{datatypeWithEnum}}] = new JsonValueCodec[{{datatypeWithEnum}}]:
import scala.util.{Try, Success, Failure}
override val nullValue: {{datatypeWithEnum}} = null
override def decodeValue(in: JsonReader, default: {{datatypeWithEnum}}): {{datatypeWithEnum}} =
val x = in.readByte()
Try { {{datatypeWithEnum}}.fromOrdinal(x) } match
case Success(v) => v
case Failure(_) => in.decodeError(s"unexpected number value: $x")
override def encodeValue(x: {{datatypeWithEnum}}, out: JsonWriter): Unit = out.writeVal(x.ordinal)
{{/isNumber}}
end {{classname}}
{{/isEnum}}
{{#hasEnums}}
object {{classname}}Enums:
{{#vars}}
{{#isEnum}}
enum {{datatypeWithEnum}}:
{{#_enum}}
case {{#fnEnumEntry}}{{.}}{{/fnEnumEntry}}
{{/_enum}}
object {{datatypeWithEnum}}:
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import com.github.plokhotnyuk.jsoniter_scala.core.*
{{#isString}}
given {{#fnCodecName}}{{datatypeWithEnum}}{{/fnCodecName}}: JsonValueCodec[{{datatypeWithEnum}}] = JsonCodecMaker.make {
CodecMakerConfig
.withAdtLeafClassNameMapper { x =>
JsonCodecMaker.simpleClassName(x) match
{{#_enum}}
case "{{#fnEnumLeaf}}{{.}}{{/fnEnumLeaf}}" => "{{.}}"
{{/_enum}}
}
.withDiscriminatorFieldName(scala.None)
}
{{/isString}}
{{#isNumber}}
given {{#fnCodecName}}{{datatypeWithEnum}}{{/fnCodecName}}: JsonValueCodec[{{datatypeWithEnum}}] = new JsonValueCodec[{{datatypeWithEnum}}]:
import scala.util.{Try, Success, Failure}
override val nullValue: {{datatypeWithEnum}} = null
override def decodeValue(in: JsonReader, default: {{datatypeWithEnum}}): {{datatypeWithEnum}} =
val x = in.readByte()
Try { {{datatypeWithEnum}}.fromOrdinal(x) } match
case Success(v) => v
case Failure(_) => in.decodeError(s"unexpected number value: $x")
override def encodeValue(x: {{datatypeWithEnum}}, out: JsonWriter): Unit = out.writeVal(x.ordinal)
{{/isNumber}}
{{/isEnum}}
{{/vars}}
end {{classname}}Enums
{{/hasEnums}}
{{/model}}
{{/models}}

View File

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

View File

@@ -0,0 +1 @@
FormSerializable.serialize("{{baseName}}", {{{paramName}}}{{#style}}, FormStyleFormat.{{style.toUpperCase}}{{/style}}{{^style}}, FormStyleFormat.FORM{{/style}}, {{isExplode}})

View File

@@ -0,0 +1,18 @@
{{#required}}
{{#isFile}}
Some(multipartFile("{{baseName}}", {{paramName}}))
{{/isFile}}
{{^isFile}}{{#isFormParam}}{{^isPrimitiveType}}
Some(multipart("{{baseName}}", FormSerializable.serialize("{{baseName}}", {{{paramName}}}{{#style}}, FormStyleFormat.{{style.toUpperCase}}{{/style}}{{^style}}, FormStyleFormat.FORM{{/style}}, {{isExplode}}))){{/isPrimitiveType}}{{/isFormParam}}{{#isPrimitiveType}}
Some(multipart("{{baseName}}", {{paramName}}.toString)){{/isPrimitiveType}}
{{/isFile}}
{{/required}}
{{^required}}
{{#isFile}}
{{paramName}}.map(multipartFile("{{baseName}}", _))
{{/isFile}}
{{^isFile}}{{#isFormParam}}{{^isPrimitiveType}}
{{paramName}}.map(value => multipart("{{baseName}}", FormSerializable.serialize("{{baseName}}", value{{#style}}, FormStyleFormat.{{style.toUpperCase}}{{/style}}{{^style}}, FormStyleFormat.FORM{{/style}}, {{isExplode}}))){{/isPrimitiveType}}{{/isFormParam}}{{#isPrimitiveType}}
{{paramName}}.map(value => multipart("{{baseName}}", value.toString)){{/isPrimitiveType}}
{{/isFile}}
{{/required}}{{^-last}},{{/-last}}

View File

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

View File

@@ -0,0 +1 @@
{{#isDefault}}Success{{/isDefault}}{{^isDefault}}Error{{/isDefault}}

View File

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

237
rebuild.sc Executable file
View File

@@ -0,0 +1,237 @@
#!/usr/bin/env scala
//> using toolkit 0.6.0
//> using scala 3.3.1
import os.*
object Debug:
var enabled = false
enum Command:
case Clean, Generate, GenerateStrict, GenerateOldGen, Strict, Exit
enum Target:
case Specific(name: String)
case All
def say(message: String)(using pwd: Path) =
val command = List("say", message)
if Debug.enabled then println(s"Running command: ${command.mkString(" ")}")
os.proc(command).call(stdout = os.Inherit, stderr = os.Inherit)
case class Project(
project: String,
projectName: String,
projectGroupId: String,
projectArtifactId: String,
projectMainPackage: String,
projectVersion: String,
generatorName: String,
skipValidate: Boolean,
additionalProps: String = "",
schemaMappings: String = "",
typeMappings: String = "",
importMappings: String = ""
)
val projects = Map(
"mattermost" -> Project(
project = "mattermost-scala",
projectName = "mattermost-scala",
projectGroupId = "ma.chinespirit",
projectArtifactId = "mattermost-scala",
projectMainPackage = "ma.chinespirit.mm",
projectVersion = "1.0.0-SNAPSHOT",
generatorName = "scala-sttp4-jsoniter",
skipValidate = false
),
"kubernetes" -> Project(
project = "kubeapi-scala",
projectName = "kubeapi-scala",
projectGroupId = "ma.chinespirit",
projectArtifactId = "kubeapi-scala",
projectMainPackage = "ma.chinespirit.kube",
projectVersion = "1.0.0-SNAPSHOT",
generatorName = "scala-sttp4-jsoniter",
skipValidate = false,
schemaMappings = "",
typeMappings = "",
importMappings = ""
),
"stripe" -> Project(
project = "stripe-scala",
projectName = "stripe-scala",
projectGroupId = "ma.chinespirit",
projectArtifactId = "stripe-scala",
projectMainPackage = "ma.chinespirit.stripe",
projectVersion = "1.0.0-SNAPSHOT",
generatorName = "scala-sttp4-jsoniter",
skipValidate = false
),
"github" -> Project(
project = "github-scala",
projectName = "github-scala",
projectGroupId = "ma.chinespirit",
projectArtifactId = "github-scala",
projectMainPackage = "ma.chinespirit.github",
projectVersion = "1.0.0-SNAPSHOT",
generatorName = "scala-sttp4-jsoniter",
skipValidate = false
),
"spotify" -> Project(
project = "spotify-scala",
projectName = "spotify-scala",
projectGroupId = "ma.chinespirit",
projectArtifactId = "spotify-scala",
projectMainPackage = "ma.chinespirit.spotify",
projectVersion = "1.0.0-SNAPSHOT",
generatorName = "scala-sttp4-jsoniter",
skipValidate = false
)
)
def cleanMaven(using pwd: Path) =
val command = List("./mvnw", "clean")
if Debug.enabled then println(s"Running command: ${command.mkString(" ")}")
os.proc(command).call(stdout = os.Inherit, stderr = os.Inherit)
def installMaven(using pwd: Path) =
val command = List("./mvnw", "install", "-DskipTests", "-Dmaven.javadoc.skip=true")
if Debug.enabled then println(s"Running command: ${command.mkString(" ")}")
os.proc(command).call(stdout = os.Inherit, stderr = os.Inherit)
def cleanupGeneratedFiles(project: Project, projectRootPath: Path) =
val basePackage = os.SubPath(project.projectMainPackage.replace(".", "/"))
os.remove.all(projectRootPath / "build.sbt")
os.remove.all(projectRootPath / "target")
os.remove.all(projectRootPath / "project")
os.remove.all(projectRootPath / "README.md")
os.remove.all(projectRootPath / "src" / "main" / "scala" / basePackage / "api")
os.remove.all(projectRootPath / "src" / "main" / "scala" / basePackage / "model")
os.remove.all(projectRootPath / "src" / "main" / "scala" / basePackage / "core")
def runGeneratorJsoniter(project: Project, projectRootPath: Path, strict: Boolean = false)(using pwd: Path) =
val additionalProps = {
val base = s"mainPackage=${project.projectMainPackage},groupId=${project.projectGroupId},artifactId=${project.projectArtifactId},artifactVersion=${project.projectVersion}"
val extended = if project.additionalProps.nonEmpty then s"$base,${project.additionalProps}" else base
List(s"--additional-properties", extended)
}
val validateFlag = if project.skipValidate && !strict then List("--skip-validate-spec") else Nil
val schemaMappings = if project.schemaMappings.nonEmpty then List("--schema-mappings", project.schemaMappings) else Nil
val typeMappings = if project.typeMappings.nonEmpty then List("--type-mappings", project.typeMappings) else Nil
val importMappings = if project.importMappings.nonEmpty then List("--import-mappings", project.importMappings) else Nil
val command = List(
List("java", "-jar", "modules/openapi-generator-cli/target/openapi-generator-cli.jar", "generate",
"-i", s"$projectRootPath/openapi.json",
"--generator-name", project.generatorName,
"-o", projectRootPath.toString),
validateFlag,
additionalProps,
schemaMappings,
typeMappings,
importMappings
).flatten
if Debug.enabled then println(s"Running command: ${command.mkString(" ")}")
os.proc(command).call(stdout = os.Inherit, stderr = os.Inherit)
def runGeneratorOld(project: Project, projectRootPath: Path)(using pwd: Path) =
val additionalProps = {
val base = s"mainPackage=${project.projectMainPackage},groupId=${project.projectGroupId},artifactId=${project.projectArtifactId},artifactVersion=${project.projectVersion},jsonLibrary=circe"
val extended = if project.additionalProps.nonEmpty then s"$base,${project.additionalProps}" else base
List("--additional-properties", extended)
}
val validateFlag = if project.skipValidate then List("--skip-validate-spec") else Nil
val schemaMappings = if project.schemaMappings.nonEmpty then List("--schema-mappings", project.schemaMappings) else Nil
val typeMappings = if project.typeMappings.nonEmpty then List("--type-mappings", project.typeMappings) else Nil
val importMappings = if project.importMappings.nonEmpty then List("--import-mappings", project.importMappings) else Nil
val command = List(
List("openapi-generator-cli", "generate",
"-i", s"$projectRootPath/openapi.json",
"--generator-name", "scala-sttp4",
"-o", projectRootPath.toString),
validateFlag,
additionalProps,
schemaMappings,
typeMappings,
importMappings
).flatten
if Debug.enabled then println(s"Running command: ${command.mkString(" ")}")
os.proc(command).call(stdout = os.Inherit, stderr = os.Inherit)
def processProject(project: Project, cmd: Command)(using pwd: Path) =
val projectRootPath = pwd / os.up / project.project
cmd match
case Command.Clean =>
cleanMaven
cleanupGeneratedFiles(project, projectRootPath)
case Command.Generate =>
runGeneratorJsoniter(project, projectRootPath)
case Command.GenerateStrict =>
runGeneratorJsoniter(project, projectRootPath, strict = true)
case Command.GenerateOldGen =>
cleanupGeneratedFiles(project, projectRootPath)
runGeneratorOld(project, projectRootPath)
case Command.Strict =>
cleanMaven
cleanupGeneratedFiles(project, projectRootPath)
installMaven
runGeneratorJsoniter(project, projectRootPath, strict = true)
case Command.Exit =>
// Do nothing
def main(): Unit =
given pwd: Path = os.pwd
println("Args: " + args.mkString(" "))
try
// Handle debug flag
val filteredArgs = args.toList.filter { arg =>
if arg == "--debug-script" then
Debug.enabled = true
false
else true
}
val (cmdStr, target) = filteredArgs match
case cmd :: "all" :: _ => (cmd, Target.All)
case cmd :: name :: _ => (cmd, Target.Specific(name))
case cmd :: Nil => (cmd, Target.All)
case _ => ("exit", Target.All)
val cmd = cmdStr match
case "clean" => Command.Clean
case "generate" => Command.Generate
case "generate-strict" => Command.GenerateStrict
case "generate-old-gen" => Command.GenerateOldGen
case "strict" => Command.Strict
case "exit" => Command.Exit
case _ => Command.Exit
(target, cmd) match
case (_, Command.Exit) =>
println("Usage: scala rebuild.sc <command> [project]")
println("Available commands: clean, generate, generate-strict, upstream, strict")
case (Target.All, _) =>
projects.values.foreach(project => processProject(project, cmd))
say("It is done")
case (Target.Specific(projectName), _) =>
projects.get(projectName) match
case Some(project) =>
processProject(project, cmd)
say("It is done")
case None =>
System.err.println(s"Project '$projectName' not found. Available projects: ${projects.keys.mkString(", ")}")
sys.exit(1)
catch
case e: Exception =>
say("You dun goofed")
throw e
main()

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,15 @@
README.md
build.sbt
project/build.properties
src/main/scala/org/openapitools/client/api/PetApi.scala
src/main/scala/org/openapitools/client/api/StoreApi.scala
src/main/scala/org/openapitools/client/api/UserApi.scala
src/main/scala/org/openapitools/client/core/AdditionalTypeSerializers.scala
src/main/scala/org/openapitools/client/core/Helpers.scala
src/main/scala/org/openapitools/client/core/JsonSupport.scala
src/main/scala/org/openapitools/client/model/ApiResponse.scala
src/main/scala/org/openapitools/client/model/Category.scala
src/main/scala/org/openapitools/client/model/Order.scala
src/main/scala/org/openapitools/client/model/Pet.scala
src/main/scala/org/openapitools/client/model/Tag.scala
src/main/scala/org/openapitools/client/model/User.scala

View File

@@ -0,0 +1 @@
7.16.0-SNAPSHOT

View File

@@ -0,0 +1,117 @@
# openapi-client
OpenAPI Petstore
- API version: 1.0.0
- Generator version: 7.16.0-SNAPSHOT
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)*
## Requirements
Building the API client library requires:
1. Java 1.7+
2. Maven/Gradle/SBT
## Installation
To install the API client library to your local Maven repository, simply execute:
```shell
mvn clean install
```
To deploy it to a remote Maven repository instead, configure the settings of the repository and execute:
```shell
mvn clean deploy
```
Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information.
### Maven users
Add this dependency to your project's POM:
```xml
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-client</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
```
### Gradle users
Add this dependency to your project's build file:
```groovy
compile "org.openapitools:openapi-client:1.0.0"
```
### SBT users
```scala
libraryDependencies += "org.openapitools" % "openapi-client" % "1.0.0"
```
## Getting Started
## Documentation for API Endpoints
All URIs are relative to *http://petstore.swagger.io/v2*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
*PetApi* | **addPet** | **POST** /pet | Add a new pet to the store
*PetApi* | **deletePet** | **DELETE** /pet/${petIdPathParam} | Deletes a pet
*PetApi* | **findPetsByStatus** | **GET** /pet/findByStatus | Finds Pets by status
*PetApi* | **findPetsByTags** | **GET** /pet/findByTags | Finds Pets by tags
*PetApi* | **getPetById** | **GET** /pet/${petIdPathParam} | Find pet by ID
*PetApi* | **updatePet** | **PUT** /pet | Update an existing pet
*PetApi* | **updatePetWithForm** | **POST** /pet/${petIdPathParam} | Updates a pet in the store with form data
*PetApi* | **uploadFile** | **POST** /pet/${petIdPathParam}/uploadImage | uploads an image
*StoreApi* | **deleteOrder** | **DELETE** /store/order/${orderIdPathParam} | Delete purchase order by ID
*StoreApi* | **getInventory** | **GET** /store/inventory | Returns pet inventories by status
*StoreApi* | **getOrderById** | **GET** /store/order/${orderIdPathParam} | Find purchase order by ID
*StoreApi* | **placeOrder** | **POST** /store/order | Place an order for a pet
*UserApi* | **createUser** | **POST** /user | Create user
*UserApi* | **createUsersWithArrayInput** | **POST** /user/createWithArray | Creates list of users with given input array
*UserApi* | **createUsersWithListInput** | **POST** /user/createWithList | Creates list of users with given input array
*UserApi* | **deleteUser** | **DELETE** /user/${usernamePathParam} | Delete user
*UserApi* | **getUserByName** | **GET** /user/${usernamePathParam} | Get user by user name
*UserApi* | **loginUser** | **GET** /user/login | Logs user into the system
*UserApi* | **logoutUser** | **GET** /user/logout | Logs out current logged in user session
*UserApi* | **updateUser** | **PUT** /user/${usernamePathParam} | Updated user
## Documentation for Models
- [ApiResponse](ApiResponse.md)
- [Category](Category.md)
- [Order](Order.md)
- [Pet](Pet.md)
- [Tag](Tag.md)
- [User](User.md)
<a id="documentation-for-authorization"></a>
## Documentation for Authorization
Authentication schemes defined for the API:
<a id="api_key"></a>
### api_key
- **Type**: API key
- **API key parameter name**: api_key
- **Location**: HTTP header
## Author

View File

@@ -0,0 +1,19 @@
version := "1.0.0"
name := "openapi-client"
organization := "org.openapitools"
scalaVersion := "3.3.4"
libraryDependencies ++= Seq(
"com.softwaremill.sttp.client4" %% "core" % "4.0.0-RC1",
"com.softwaremill.sttp.client4" %% "jsoniter" % "4.0.0-RC1",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.31.1",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.31.1" % "compile-internal",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-circe" % "2.31.1"
)
scalacOptions := Seq(
"-unchecked",
"-deprecation",
"-feature"
)

View File

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

View File

@@ -0,0 +1,230 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.api
import org.openapitools.client.model.ApiResponse
import java.io.File
import org.openapitools.client.model.Pet
import org.openapitools.client.core.JsonSupport.{*, given}
import org.openapitools.client.core.FormSerializable
import org.openapitools.client.core.FormStyleFormat
import org.openapitools.client.core.HeaderSerializable
import org.openapitools.client.core.ApiKeyLocation
import org.openapitools.client.core.PathStyleFormat
import org.openapitools.client.core.PathSerializable
import org.openapitools.client.core.CookieSerializable
import org.openapitools.client.core.Helpers.*
import sttp.client4.jsoniter.*
import sttp.client4.*
import sttp.model.Method
object PetApi:
def apply(baseUrl: String = "http://petstore.swagger.io/v2"): PetApi[org.openapitools.client.core.Authorization.NoAuthorization.type] = PetApi(baseUrl, org.openapitools.client.core.Authorization.NoAuthorization)
def withBasicAuth(baseUrl: String, username: String, password: String): PetApi[org.openapitools.client.core.Authorization.BasicAuth] =
PetApi(baseUrl, org.openapitools.client.core.Authorization.BasicAuth(username, password))
def withApiKeyAuth(baseUrl: String, apiKey: String): PetApi[org.openapitools.client.core.Authorization.ApiKey] =
PetApi(baseUrl, org.openapitools.client.core.Authorization.ApiKey(apiKey))
def withBearerTokenAuth(baseUrl: String, token: String): PetApi[org.openapitools.client.core.Authorization.BearerToken] =
PetApi(baseUrl, org.openapitools.client.core.Authorization.BearerToken(token))
case class PetApi[Auth <: org.openapitools.client.core.Authorization] private (baseUrl: String, authConfig: org.openapitools.client.core.Authorization):
def withBasicAuth(username: String, password: String): PetApi[org.openapitools.client.core.Authorization.BasicAuth] =
copy(authConfig = org.openapitools.client.core.Authorization.BasicAuth(username, password))
def withApiKeyAuth(apiKey: String): PetApi[org.openapitools.client.core.Authorization.ApiKey] =
copy(authConfig = org.openapitools.client.core.Authorization.ApiKey(apiKey))
def withNoAuth: PetApi[org.openapitools.client.core.Authorization.NoAuthorization.type] =
copy(authConfig = org.openapitools.client.core.Authorization.NoAuthorization)
def withBearerTokenAuth(token: String): PetApi[org.openapitools.client.core.Authorization.BearerToken] =
copy(authConfig = org.openapitools.client.core.Authorization.BearerToken(token))
/**
*
*
* Expected answers:
* code 200 : Pet (successful operation)
* code 405 : (Invalid input)
*
* @param pet Pet object that needs to be added to the store
*/
def addPet(pet: Pet): sttp.client4.Request[Either[ResponseException[String], Pet]] =
val requestURL =
uri"$baseUrl/pet"
basicRequest
.method(Method.POST, requestURL)
.contentType("application/json")
.body(asJson(pet))
.response(asJson[Pet])
/**
*
*
* Expected answers:
* code 400 : (Invalid pet value)
*
* @param petId Pet id to delete
* @param apiKey
*/
def deletePet(petId: Long, apiKey: Option[String] = scala.None): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val petIdPathParam = PathSerializable.serialize("petId", petId, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/pet/${petIdPathParam}"
basicRequest
.method(Method.DELETE, requestURL)
.contentType("application/json")
.headers(HeaderSerializable.serialize("api_key", apiKey, false))
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
* Multiple status values can be provided with comma separated strings
*
* Expected answers:
* code 200 : Seq[Pet] (successful operation)
* code 400 : (Invalid status value)
*
* @param status Status values that need to be considered for filter
*/
def findPetsByStatus(status: Seq[String]): sttp.client4.Request[Either[ResponseException[String], Seq[Pet]]] =
val requestURL =
uri"$baseUrl/pet/findByStatus"
.addParams(FormSerializable.serialize("status", status, FormStyleFormat.FORM, false): _*)
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.response(asJson[Seq[Pet]])
/**
* Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
*
* Expected answers:
* code 200 : Seq[Pet] (successful operation)
* code 400 : (Invalid tag value)
*
* @param tags Tags to filter by
*/
def findPetsByTags(tags: Seq[String]): sttp.client4.Request[Either[ResponseException[String], Seq[Pet]]] =
val requestURL =
uri"$baseUrl/pet/findByTags"
.addParams(FormSerializable.serialize("tags", tags, FormStyleFormat.FORM, false): _*)
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.response(asJson[Seq[Pet]])
/**
* Returns a single pet
*
* Expected answers:
* code 200 : Pet (successful operation)
* code 400 : (Invalid ID supplied)
* code 404 : (Pet not found)
*
* Available security schemes:
* api_key (apiKey)
*
* @param petId ID of pet to return
*/
def getPetById(petId: Long)(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Pet]] =
val petIdPathParam = PathSerializable.serialize("petId", petId, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/pet/${petIdPathParam}"
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.response(asJson[Pet])
/**
*
*
* Expected answers:
* code 200 : Pet (successful operation)
* code 400 : (Invalid ID supplied)
* code 404 : (Pet not found)
* code 405 : (Validation exception)
*
* @param pet Pet object that needs to be added to the store
*/
def updatePet(pet: Pet): sttp.client4.Request[Either[ResponseException[String], Pet]] =
val requestURL =
uri"$baseUrl/pet"
basicRequest
.method(Method.PUT, requestURL)
.contentType("application/json")
.body(asJson(pet))
.response(asJson[Pet])
/**
*
*
* Expected answers:
* code 405 : (Invalid input)
*
* @param petId ID of pet that needs to be updated
* @param name Updated name of the pet
* @param status Updated status of the pet
*/
def updatePetWithForm(petId: Long, name: Option[String] = scala.None, status: Option[String] = scala.None): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val petIdPathParam = PathSerializable.serialize("petId", petId, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/pet/${petIdPathParam}"
basicRequest
.method(Method.POST, requestURL)
.contentType("application/x-www-form-urlencoded")
.body(
FormSerializable.serialize("name", name, FormStyleFormat.FORM, false) ++
FormSerializable.serialize("status", status, FormStyleFormat.FORM, false),
"utf-8"
)
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
*
*
* Expected answers:
* code 200 : ApiResponse (successful operation)
*
* @param petId ID of pet to update
* @param additionalMetadata Additional data to pass to server
* @param file file to upload
*/
def uploadFile(petId: Long, additionalMetadata: Option[String] = scala.None, file: Option[File] = scala.None): sttp.client4.Request[Either[ResponseException[String], ApiResponse]] =
val petIdPathParam = PathSerializable.serialize("petId", petId, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/pet/${petIdPathParam}/uploadImage"
basicRequest
.method(Method.POST, requestURL)
.contentType("multipart/form-data")
.multipartBody(Seq(
additionalMetadata.map(value => multipart("additionalMetadata", value.toString))
,
file.map(multipartFile("file", _))
).flatten)
.response(asJson[ApiResponse])
end PetApi

View File

@@ -0,0 +1,129 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.api
import org.openapitools.client.model.Order
import org.openapitools.client.core.JsonSupport.{*, given}
import org.openapitools.client.core.FormSerializable
import org.openapitools.client.core.FormStyleFormat
import org.openapitools.client.core.HeaderSerializable
import org.openapitools.client.core.ApiKeyLocation
import org.openapitools.client.core.PathStyleFormat
import org.openapitools.client.core.PathSerializable
import org.openapitools.client.core.CookieSerializable
import org.openapitools.client.core.Helpers.*
import sttp.client4.jsoniter.*
import sttp.client4.*
import sttp.model.Method
object StoreApi:
def apply(baseUrl: String = "http://petstore.swagger.io/v2"): StoreApi[org.openapitools.client.core.Authorization.NoAuthorization.type] = StoreApi(baseUrl, org.openapitools.client.core.Authorization.NoAuthorization)
def withBasicAuth(baseUrl: String, username: String, password: String): StoreApi[org.openapitools.client.core.Authorization.BasicAuth] =
StoreApi(baseUrl, org.openapitools.client.core.Authorization.BasicAuth(username, password))
def withApiKeyAuth(baseUrl: String, apiKey: String): StoreApi[org.openapitools.client.core.Authorization.ApiKey] =
StoreApi(baseUrl, org.openapitools.client.core.Authorization.ApiKey(apiKey))
def withBearerTokenAuth(baseUrl: String, token: String): StoreApi[org.openapitools.client.core.Authorization.BearerToken] =
StoreApi(baseUrl, org.openapitools.client.core.Authorization.BearerToken(token))
case class StoreApi[Auth <: org.openapitools.client.core.Authorization] private (baseUrl: String, authConfig: org.openapitools.client.core.Authorization):
def withBasicAuth(username: String, password: String): StoreApi[org.openapitools.client.core.Authorization.BasicAuth] =
copy(authConfig = org.openapitools.client.core.Authorization.BasicAuth(username, password))
def withApiKeyAuth(apiKey: String): StoreApi[org.openapitools.client.core.Authorization.ApiKey] =
copy(authConfig = org.openapitools.client.core.Authorization.ApiKey(apiKey))
def withNoAuth: StoreApi[org.openapitools.client.core.Authorization.NoAuthorization.type] =
copy(authConfig = org.openapitools.client.core.Authorization.NoAuthorization)
def withBearerTokenAuth(token: String): StoreApi[org.openapitools.client.core.Authorization.BearerToken] =
copy(authConfig = org.openapitools.client.core.Authorization.BearerToken(token))
/**
* For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
*
* Expected answers:
* code 400 : (Invalid ID supplied)
* code 404 : (Order not found)
*
* @param orderId ID of the order that needs to be deleted
*/
def deleteOrder(orderId: String): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val orderIdPathParam = PathSerializable.serialize("orderId", orderId, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/store/order/${orderIdPathParam}"
basicRequest
.method(Method.DELETE, requestURL)
.contentType("application/json")
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
* Returns a map of status codes to quantities
*
* Expected answers:
* code 200 : Map[String, Int] (successful operation)
*
* Available security schemes:
* api_key (apiKey)
*/
def getInventory(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Map[String, Int]]] =
val requestURL =
uri"$baseUrl/store/inventory"
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.response(asJson[Map[String, Int]])
/**
* For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions
*
* Expected answers:
* code 200 : Order (successful operation)
* code 400 : (Invalid ID supplied)
* code 404 : (Order not found)
*
* @param orderId ID of pet that needs to be fetched
*/
def getOrderById(orderId: Long): sttp.client4.Request[Either[ResponseException[String], Order]] =
val orderIdPathParam = PathSerializable.serialize("orderId", orderId, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/store/order/${orderIdPathParam}"
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.response(asJson[Order])
/**
*
*
* Expected answers:
* code 200 : Order (successful operation)
* code 400 : (Invalid Order)
*
* @param order order placed for purchasing the pet
*/
def placeOrder(order: Order): sttp.client4.Request[Either[ResponseException[String], Order]] =
val requestURL =
uri"$baseUrl/store/order"
basicRequest
.method(Method.POST, requestURL)
.contentType("application/json")
.body(asJson(order))
.response(asJson[Order])
end StoreApi

View File

@@ -0,0 +1,231 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.api
import java.time.OffsetDateTime
import org.openapitools.client.model.User
import org.openapitools.client.core.JsonSupport.{*, given}
import org.openapitools.client.core.FormSerializable
import org.openapitools.client.core.FormStyleFormat
import org.openapitools.client.core.HeaderSerializable
import org.openapitools.client.core.ApiKeyLocation
import org.openapitools.client.core.PathStyleFormat
import org.openapitools.client.core.PathSerializable
import org.openapitools.client.core.CookieSerializable
import org.openapitools.client.core.Helpers.*
import sttp.client4.jsoniter.*
import sttp.client4.*
import sttp.model.Method
object UserApi:
def apply(baseUrl: String = "http://petstore.swagger.io/v2"): UserApi[org.openapitools.client.core.Authorization.NoAuthorization.type] = UserApi(baseUrl, org.openapitools.client.core.Authorization.NoAuthorization)
def withBasicAuth(baseUrl: String, username: String, password: String): UserApi[org.openapitools.client.core.Authorization.BasicAuth] =
UserApi(baseUrl, org.openapitools.client.core.Authorization.BasicAuth(username, password))
def withApiKeyAuth(baseUrl: String, apiKey: String): UserApi[org.openapitools.client.core.Authorization.ApiKey] =
UserApi(baseUrl, org.openapitools.client.core.Authorization.ApiKey(apiKey))
def withBearerTokenAuth(baseUrl: String, token: String): UserApi[org.openapitools.client.core.Authorization.BearerToken] =
UserApi(baseUrl, org.openapitools.client.core.Authorization.BearerToken(token))
case class UserApi[Auth <: org.openapitools.client.core.Authorization] private (baseUrl: String, authConfig: org.openapitools.client.core.Authorization):
def withBasicAuth(username: String, password: String): UserApi[org.openapitools.client.core.Authorization.BasicAuth] =
copy(authConfig = org.openapitools.client.core.Authorization.BasicAuth(username, password))
def withApiKeyAuth(apiKey: String): UserApi[org.openapitools.client.core.Authorization.ApiKey] =
copy(authConfig = org.openapitools.client.core.Authorization.ApiKey(apiKey))
def withNoAuth: UserApi[org.openapitools.client.core.Authorization.NoAuthorization.type] =
copy(authConfig = org.openapitools.client.core.Authorization.NoAuthorization)
def withBearerTokenAuth(token: String): UserApi[org.openapitools.client.core.Authorization.BearerToken] =
copy(authConfig = org.openapitools.client.core.Authorization.BearerToken(token))
/**
* This can only be done by the logged in user.
*
* Expected answers:
* code 0 : (successful operation)
*
* Available security schemes:
* api_key (apiKey)
*
* @param user Created user object
*/
def createUser(user: User)(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val requestURL =
uri"$baseUrl/user"
basicRequest
.method(Method.POST, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.body(asJson(user))
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
*
*
* Expected answers:
* code 0 : (successful operation)
*
* Available security schemes:
* api_key (apiKey)
*
* @param user List of user object
*/
def createUsersWithArrayInput(user: Seq[User])(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val requestURL =
uri"$baseUrl/user/createWithArray"
basicRequest
.method(Method.POST, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.body(asJson(user))
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
*
*
* Expected answers:
* code 0 : (successful operation)
*
* Available security schemes:
* api_key (apiKey)
*
* @param user List of user object
*/
def createUsersWithListInput(user: Seq[User])(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val requestURL =
uri"$baseUrl/user/createWithList"
basicRequest
.method(Method.POST, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.body(asJson(user))
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
* This can only be done by the logged in user.
*
* Expected answers:
* code 400 : (Invalid username supplied)
* code 404 : (User not found)
*
* Available security schemes:
* api_key (apiKey)
*
* @param username The name that needs to be deleted
*/
def deleteUser(username: String)(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val usernamePathParam = PathSerializable.serialize("username", username, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/user/${usernamePathParam}"
basicRequest
.method(Method.DELETE, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
*
*
* Expected answers:
* code 200 : User (successful operation)
* code 400 : (Invalid username supplied)
* code 404 : (User not found)
*
* @param username The name that needs to be fetched. Use user1 for testing.
*/
def getUserByName(username: String): sttp.client4.Request[Either[ResponseException[String], User]] =
val usernamePathParam = PathSerializable.serialize("username", username, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/user/${usernamePathParam}"
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.response(asJson[User])
/**
*
*
* Expected answers:
* code 200 : String (successful operation)
* Headers :
* Set-Cookie - Cookie authentication key for use with the `api_key` apiKey authentication.
* X-Rate-Limit - calls per hour allowed by the user
* X-Expires-After - date in UTC when token expires
* code 400 : (Invalid username/password supplied)
*
* @param username The user name for login
* @param password The password for login in clear text
*/
def loginUser(username: String, password: String): sttp.client4.Request[Either[ResponseException[String], String]] =
val requestURL =
uri"$baseUrl/user/login"
.addParams(FormSerializable.serialize("username", username, FormStyleFormat.FORM, true): _*)
.addParams(FormSerializable.serialize("password", password, FormStyleFormat.FORM, true): _*)
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.response(asJson[String])
/**
*
*
* Expected answers:
* code 0 : (successful operation)
*
* Available security schemes:
* api_key (apiKey)
*/
def logoutUser(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val requestURL =
uri"$baseUrl/user/logout"
basicRequest
.method(Method.GET, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
/**
* This can only be done by the logged in user.
*
* Expected answers:
* code 400 : (Invalid user supplied)
* code 404 : (User not found)
*
* Available security schemes:
* api_key (apiKey)
*
* @param username name that need to be deleted
* @param user Updated user object
*/
def updateUser(username: String, user: User)(using Auth <:< org.openapitools.client.core.Authorization.ApiKey): sttp.client4.Request[Either[ResponseException[String], Unit]] =
val usernamePathParam = PathSerializable.serialize("username", username, PathStyleFormat.SIMPLE, false)
val requestURL =
uri"$baseUrl/user/${usernamePathParam}"
basicRequest
.method(Method.PUT, requestURL)
.contentType("application/json")
.auth(authConfig, org.openapitools.client.core.ApiKeyLocation.HEADER, "api_key")
.body(asJson(user))
.response(asString.mapWithMetadata(ResponseAs.deserializeRightWithError(_ => Right(()))))
end UserApi

View File

@@ -0,0 +1,20 @@
package org.openapitools.client.core
import java.net.{URI, URISyntaxException}
import com.github.plokhotnyuk.jsoniter_scala.core.*
trait AdditionalTypeSerializers:
implicit final lazy val URICodec: JsonValueCodec[URI] = new JsonValueCodec[URI]:
def nullValue: URI = null
def decodeValue(in: JsonReader, default: URI): URI =
try
val uriString = in.readString(null)
if (uriString != null) new URI(uriString) else default
catch
case e: URISyntaxException =>
in.decodeError(s"Invalid URI syntax: ${e.getMessage}")
def encodeValue(uri: URI, out: JsonWriter): Unit =
if (uri != null) out.writeVal(uri.toString)
else out.writeNull()

View File

@@ -0,0 +1,404 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.core
import scala.deriving.*
import scala.compiletime.*
import java.io.File
import com.github.plokhotnyuk.jsoniter_scala.core.{JsonValueCodec, writeToString}
type Primitive = String | Short | Int | Long | Float | Double | BigDecimal |
Boolean
enum Authorization:
case NoAuthorization
case BasicAuth(username: String, password: String)
case ApiKey(apiKey: String)
case BearerToken(token: String)
enum ApiKeyLocation:
case HEADER
case COOKIE
case QUERY
case NOAPIKEY
enum FormStyleFormat:
case FORM
case SPACEDELIMITED
case PIPEDELIMITED
case DEEPOBJECT
enum PathStyleFormat:
case SIMPLE
case LABEL
case MATRIX
inline def allLabels[T <: Tuple]: List[String] =
constValueTuple[T].toList.asInstanceOf[List[String]]
private inline def checkFields[T <: Tuple]: Unit =
inline erasedValue[T] match {
case _: EmptyTuple => ()
case _: (t *: ts) =>
inline erasedValue[t] match
case _: Primitive => checkFields[ts]
case _: Option[Primitive] => checkFields[ts]
case _ => error("Cannot derive structure, structure must consist only of primitive fields")
}
private val flattenKeyVals: Primitive | Option[Primitive] => Option[Primitive] = {
case p: Primitive => Some(p)
case opt: Option[Primitive] => opt
}
trait FormSerializable[T]:
inline def serialize(
name: String,
obj: T,
inline format: FormStyleFormat = FormStyleFormat.FORM,
inline explode: Boolean = true
): Seq[(String, String)]
object FormSerializable:
inline def serialize[T](
name: String,
obj: T,
inline format: FormStyleFormat = FormStyleFormat.FORM,
inline explode: Boolean = true
): Seq[(String, String)] =
summonFrom {
case t: FormSerializable[T] => t.serialize(name, obj, format, explode)
case _ =>
inline obj match
case primitive: Primitive =>
serializePrimitive(name, primitive, format, explode)
case array: Seq[Primitive] =>
serializeArray(name, array, format, explode)
case optPrimitive: Option[Primitive] =>
optPrimitive.map(value => serializePrimitive(name, value, format, explode))
.getOrElse(Seq.empty[(String, String)])
case optArray: Option[Seq[Primitive]] =>
optArray.map(serializeArray(name, _, format, explode))
.getOrElse(Seq.empty[(String, String)])
case freeObj: Map[String, Primitive] =>
freeObj.map((key, value) => (key, value.toString)).toSeq
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, format, explode)
}.getOrElse(Seq.empty[(String, String)])
case mirror: Mirror.SumOf[t] => optObj.map(v => (name, writeToString(v)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))).toSeq
case obj =>
inline summonInline[Mirror.Of[T]] match
case _: Mirror.SumOf[T] =>
Seq((name, writeToString(obj)(summonInline[JsonValueCodec[T]])))
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes] // Stripe ma IDGAF bo używają deepObject np. tak lines[0][tax_amounts][0][amount] - mimo tego że spec na to nie pozwala
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, format, explode)
}
private inline def serializePrimitive(
paramName: String,
value: Primitive,
inline format: FormStyleFormat,
inline explode: Boolean
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
Seq(paramName -> value.toString) // for primitve values explode does not change anything
case FormStyleFormat.SPACEDELIMITED =>
error("FormStyleFormat.SpaceDelimited does not support primitive values")
case FormStyleFormat.PIPEDELIMITED =>
error("FormStyleFormat.PipeDelimited does not support primitive values")
case FormStyleFormat.DEEPOBJECT =>
error("FormStyleFormat.DeepObject does not support primitive values")
}
private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline format: FormStyleFormat,
inline explode: Boolean
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString(","))
case FormStyleFormat.SPACEDELIMITED =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString(" ")) // Sttp will encode space as +, from https://swagger.io/docs/specification/v3_0/serialization/#query-parameters it is not clear if it should be + or %20
case FormStyleFormat.PIPEDELIMITED =>
inline if explode then values.map(s => (paramName, s.toString))
else Seq(paramName -> values.mkString("|"))
case FormStyleFormat.DEEPOBJECT =>
error("FormStyleFormat.DeepObject does not support arrays")
}
private inline def serializeModel(
paramName: String,
keyValPairs: Seq[(String, Primitive)],
inline format: FormStyleFormat,
inline explode: Boolean
): Seq[(String, String)] = {
inline format match
case FormStyleFormat.FORM =>
inline if explode then keyValPairs.map((key, value) => (key, value.toString))
else Seq(paramName -> keyValPairs.flatMap((key, value) => Seq(key, value.toString)).mkString(","))
case FormStyleFormat.SPACEDELIMITED =>
error("FormStyleFormat.SpaceDelimited does not support objects")
case FormStyleFormat.PIPEDELIMITED =>
error("FormStyleFormat.PipeDelimited does not support objects")
case FormStyleFormat.DEEPOBJECT =>
inline if explode then keyValPairs.map((key, value) => (s"$paramName[$key]", value.toString))
else error("FormStyleFormat.DeepObject does not support explode=false")
}
end FormSerializable
trait HeaderSerializable[T]:
inline def serialize(
name: String,
obj: T,
inline explode: Boolean = true
): Map[String, String]
object HeaderSerializable:
inline def serialize[T](
name: String,
obj: T,
inline explode: Boolean = true
): Map[String, String] =
summonFrom {
case t: HeaderSerializable[T] => t.serialize(name, obj, explode)
case _ => inline obj match
case primitive: Primitive => Map(name -> primitive.toString)
case optPrimitive: Option[Primitive] => optPrimitive.map(v => Map(name -> v.toString)).getOrElse(Map.empty[String, String])
case seqPrimitive: Seq[Primitive] => Map(name -> seqPrimitive.map(_.toString).mkString(","))
case optSeqPrimitive: Option[Seq[Primitive]] => optSeqPrimitive.map(v => Map(name -> v.map(_.toString).mkString(","))).getOrElse(Map.empty[String, String])
case mapPrimitive: Map[String, Primitive] => mapPrimitive.map((k, v) => (k, v.toString))
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get.toString))
inline if explode then
Map(name ->keyVals.map((k, v) => s"$k=$v").mkString(","))
else
Map(name -> keyVals.flatMap((k, v) => Seq(k, v)).mkString(","))
}.getOrElse(Map.empty[String, String])
case mirror: Mirror.SumOf[t] => optObj.map(v => Map(name -> writeToString(v)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))).getOrElse(Map.empty[String, String])
case obj: T =>
inline summonInline[Mirror.Of[T]] match
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get.toString))
inline if explode then
Map(name ->keyVals.map((k, v) => s"$k=$v").mkString(","))
else
Map(name -> keyVals.flatMap((k, v) => Seq(k, v)).mkString(","))
case mirror: Mirror.SumOf[T] => Map(name -> writeToString(obj)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))
}
end HeaderSerializable
trait PathSerializer[T]:
inline def serialize[T](name: String, obj: T, inline style: PathStyleFormat, inline explode: Boolean): String
object PathSerializable:
inline def serialize[T](name: String, obj: T, inline style: PathStyleFormat, inline explode: Boolean): String =
summonFrom {
case t: PathSerializer[T] => t.serialize(name, obj, style, explode)
case _ =>
inline obj match
case primitive: Primitive =>
serializePrimitive(name, primitive, style, explode)
case array: Seq[Primitive] =>
serializeArray(name, array, style, explode)
case optPrimitive: Option[Primitive] =>
optPrimitive.map(value => serializePrimitive(name, value, style, explode))
.getOrElse("")
case optArray: Option[Seq[Primitive]] =>
optArray.map(serializeArray(name, _, style, explode))
.getOrElse("")
case freeObj: Map[String, Primitive] =>
serializeModel(name, freeObj.map((key, value) => (key, value.toString)).toSeq, style, explode)
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, style, explode)
}.getOrElse("")
case mirror: Mirror.SumOf[t] => optObj.map(writeToString(_)(summonInline[JsonValueCodec[mirror.MirroredMonoType]])).getOrElse("")
case obj =>
inline summonInline[Mirror.Of[T]] match
case _: Mirror.SumOf[T] =>
writeToString(obj)(summonInline[JsonValueCodec[T]])
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, style, explode)
}
private inline def serializePrimitive(
paramName: String,
value: Primitive,
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE => value.toString
case PathStyleFormat.LABEL => s".${value.toString}"
case PathStyleFormat.MATRIX => s";$paramName=${value.toString}"
private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE => values.map(_.toString).mkString(",")
case PathStyleFormat.LABEL => inline if explode then values.map(_.toString).mkString(".", ".", "") else values.map(_.toString).mkString(".", ",", "")
case PathStyleFormat.MATRIX => inline if explode then values.map(v => s";$paramName=${v.toString}").mkString else s";$paramName=" + values.map(_.toString).mkString(",")
private inline def serializeModel(
paramName: String,
keyValPairs: Seq[(String, Primitive)],
inline format: PathStyleFormat,
inline explode: Boolean
): String = inline format match
case PathStyleFormat.SIMPLE =>
inline if explode then keyValPairs.map((k, v) => s"$k=${v.toString}").mkString(",")
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(",")
case PathStyleFormat.LABEL =>
inline if explode then keyValPairs.map((k, v) => s"$k=${v.toString}").mkString(".", ".", "")
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(".", ",", "")
case PathStyleFormat.MATRIX =>
inline if explode then keyValPairs.map((k, v) => s";$k=${v.toString}").mkString
else keyValPairs.map((k, v) => s"$k,${v.toString}").mkString(s";$paramName=", ",", "")
end PathSerializable
trait CookieSerializable[T]:
inline def serialize(
name: String,
obj: T,
inline explode: Boolean = true
): Seq[(String, String)]
object CookieSerializable:
inline def serialize[T](
name: String,
obj: T,
inline explode: Boolean = true
): Seq[(String, String)] =
summonFrom {
case t: CookieSerializable[T] => t.serialize(name, obj, explode)
case _ =>
inline obj match
case primitive: Primitive =>
serializePrimitive(name, primitive, explode)
case array: Seq[Primitive] =>
serializeArray(name, array, explode)
case optPrimitive: Option[Primitive] =>
optPrimitive.map(value => serializePrimitive(name, value, explode))
.getOrElse(Seq.empty[(String, String)])
case optArray: Option[Seq[Primitive]] =>
optArray.map(serializeArray(name, _, explode))
.getOrElse(Seq.empty[(String, String)])
case freeObj: Map[String, Primitive] =>
serializeModel(name, freeObj.map((key, value) => (key, value.toString)).toSeq, explode)
case optObj: Option[t] =>
inline summonInline[Mirror.Of[t]] match
case mirror: Mirror.ProductOf[t] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
optObj.map { obj =>
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, explode)
}.getOrElse(Seq.empty[(String, String)])
case mirror: Mirror.SumOf[t] => optObj.map(v => (name, writeToString(v)(summonInline[JsonValueCodec[mirror.MirroredMonoType]]))).toSeq
case obj =>
inline summonInline[Mirror.Of[T]] match
case _: Mirror.SumOf[T] =>
Seq(name -> writeToString(obj)(summonInline[JsonValueCodec[T]]))
case mirror: Mirror.ProductOf[T] =>
checkFields[mirror.MirroredElemTypes]
val labels = allLabels[mirror.MirroredElemLabels]
val keyVals = labels.zip(obj.asInstanceOf[Product].productIterator.toSeq.asInstanceOf[Seq[Primitive | Option[Primitive]]].map(flattenKeyVals))
.filter((_, v) => v.isDefined)
.map((k, v) => (k, v.get))
serializeModel(name, keyVals, explode)
}
private inline def serializePrimitive(
paramName: String,
value: Primitive,
inline explode: Boolean
): Seq[(String, String)] = Seq(paramName -> value.toString)
private inline def serializeArray(
paramName: String,
values: Seq[Primitive],
inline explode: Boolean
): Seq[(String, String)] =
inline if explode then error("Not supported")
else Seq(paramName -> values.map(_.toString).mkString(","))
private inline def serializeModel(
paramName: String,
keyValPairs: Seq[(String, Primitive)],
inline explode: Boolean
): Seq[(String, String)] =
inline if explode then error("Not supported")
else Seq(paramName -> keyValPairs.map((k, v) => s"$k,$v").mkString(","))
end CookieSerializable
object Helpers:
extension (request: sttp.client4.Request[?])
def fileBody(file: Option[File] | File): sttp.client4.Request[?] =
file match
case f: File => request.body(f)
case f: Option[File] => f.map(request.body(_)).getOrElse(request)
def auth(authConfig: Authorization, location: ApiKeyLocation = ApiKeyLocation.NOAPIKEY, keyParamName: String = ""): sttp.client4.Request[?] =
authConfig match
case Authorization.NoAuthorization => request
case Authorization.BasicAuth(username, password) => request.auth.basic(username, password)
case Authorization.BearerToken(token) => request.auth.bearer(token)
case Authorization.ApiKey(apiKey) =>location match
case ApiKeyLocation.HEADER => request.header(keyParamName, apiKey)
case ApiKeyLocation.COOKIE => request.cookie(keyParamName, apiKey)
case ApiKeyLocation.QUERY => request.copy(uri = request.uri.addParam(keyParamName, apiKey))
case ApiKeyLocation.NOAPIKEY => request // since it can be called multiple times in request (when there are for example 2 auth methods) we want to make this call idempotent

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
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.core
import org.openapitools.client.model.*
import java.time.*
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import com.github.plokhotnyuk.jsoniter_scala.core.*
import com.github.plokhotnyuk.jsoniter_scala.circe.JsoniterScalaCodec.*
object JsonSupport extends AdditionalTypeSerializers:
inline given CodecMakerConfig = CodecMakerConfig.withAllowRecursiveTypes(true)
inline def deriveJsonCodec[A](using inline config: CodecMakerConfig): JsonValueCodec[A] =
JsonCodecMaker.make(config)
given orderCodec: JsonValueCodec[Order] = deriveJsonCodec
given seqPetCodec: JsonValueCodec[Seq[Pet]] = deriveJsonCodec
given userCodec: JsonValueCodec[User] = deriveJsonCodec
given apiResponseCodec: JsonValueCodec[ApiResponse] = deriveJsonCodec
given petCodec: JsonValueCodec[Pet] = deriveJsonCodec
given seqUserCodec: JsonValueCodec[Seq[User]] = deriveJsonCodec
given stringCodec: JsonValueCodec[String] = deriveJsonCodec
given mapStringIntCodec: JsonValueCodec[Map[String, Int]] = deriveJsonCodec

View File

@@ -0,0 +1,25 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model
import com.github.plokhotnyuk.jsoniter_scala.macros.named
/**
* An uploaded response
* Describes the result of uploading an image resource
*/
case class ApiResponse(
@named("code") code: Option[Int] = scala.None,
@named("type") `type`: Option[String] = scala.None,
@named("message") message: Option[String] = scala.None
)

View File

@@ -0,0 +1,24 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model
import com.github.plokhotnyuk.jsoniter_scala.macros.named
/**
* Pet category
* A category for a pet
*/
case class Category(
@named("id") id: Option[Long] = scala.None,
@named("name") name: Option[String] = scala.None
)

View File

@@ -0,0 +1,50 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model
import java.time.OffsetDateTime
import com.github.plokhotnyuk.jsoniter_scala.macros.named
/**
* Pet Order
* An order for a pets from the pet store
*/
case class Order(
@named("id") id: Option[Long] = scala.None,
@named("petId") petId: Option[Long] = scala.None,
@named("quantity") quantity: Option[Int] = scala.None,
@named("shipDate") shipDate: Option[OffsetDateTime] = scala.None,
/* Order Status */
@named("status") status: Option[OrderEnums.Status] = scala.None,
@named("complete") complete: Option[Boolean] = scala.None
)
object OrderEnums:
enum Status:
case `placed`
case `approved`
case `delivered`
object Status:
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import com.github.plokhotnyuk.jsoniter_scala.core.*
given statusCodec: JsonValueCodec[Status] = JsonCodecMaker.make {
CodecMakerConfig
.withAdtLeafClassNameMapper { x =>
JsonCodecMaker.simpleClassName(x) match
case "placed" => "placed"
case "approved" => "approved"
case "delivered" => "delivered"
}
.withDiscriminatorFieldName(scala.None)
}
end OrderEnums

View File

@@ -0,0 +1,49 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model
import com.github.plokhotnyuk.jsoniter_scala.macros.named
/**
* a Pet
* A pet for sale in the pet store
*/
case class Pet(
@named("id") id: Option[Long] = scala.None,
@named("category") category: Option[Category] = scala.None,
@named("name") name: String,
@named("photoUrls") photoUrls: Seq[String],
@named("tags") tags: Option[Seq[Tag]] = scala.None,
/* pet status in the store */
@named("status") status: Option[PetEnums.Status] = scala.None
)
object PetEnums:
enum Status:
case `available`
case `pending`
case `sold`
object Status:
import com.github.plokhotnyuk.jsoniter_scala.macros.*
import com.github.plokhotnyuk.jsoniter_scala.core.*
given statusCodec: JsonValueCodec[Status] = JsonCodecMaker.make {
CodecMakerConfig
.withAdtLeafClassNameMapper { x =>
JsonCodecMaker.simpleClassName(x) match
case "available" => "available"
case "pending" => "pending"
case "sold" => "sold"
}
.withDiscriminatorFieldName(scala.None)
}
end PetEnums

View File

@@ -0,0 +1,24 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model
import com.github.plokhotnyuk.jsoniter_scala.macros.named
/**
* Pet Tag
* A tag for a pet
*/
case class Tag(
@named("id") id: Option[Long] = scala.None,
@named("name") name: Option[String] = scala.None
)

View File

@@ -0,0 +1,31 @@
/**
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.client.model
import com.github.plokhotnyuk.jsoniter_scala.macros.named
/**
* a User
* A User who is purchasing from the pet store
*/
case class User(
@named("id") id: Option[Long] = scala.None,
@named("username") username: Option[String] = scala.None,
@named("firstName") firstName: Option[String] = scala.None,
@named("lastName") lastName: Option[String] = scala.None,
@named("email") email: Option[String] = scala.None,
@named("password") password: Option[String] = scala.None,
@named("phone") phone: Option[String] = scala.None,
/* User Status */
@named("userStatus") userStatus: Option[Int] = scala.None
)