Skip to content

Commit

Permalink
Fix Golang pattern validation with regex fails on commas #20079
Browse files Browse the repository at this point in the history
  • Loading branch information
mlebihan committed Dec 23, 2024
1 parent ff0fe26 commit aa7aba8
Show file tree
Hide file tree
Showing 88 changed files with 8,104 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ public class CodegenModel implements IJsonSchemaValidationProperties {
private String minimum;
private String maximum;
private String pattern;
private String originalPattern;
private Number multipleOf;
private CodegenProperty items;
private CodegenProperty additionalProperties;
Expand Down Expand Up @@ -363,7 +364,6 @@ public String getDiscriminatorName() {
return discriminator == null ? null : discriminator.getPropertyName();
}


@Override
public String getPattern() {
return pattern;
Expand All @@ -374,6 +374,16 @@ public void setPattern(String pattern) {
this.pattern = pattern;
}

@Override
public String getOriginalPattern() {
return originalPattern;
}

@Override
public void setOriginalPattern(String originalPattern) {
this.originalPattern = originalPattern;
}

@Override
public String getMaximum() {
return maximum;
Expand Down Expand Up @@ -966,6 +976,7 @@ public boolean equals(Object o) {
Objects.equals(getMinimum(), that.getMinimum()) &&
Objects.equals(getMaximum(), that.getMaximum()) &&
Objects.equals(getPattern(), that.getPattern()) &&
Objects.equals(getOriginalPattern(), that.getOriginalPattern()) &&
Objects.equals(getItems(), that.getItems()) &&
Objects.equals(getAdditionalProperties(), that.getAdditionalProperties()) &&
Objects.equals(getIsModel(), that.getIsModel()) &&
Expand All @@ -986,7 +997,7 @@ public int hashCode() {
hasChildren, isMap, isOptional, isDeprecated, hasReadOnly, hasOnlyReadOnly, getExternalDocumentation(), getVendorExtensions(),
getAdditionalPropertiesType(), getMaxProperties(), getMinProperties(), getUniqueItems(), getMaxItems(),
getMinItems(), getMaxLength(), getMinLength(), getExclusiveMinimum(), getExclusiveMaximum(), getMinimum(),
getMaximum(), getPattern(), getMultipleOf(), getItems(), getAdditionalProperties(), getIsModel(),
getMaximum(), getPattern(), getOriginalPattern(), getMultipleOf(), getItems(), getAdditionalProperties(), getIsModel(),
getAdditionalPropertiesIsAnyType(), hasDiscriminatorWithNonEmptyMapping,
isAnyType, getComposedSchemas(), hasMultipleTypes, isDecimal, isUuid, isUri, requiredVarsMap, ref,
uniqueItemsBoolean, schemaIsFromAdditionalProperties, isBooleanSchemaTrue, isBooleanSchemaFalse,
Expand Down Expand Up @@ -1079,6 +1090,7 @@ public String toString() {
sb.append(", minimum='").append(minimum).append('\'');
sb.append(", maximum='").append(maximum).append('\'');
sb.append(", pattern='").append(pattern).append('\'');
sb.append(", originalPattern='").append(originalPattern).append('\'');
sb.append(", multipleOf='").append(multipleOf).append('\'');
sb.append(", items='").append(items).append('\'');
sb.append(", additionalProperties='").append(additionalProperties).append('\'');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ public class CodegenParameter implements IJsonSchemaValidationProperties {
* See <a href="https://web.archive.org/web/20240502205731/https://json-schema.org/draft/2020-12/json-schema-validation#name-pattern">JSON Schema Validation Spec, Section 6.3.3</a>
*/
public String pattern;

/** Original pattern validation for strings, kept unchanged from OpenAPI schema */
public String originalPattern;

/**
* See <a href="https://web.archive.org/web/20240502205731/https://json-schema.org/draft/2020-12/json-schema-validation#name-maxitems">JSON Schema Validation Spec, Section 6.4.1</a>
*/
Expand Down Expand Up @@ -173,6 +177,7 @@ public CodegenParameter copy() {
output.maxLength = this.maxLength;
output.minLength = this.minLength;
output.pattern = this.pattern;
output.originalPattern = this.originalPattern;
output.maxItems = this.maxItems;
output.minItems = this.minItems;
output.uniqueItems = this.uniqueItems;
Expand Down Expand Up @@ -291,7 +296,7 @@ public int hashCode() {
items, mostInnerItems, additionalProperties, vars, requiredVars, vendorExtensions, hasValidation,
getMaxProperties(), getMinProperties(), isNullable, isDeprecated, required, getMaximum(),
getExclusiveMaximum(), getMinimum(), getExclusiveMinimum(), getMaxLength(), getMinLength(),
getPattern(), getMaxItems(), getMinItems(), getUniqueItems(), contentType, multipleOf, isNull,isVoid,
getPattern(), getOriginalPattern(), getMaxItems(), getMinItems(), getUniqueItems(), contentType, multipleOf, isNull,isVoid,
additionalPropertiesIsAnyType, hasVars, hasRequired, isShort, isUnboundedInteger,
hasDiscriminatorWithNonEmptyMapping, composedSchemas, hasMultipleTypes, schema, content,
requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties,
Expand Down Expand Up @@ -398,6 +403,7 @@ public boolean equals(Object o) {
Objects.equals(getMaxLength(), that.getMaxLength()) &&
Objects.equals(getMinLength(), that.getMinLength()) &&
Objects.equals(getPattern(), that.getPattern()) &&
Objects.equals(getOriginalPattern(), that.getOriginalPattern()) &&
Objects.equals(getMaxItems(), that.getMaxItems()) &&
Objects.equals(getMinItems(), that.getMinItems()) &&
Objects.equals(contentType, that.contentType) &&
Expand Down Expand Up @@ -496,6 +502,7 @@ public String toString() {
sb.append(", maxLength=").append(maxLength);
sb.append(", minLength=").append(minLength);
sb.append(", pattern='").append(pattern).append('\'');
sb.append(", originalPattern='").append(originalPattern).append('\'');
sb.append(", maxItems=").append(maxItems);
sb.append(", minItems=").append(minItems);
sb.append(", uniqueItems=").append(uniqueItems);
Expand Down Expand Up @@ -584,6 +591,16 @@ public void setPattern(String pattern) {
this.pattern = pattern;
}

@Override
public String getOriginalPattern() {
return originalPattern;
}

@Override
public void setOriginalPattern(String originalPattern) {
this.originalPattern = originalPattern;
}

@Override
public String getMaximum() {
return maximum;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
* pattern validation for strings, see http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.3
*/
public String pattern;

/** Original pattern validation for strings, kept unchanged from OpenAPI schema */
public String originalPattern;

/**
* A free-form property to include an example of an instance for this schema.
*/
Expand Down Expand Up @@ -382,6 +386,16 @@ public void setPattern(String pattern) {
this.pattern = pattern;
}

@Override
public String getOriginalPattern() {
return originalPattern;
}

@Override
public void setOriginalPattern(String originalPattern) {
this.originalPattern = originalPattern;
}

@Override
public String getMinimum() {
return minimum;
Expand Down Expand Up @@ -986,6 +1000,7 @@ public String toString() {
sb.append(", maxLength=").append(maxLength);
sb.append(", minLength=").append(minLength);
sb.append(", pattern='").append(pattern).append('\'');
sb.append(", originalPattern='").append(originalPattern).append('\'');
sb.append(", example='").append(example).append('\'');
sb.append(", jsonSchema='").append(jsonSchema).append('\'');
sb.append(", minimum='").append(minimum).append('\'');
Expand Down Expand Up @@ -1175,6 +1190,7 @@ public boolean equals(Object o) {
Objects.equals(maxLength, that.maxLength) &&
Objects.equals(minLength, that.minLength) &&
Objects.equals(pattern, that.pattern) &&
Objects.equals(originalPattern, that.originalPattern) &&
Objects.equals(example, that.example) &&
Objects.equals(jsonSchema, that.jsonSchema) &&
Objects.equals(minimum, that.minimum) &&
Expand Down Expand Up @@ -1206,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,
maxLength, minLength, pattern, example, jsonSchema, minimum, maximum,
maxLength, minLength, pattern, originalPattern, example, jsonSchema, minimum, maximum,
exclusiveMinimum, exclusiveMaximum, required, deprecated,
hasMoreNonReadOnly, isPrimitiveType, isModel, isContainer, isString, isNumeric,
isInteger, isLong, isNumber, isFloat, isDouble, isDecimal, isByteArray, isBinary, isFile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public class CodegenResponse implements IJsonSchemaValidationProperties {
private String minimum;
private String maximum;
public String pattern;
public String originalPattern;
public Number multipleOf;
public CodegenProperty items;
public CodegenProperty additionalProperties;
Expand Down Expand Up @@ -113,7 +114,7 @@ public int hashCode() {
isMap, isOptional, isArray, isBinary, isFile, schema, jsonSchema, vendorExtensions, items, additionalProperties,
vars, requiredVars, isNull, isVoid, hasValidation, isShort, isUnboundedInteger,
getMaxProperties(), getMinProperties(), uniqueItems, getMaxItems(), getMinItems(), getMaxLength(),
getMinLength(), exclusiveMinimum, exclusiveMaximum, getMinimum(), getMaximum(), getPattern(),
getMinLength(), exclusiveMinimum, exclusiveMaximum, getMinimum(), getMaximum(), getPattern(), getOriginalPattern(),
is1xx, is2xx, is3xx, is4xx, is5xx, additionalPropertiesIsAnyType, hasVars, hasRequired,
hasDiscriminatorWithNonEmptyMapping, composedSchemas, hasMultipleTypes, responseHeaders, content,
requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties);
Expand Down Expand Up @@ -200,6 +201,7 @@ public boolean equals(Object o) {
Objects.equals(getMinimum(), that.getMinimum()) &&
Objects.equals(getMaximum(), that.getMaximum()) &&
Objects.equals(getPattern(), that.getPattern()) &&
Objects.equals(getOriginalPattern(), that.getOriginalPattern()) &&
Objects.equals(getMultipleOf(), that.getMultipleOf());

}
Expand Down Expand Up @@ -264,6 +266,16 @@ public void setPattern(String pattern) {
this.pattern = pattern;
}

@Override
public String getOriginalPattern() {
return originalPattern;
}

@Override
public void setOriginalPattern(String originalPattern) {
this.originalPattern = originalPattern;
}

@Override
public String getMaximum() {
return maximum;
Expand Down Expand Up @@ -616,6 +628,7 @@ public String toString() {
sb.append(", minimum='").append(minimum).append('\'');
sb.append(", maximum='").append(maximum).append('\'');
sb.append(", pattern='").append(pattern).append('\'');
sb.append(", originalPattern='").append(originalPattern).append('\'');
sb.append(", multipleOf='").append(multipleOf).append('\'');
sb.append(", items='").append(items).append('\'');
sb.append(", additionalProperties='").append(additionalProperties).append('\'');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4893,6 +4893,7 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
ModelUtils.syncValidationProperties(responseSchema, r);
if (responseSchema.getPattern() != null) {
r.setPattern(toRegularExpression(responseSchema.getPattern()));
r.setOriginalPattern(responseSchema.getPattern());
}

CodegenProperty cp = fromProperty("response", responseSchema, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public interface IJsonSchemaValidationProperties {

void setPattern(String pattern);

String getOriginalPattern();

void setOriginalPattern(String originalPattern);

String getMaximum();

void setMaximum(String maximum);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import java.io.File;
import java.util.*;
import java.util.regex.Matcher;

import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
import static org.openapitools.codegen.utils.StringUtils.camelize;
Expand All @@ -38,6 +39,7 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege

private final Logger LOGGER = LoggerFactory.getLogger(AbstractGoCodegen.class);
private static final String NUMERIC_ENUM_PREFIX = "_";
private static final String X_GO_CUSTOM_TAG = "x-go-custom-tag";

@Setter
protected boolean withGoCodegenComment = false;
Expand Down Expand Up @@ -785,9 +787,26 @@ public ModelsMap postProcessModels(ModelsMap objs) {
}

if (cp.pattern != null) {
cp.vendorExtensions.put("x-go-custom-tag", "validate:\"regexp=" +
cp.pattern.replace("\\", "\\\\").replaceAll("^/|/$", "") +
"\"");
String regexp = String.format(Locale.getDefault(), "regexp=%s", cp.originalPattern);
regexp = regexp.replace("\\", "\\\\");

// Replace backtick by \\x60, if found
if (regexp.contains("`")) {
regexp = regexp.replace("`", "\\x60");
}

// Escape comma
if (regexp.contains(",")) {
regexp = regexp.replace(",", "\\\\,");
}

// as the double quotes will be included in a string, ".......".....", they should be escaped once: ".....\"...."
if (regexp.contains("\"")) {
regexp = regexp.replace("\"", "\\\",");
}

String validate = String.format(Locale.getDefault(), "validate:\"%s\"", regexp);
cp.vendorExtensions.put(X_GO_CUSTOM_TAG, validate);
}

// construct data tag in the template: x-go-datatag
Expand All @@ -813,8 +832,8 @@ public ModelsMap postProcessModels(ModelsMap objs) {
}

// {{#vendorExtensions.x-go-custom-tag}} {{{.}}}{{/vendorExtensions.x-go-custom-tag}}
if (StringUtils.isNotEmpty(String.valueOf(cp.vendorExtensions.getOrDefault("x-go-custom-tag", "")))) {
goDataTag += " " + cp.vendorExtensions.get("x-go-custom-tag");
if (StringUtils.isNotEmpty(String.valueOf(cp.vendorExtensions.getOrDefault(X_GO_CUSTOM_TAG, "")))) {
goDataTag += " " + cp.vendorExtensions.get(X_GO_CUSTOM_TAG);
}

// if it contains backtick, wrap with " instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,7 @@ public ExtendedCodegenModel(CodegenModel cm) {
this.setMinimum(cm.getMinimum());
this.setMaximum(cm.getMaximum());
this.setPattern(cm.getPattern());
this.setOriginalPattern(cm.getOriginalPattern());
this.setMultipleOf(cm.getMultipleOf());
this.setItems(cm.getItems());
this.setAdditionalProperties(cm.getAdditionalProperties());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1838,6 +1838,8 @@ public static void syncValidationProperties(Schema schema, IJsonSchemaValidation
String pattern = schema.getPattern();
if (pattern != null) vSB.withPattern();

String originalPattern = pattern;

BigDecimal multipleOf = schema.getMultipleOf();
if (multipleOf != null) vSB.withMultipleOf();

Expand Down Expand Up @@ -1867,7 +1869,7 @@ public static void syncValidationProperties(Schema schema, IJsonSchemaValidation
logWarnMessagesForIneffectiveValidations(new LinkedHashSet(setValidations), schema, SchemaValidations.OBJECT_VALIDATIONS);
} else if (isStringSchema(schema)) {
if (minLength != null || maxLength != null || pattern != null)
setStringValidations(minLength, maxLength, pattern, target);
setStringValidations(minLength, maxLength, pattern, originalPattern, target);
if (isDecimalSchema(schema)) {
if (multipleOf != null || minimum != null || maximum != null || exclusiveMinimum != null || exclusiveMaximum != null)
setNumericValidations(schema, multipleOf, minimum, maximum, exclusiveMinimum, exclusiveMaximum, target);
Expand All @@ -1886,7 +1888,7 @@ public static void syncValidationProperties(Schema schema, IJsonSchemaValidation
// anyType can have any validations set on it
setArrayValidations(minItems, maxItems, uniqueItems, target);
setObjectValidations(minProperties, maxProperties, target);
setStringValidations(minLength, maxLength, pattern, target);
setStringValidations(minLength, maxLength, pattern, originalPattern, target);
setNumericValidations(schema, multipleOf, minimum, maximum, exclusiveMinimum, exclusiveMaximum, target);
}

Expand All @@ -1906,10 +1908,11 @@ private static void setObjectValidations(Integer minProperties, Integer maxPrope
if (maxProperties != null) target.setMaxProperties(maxProperties);
}

private static void setStringValidations(Integer minLength, Integer maxLength, String pattern, IJsonSchemaValidationProperties target) {
private static void setStringValidations(Integer minLength, Integer maxLength, String pattern, String originalPattern, IJsonSchemaValidationProperties target) {
if (minLength != null) target.setMinLength(minLength);
if (maxLength != null) target.setMaxLength(maxLength);
if (pattern != null) target.setPattern(pattern);
if (originalPattern != null) target.setOriginalPattern(originalPattern);
}

private static void setNumericValidations(Schema schema, BigDecimal multipleOf, BigDecimal minimum, BigDecimal maximum, Boolean exclusiveMinimum, Boolean exclusiveMaximum, IJsonSchemaValidationProperties target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,16 @@ func (dst *{{classname}}) UnmarshalJSON(data []byte) error {
} else if match == 1 {
return nil // exactly one match
} else { // no match
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
{{#oneOf}}
if err != nil {
return fmt.Errorf("data failed to match schemas in oneOf({{classname}}): %v", err)
} else {
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
}
{{/oneOf}}
{{^oneOf}}
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
{{/oneOf}}
}
{{/discriminator}}
{{/useOneOfDiscriminatorLookup}}
Expand Down Expand Up @@ -114,7 +123,16 @@ func (dst *{{classname}}) UnmarshalJSON(data []byte) error {
} else if match == 1 {
return nil // exactly one match
} else { // no match
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
{{#oneOf}}
if err != nil {
return fmt.Errorf("data failed to match schemas in oneOf({{classname}}): %v", err)
} else {
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
}
{{/oneOf}}
{{^oneOf}}
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
{{/oneOf}}
}
{{/useOneOfDiscriminatorLookup}}
}
Expand Down
Loading

0 comments on commit aa7aba8

Please sign in to comment.