Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/configs/kotlin-misk-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
generatorName: kotlin-misk
outputDir: samples/server/petstore/kotlin-misk-config
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
inputSpec: modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-kotlin-misk.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-misk
validateSpec: false
useBeanValidation: true
Expand Down
2 changes: 1 addition & 1 deletion bin/configs/kotlin-misk.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
generatorName: kotlin-misk
outputDir: samples/server/petstore/kotlin-misk
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
inputSpec: modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-kotlin-misk.yaml
templateDir: modules/openapi-generator/src/main/resources/kotlin-misk
additionalProperties:
hideGenerationTimestamp: "true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class CodegenOperation {
hasErrorResponseObject; // if 4xx, 5xx responses have at least one error object defined
public CodegenProperty returnProperty;
public String path, operationId, returnType, returnFormat, httpMethod, returnBaseType,
returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse;
returnContainer, summary, unescapedSummary, unescapedNotes, notes, baseName, defaultResponse;
public CodegenDiscriminator discriminator;
public List<Map<String, String>> consumes, produces, prioritizedContentTypes;
public List<CodegenServer> servers = new ArrayList<CodegenServer>();
Expand Down Expand Up @@ -429,6 +429,7 @@ public String toString() {
sb.append(", returnBaseType='").append(returnBaseType).append('\'');
sb.append(", returnContainer='").append(returnContainer).append('\'');
sb.append(", summary='").append(summary).append('\'');
sb.append(", unescapedSummary='").append(unescapedSummary).append('\'');
sb.append(", unescapedNotes='").append(unescapedNotes).append('\'');
sb.append(", notes='").append(notes).append('\'');
sb.append(", baseName='").append(baseName).append('\'');
Expand Down Expand Up @@ -502,6 +503,7 @@ public boolean equals(Object o) {
Objects.equals(returnBaseType, that.returnBaseType) &&
Objects.equals(returnContainer, that.returnContainer) &&
Objects.equals(summary, that.summary) &&
Objects.equals(unescapedSummary, that.unescapedSummary) &&
Objects.equals(unescapedNotes, that.unescapedNotes) &&
Objects.equals(notes, that.notes) &&
Objects.equals(baseName, that.baseName) &&
Expand Down Expand Up @@ -547,7 +549,7 @@ public int hashCode() {
returnTypeIsPrimitive, returnSimpleType, subresourceOperation, isMap,
isArray, isMultipart, isVoid, isResponseBinary, isResponseFile, isResponseOptional, hasReference,
isDeprecated, isCallbackRequest, uniqueItems, path, operationId, returnType, httpMethod,
returnBaseType, returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse,
returnBaseType, returnContainer, summary, unescapedSummary, unescapedNotes, notes, baseName, defaultResponse,
discriminator, consumes, produces, prioritizedContentTypes, servers, bodyParam, allParams, bodyParams,
pathParams, queryParams, headerParams, formParams, cookieParams, requiredParams, returnProperty, optionalParams,
authMethods, tags, responses, callbacks, imports, examples, requestBodyExamples, externalDocs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class CodegenParameter implements IJsonSchemaValidationProperties {
isCollectionFormatMulti, isPrimitiveType, isModel, isExplode, isDeepObject, isMatrix, isAllowEmptyValue,
isFormStyle, isSpaceDelimited, isPipeDelimited;
public String baseName, paramName, dataType, datatypeWithEnum, dataFormat, contentType,
collectionFormat, description, unescapedDescription, baseType, defaultValue, enumDefaultValue, enumName, style;
collectionFormat, description, unescapedDescription, baseType, defaultValue, unescapedDefaultValue, enumDefaultValue, enumName, style;

public String nameInLowerCase; // property name in lower case
public String nameInCamelCase; // property name in camel case (e.g. modifiedDate)
Expand Down Expand Up @@ -182,6 +182,7 @@ public CodegenParameter copy() {
output.multipleOf = this.multipleOf;
output.jsonSchema = this.jsonSchema;
output.defaultValue = this.defaultValue;
output.unescapedDefaultValue = this.unescapedDefaultValue;
output.enumDefaultValue = this.enumDefaultValue;
output.example = this.example;
output.examples = this.examples;
Expand Down Expand Up @@ -289,7 +290,7 @@ public int hashCode() {
return Objects.hash(isFormParam, isQueryParam, isPathParam, isHeaderParam, isCookieParam,
isBodyParam, isContainer, isCollectionFormatMulti, isPrimitiveType, isModel, isExplode, baseName,
paramName, dataType, datatypeWithEnum, dataFormat, collectionFormat, description,
unescapedDescription, baseType, containerType, containerTypeMapped, defaultValue,
unescapedDescription, baseType, containerType, containerTypeMapped, defaultValue, unescapedDefaultValue,
enumDefaultValue, enumName, style, isDeepObject, isMatrix, isAllowEmptyValue, example, examples,
isFormStyle, isSpaceDelimited, isPipeDelimited,
jsonSchema, isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal,
Expand Down Expand Up @@ -382,6 +383,7 @@ public boolean equals(Object o) {
Objects.equals(containerType, that.containerType) &&
Objects.equals(containerTypeMapped, that.containerTypeMapped) &&
Objects.equals(defaultValue, that.defaultValue) &&
Objects.equals(unescapedDefaultValue, that.unescapedDefaultValue) &&
Objects.equals(enumDefaultValue, that.enumDefaultValue) &&
Objects.equals(enumName, that.enumName) &&
Objects.equals(style, that.style) &&
Expand Down Expand Up @@ -450,6 +452,7 @@ public String toString() {
sb.append(", containerType='").append(containerType).append('\'');
sb.append(", containerTypeMapped='").append(containerTypeMapped).append('\'');
sb.append(", defaultValue='").append(defaultValue).append('\'');
sb.append(", unescapedDefaultValue='").append(unescapedDefaultValue).append('\'');
sb.append(", enumDefaultValue='").append(enumDefaultValue).append('\'');
sb.append(", enumName='").append(enumName).append('\'');
sb.append(", style='").append(style).append('\'');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
@Getter @Setter
public String unescapedDescription;

/**
* The default value string without escape characters; use this in string literal contexts
* (e.g. inside "..." or """...""") together with an appropriate escaping lambda.
* Unlike {@code defaultValue}, this is never passed through escapeText() or escapeUnsafeCharacters().
*/
@Getter @Setter
public String unescapedDefaultValue;

/**
* maxLength validation for strings, see http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.1
*/
Expand Down Expand Up @@ -992,6 +1000,7 @@ public String toString() {
sb.append(", containerTypeMapped='").append(containerTypeMapped).append('\'');
sb.append(", title='").append(title).append('\'');
sb.append(", unescapedDescription='").append(unescapedDescription).append('\'');
sb.append(", unescapedDefaultValue='").append(unescapedDefaultValue).append('\'');
sb.append(", maxLength=").append(maxLength);
sb.append(", minLength=").append(minLength);
sb.append(", pattern='").append(pattern).append('\'');
Expand Down Expand Up @@ -1179,6 +1188,7 @@ public boolean equals(Object o) {
Objects.equals(containerTypeMapped, that.containerTypeMapped) &&
Objects.equals(title, that.title) &&
Objects.equals(unescapedDescription, that.unescapedDescription) &&
Objects.equals(unescapedDefaultValue, that.unescapedDefaultValue) &&
Objects.equals(maxLength, that.maxLength) &&
Objects.equals(minLength, that.minLength) &&
Objects.equals(pattern, that.pattern) &&
Expand Down Expand Up @@ -1212,7 +1222,7 @@ public int hashCode() {

return Objects.hash(openApiType, baseName, complexType, getter, setter, description,
dataType, datatypeWithEnum, dataFormat, name, min, max, defaultValue,
defaultValueWithParam, baseType, containerType, containerTypeMapped, title, unescapedDescription,
defaultValueWithParam, baseType, containerType, containerTypeMapped, title, unescapedDescription, unescapedDefaultValue,
maxLength, minLength, pattern, example, jsonSchema, minimum, maximum,
exclusiveMinimum, exclusiveMaximum, required, deprecated,
isPrimitiveType, isModel, isContainer, isString, isNumeric,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class CodegenResponse implements IJsonSchemaValidationProperties {
public boolean is4xx;
public boolean is5xx;
public String message;
public String unescapedMessage;
public List<Map<String, Object>> examples;
public String dataType;
public String baseType;
Expand Down Expand Up @@ -109,7 +110,7 @@ public class CodegenResponse implements IJsonSchemaValidationProperties {

@Override
public int hashCode() {
return Objects.hash(headers, code, message, examples, dataType, baseType, containerType, containerTypeMapped, hasHeaders,
return Objects.hash(headers, code, message, unescapedMessage, examples, dataType, baseType, containerType, containerTypeMapped, hasHeaders,
isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal, isByteArray, isBoolean, isDate,
isDateTime, isUuid, isEmail, isPassword, isModel, isFreeFormObject, isAnyType, isDefault, simpleType, primitiveType,
isMap, isOptional, isArray, isBinary, isFile, schema, jsonSchema, vendorExtensions, items, additionalProperties,
Expand Down Expand Up @@ -182,6 +183,7 @@ public boolean equals(Object o) {
Objects.equals(headers, that.headers) &&
Objects.equals(code, that.code) &&
Objects.equals(message, that.message) &&
Objects.equals(unescapedMessage, that.unescapedMessage) &&
Objects.equals(examples, that.examples) &&
Objects.equals(dataType, that.dataType) &&
Objects.equals(baseType, that.baseType) &&
Expand Down Expand Up @@ -582,6 +584,7 @@ public String toString() {
sb.append(", is4xx='").append(is4xx).append('\'');
sb.append(", is5xx='").append(is5xx).append('\'');
sb.append(", message='").append(message).append('\'');
sb.append(", unescapedMessage='").append(unescapedMessage).append('\'');
sb.append(", examples=").append(examples);
sb.append(", dataType='").append(dataType).append('\'');
sb.append(", baseType='").append(baseType).append('\'');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,24 @@ protected ImmutableMap.Builder<String, Lambda> addMustacheLambdas() {
.put("trimLineBreaks", new TrimLineBreaksLambda())
.put("trimWhitespace", new TrimWhitespaceLambda())
.put("trimTrailingWithNewLine", new TrimTrailingWhiteSpaceLambda(true))
.put("trimTrailing", new TrimTrailingWhiteSpaceLambda(false));
.put("trimTrailing", new TrimTrailingWhiteSpaceLambda(false))
// Escapes text for use inside a Markdown table cell or inline text.
// Order matters: \\ and & must be first to avoid double-escaping.
// $ is escaped to prevent LaTeX math mode ($...$) in renderers that support it.
.put("escapeMarkdown", (fragment, writer) -> writer.write(fragment.execute()
.replace("\\", "\\\\")
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("$", "\\$")
.replace("|", "\\|")
.replace("\r\n", " ")
.replace("\n", " ")
.replace("\r", " ")))
.put("collapseNewlines", (fragment, writer) -> writer.write(fragment.execute()
.replace("\r\n", " ")
.replace("\n", " ")
.replace("\r", " ")));
}

private void registerMustacheLambdas() {
Expand Down Expand Up @@ -4315,6 +4332,7 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo

// set the default value
property.defaultValue = toDefaultValue(property, p);
property.unescapedDefaultValue = p.getDefault() != null ? String.valueOf(p.getDefault()) : null;
property.defaultValueWithParam = toDefaultValueWithParam(name, p);

LOGGER.debug("debugging from property return: {}", property);
Expand Down Expand Up @@ -4738,6 +4756,7 @@ public CodegenOperation fromOperation(String path,
}

op.summary = escapeText(operation.getSummary());
op.unescapedSummary = operation.getSummary();
op.unescapedNotes = operation.getDescription();
op.notes = escapeText(operation.getDescription());
op.hasConsumes = false;
Expand Down Expand Up @@ -5092,6 +5111,7 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
}
r.schema = responseSchema;
r.message = escapeText(response.getDescription());
r.unescapedMessage = response.getDescription();

// adding examples to API responses
Map<String, Example> examples = ExamplesUtils.getExamplesFromResponse(openAPI, response);
Expand Down Expand Up @@ -5641,6 +5661,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set<String> imports)

// set default value
codegenParameter.defaultValue = toDefaultParameterValue(codegenProperty, parameterSchema);
codegenParameter.unescapedDefaultValue = parameterSchema.getDefault() != null ? String.valueOf(parameterSchema.getDefault()) : null;

finishUpdatingParameter(codegenParameter, parameter);
return codegenParameter;
Expand Down Expand Up @@ -7543,6 +7564,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set

// set default value
codegenParameter.defaultValue = toDefaultParameterValue(codegenProperty, propertySchema);
codegenParameter.unescapedDefaultValue = propertySchema.getDefault() != null ? String.valueOf(propertySchema.getDefault()) : null;

if (ModelUtils.isFileSchema(ps) && !ModelUtils.isStringSchema(ps)) {
// swagger v2 only, type file
Expand Down Expand Up @@ -8089,6 +8111,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
codegenParameter.baseName = "UNKNOWN_BASE_NAME";
codegenParameter.paramName = "UNKNOWN_PARAM_NAME";
codegenParameter.description = escapeText(body.getDescription());
codegenParameter.unescapedDescription = body.getDescription();
codegenParameter.required = body.getRequired() != null ? body.getRequired() : Boolean.FALSE;
codegenParameter.isBodyParam = Boolean.TRUE;
if (body.getExtensions() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,20 @@ protected void updateModelForObject(CodegenModel m, Schema schema) {
@Override
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
.put("escapeDollar", new EscapeChar("(?<!\\\\)\\$", "\\\\\\$"));
.put("escapeDollar", new EscapeChar("(?<!\\\\)\\$", "\\\\\\$"))
// Replaces each $ with ${'$'} for use inside """...""" triple-quoted Kotlin strings,
// where backslash escapes are not available.
.put("escapeDollarInMultiline", new EscapeChar("\\$", "\\${'\\$'}"))
// Full escaping for raw values going into "..." double-quoted Kotlin strings.
// Handles \, $, " and also literal newline/tab/carriage-return chars.
// \\ replacement must be first to avoid double-escaping subsequent insertions.
.put("escapeInNormalString", (fragment, writer) -> writer.write(fragment.execute()
.replace("\\", "\\\\")
.replace("$", "\\$")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t")));
}

protected interface DataTypeAssigner {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -115,6 +116,7 @@ public class KotlinClientCodegen extends AbstractKotlinCodegen {
public static final String COMPANION_OBJECT = "companionObject";

protected static final String VENDOR_EXTENSION_BASE_NAME_LITERAL = "x-base-name-literal";
private static final Pattern RETROFIT_PATH_PARAM_NAME_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9_-]*");


@Setter protected String dateLibrary = DateLibrary.JAVA8.value;
Expand Down Expand Up @@ -1062,6 +1064,26 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
operation.path = operation.path.substring(1);
}

// For jvm-retrofit2, Retrofit @Path names must match [a-zA-Z][a-zA-Z0-9_-]*.
// When the OpenAPI baseName is invalid (e.g., starts with '$'), replace the
// URL placeholder with the sanitized paramName and mark via vendor extension.
if (JVM_RETROFIT2.equals(getLibrary())) {
for (CodegenParameter param : operation.allParams) {
if (param.isPathParam) {
boolean validRetrofitName = RETROFIT_PATH_PARAM_NAME_PATTERN.matcher(param.baseName).matches();
if (validRetrofitName) {
param.vendorExtensions.put("x-retrofit-path-name", param.baseName);
} else {
param.vendorExtensions.put("x-retrofit-path-name", param.paramName);
operation.path = operation.path.replace(
"{" + param.baseName + "}",
"{" + param.paramName + "}"
);
}
}
}
}

if (JVM_OKHTTP.equals(getLibrary()) || JVM_OKHTTP4.equals(getLibrary())) {
// Ideally we would do content negotiation to choose the best mediatype, but that would be a next step.
// For now we take the first mediatype we can parse and send that.
Expand Down
Loading
Loading