Skip to content

Commit

Permalink
[csharp] Fixes incorrect property name (OpenAPITools#18136)
Browse files Browse the repository at this point in the history
* moved camel case lambda

* renamed camel case lambda

* reverted unintended change

* fixed wrong property names

* restored accidental file deletion

* build samples
  • Loading branch information
devhl-labs authored and zapodot committed Mar 21, 2024
1 parent 5297e7b commit d677aa2
Show file tree
Hide file tree
Showing 158 changed files with 2,108 additions and 1,305 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
protected String enumValueSuffix = "Enum";

protected String sourceFolder = "src";
protected String invalidNamePrefix = "var";
protected static final String invalidParameterNamePrefix = "var";
protected static final String invalidPropertyNamePrefix = "Var";
protected CodegenConstants.ENUM_PROPERTY_NAMING_TYPE enumPropertyNaming = CodegenConstants.ENUM_PROPERTY_NAMING_TYPE.PascalCase;

// TODO: Add option for test folder output location. Nice to allow e.g. ./test instead of ./src.
Expand Down Expand Up @@ -461,7 +462,9 @@ protected ImmutableMap.Builder<String, Lambda> addMustacheLambdas() {
.put("pasteOnce", new PasteLambda(copyLambda, true, true, true, true))
.put("pasteLine", new PasteLambda(copyLambda, true, true, false, false))
.put("uniqueLines", new UniqueLambda("\n", false))
.put("unique", new UniqueLambda("\n", true));
.put("unique", new UniqueLambda("\n", true))
.put("camel_case", new CamelCaseLambda())
.put("escape_reserved_word", new EscapeKeywordLambda((val) -> this.escapeKeyword(val)));
}

@Override
Expand Down Expand Up @@ -664,18 +667,13 @@ protected void removePropertiesDeclaredInComposedTypes(Map<String, ModelsMap> ob
}

private String patchPropertyName(CodegenModel model, String value) {
// the casing will be wrong if we just set the name to escapeReservedWord
// if we try to fix it with camelize, underscores get stripped out
// so test if the name was escaped and then replace var with Var
String tmpPropertyName = escapeReservedWord(model, value);
if (!value.equals(tmpPropertyName) || value.startsWith(this.invalidNamePrefix)) {
value = tmpPropertyName;
String firstCharacter = value.substring(0, 1);
value = value.substring(1);
value = firstCharacter.toUpperCase(Locale.ROOT) + value;
String name = escapeReservedWord(model, value);

if (name.startsWith(AbstractCSharpCodegen.invalidParameterNamePrefix)) {
name = AbstractCSharpCodegen.invalidPropertyNamePrefix + name.substring(AbstractCSharpCodegen.invalidParameterNamePrefix.length());
}

return value;
return name;
}

private void patchPropertyVendorExtensions(CodegenProperty property) {
Expand All @@ -700,7 +698,6 @@ protected void patchProperty(Map<String, CodegenModel> enumRefs, CodegenModel mo

patchPropertyVendorExtensions(property);

String tmpPropertyName = escapeReservedWord(model, property.name);
property.name = patchPropertyName(model, property.name);

String[] nestedTypes = { "List", "Collection", "ICollection", "Dictionary" };
Expand Down Expand Up @@ -1308,23 +1305,24 @@ public String toParamName(String name) {
public String escapeReservedWord(CodegenModel model, String name) {
name = this.escapeReservedWord(name);

return name.equalsIgnoreCase(model.getClassname())
? this.invalidNamePrefix + camelize(name)
return name.equals(model.getClassname())
? AbstractCSharpCodegen.invalidParameterNamePrefix + camelize(name)
: name;
}

@Override
public String escapeReservedWord(String name) {
if (reservedWords().contains(name) ||
reservedWords().contains(name.toLowerCase(Locale.ROOT)) ||
reservedWords().contains(camelize(sanitizeName(name))) ||
isReservedWord(name) ||
if (isReservedWord(name) ||
name.matches("^\\d.*")) {
name = this.invalidNamePrefix + camelize(name);
name = AbstractCSharpCodegen.invalidParameterNamePrefix + camelize(name);
}
return name;
}

public String escapeKeyword(String value) {
return isReservedWord(value) ? "@" + value : value;
}

/**
* Return the example value of the property
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.templating.mustache;

import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import org.openapitools.codegen.utils.CamelizeOption;

import java.io.IOException;
import java.io.Writer;

import static org.openapitools.codegen.utils.StringUtils.camelize;

/**
* Converts text in a fragment to camelCase.
*
* Register:
* <pre>
* additionalProperties.put("camelcase", new CamelCaseLambda());
* </pre>
*
* Use:
* <pre>
* {{#camelcase}}{{name}}{{/camelcase}}
* </pre>
*/
public class CamelCaseLambda implements Mustache.Lambda {
public CamelCaseLambda() {
}

@Override
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
String text = fragment.execute();
text = camelize(text, CamelizeOption.LOWERCASE_FIRST_CHAR);
writer.write(text);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
* Copyright 2018 SmartBear Software
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.openapitools.codegen.templating.mustache;

import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;

import java.io.IOException;
import java.io.Writer;
import java.util.function.UnaryOperator;

/**
* Converts text in a fragment to escape_keyword.
*
* Register:
* <pre>
* additionalProperties.put("escape_keyword", new EscapeKeywordLambda((val) -> this.escapeKeyword(val))));
* </pre>
*
* Use:
* <pre>
* {{#escape_keyword}}{{name}}{{/escape_keyword}}
* </pre>
*/
public class EscapeKeywordLambda implements Mustache.Lambda {
private UnaryOperator<String> callback;

public EscapeKeywordLambda(final UnaryOperator<String> callback) {
this.callback = callback;
}

@Override
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
String text = fragment.execute();

text = this.callback.apply(text);

writer.write(text);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ namespace {{packageName}}.Test.{{apiPackage}}
[Fact]
public void ConfigureApiWithAClientTest()
{
{{#apiInfo}}{{#apis}}var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingConfigureWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}}
{{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}

{{/-last}}{{/apis}}{{/apiInfo}}
}
Expand All @@ -178,8 +178,8 @@ namespace {{packageName}}.Test.{{apiPackage}}
[Fact]
public void ConfigureApiWithoutAClientTest()
{
{{#apiInfo}}{{#apis}}var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingConfigureWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}}
{{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}

{{/-last}}{{/apis}}{{/apiInfo}}
}
Expand All @@ -190,8 +190,8 @@ namespace {{packageName}}.Test.{{apiPackage}}
[Fact]
public void AddApiWithAClientTest()
{
{{#apiInfo}}{{#apis}}var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingAddWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}}
{{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}

{{/-last}}{{/apis}}{{/apiInfo}}
}
Expand All @@ -202,8 +202,8 @@ namespace {{packageName}}.Test.{{apiPackage}}
[Fact]
public void AddApiWithoutAClientTest()
{
{{#apiInfo}}{{#apis}}var {{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}} = _hostUsingAddWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}.HttpClient.BaseAddress != null);{{^-last}}
{{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>();
Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}}

{{/-last}}{{/apis}}{{/apiInfo}}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
/// <exception cref="JsonException"></exception>
public override {{classname}} Read(ref Utf8JsonReader utf8JsonReader, Type typeToConvert, JsonSerializerOptions jsonSerializerOptions)
{
{{#lambda.trimTrailingWithNewLine}}
{{#lambda.trimLineBreaks}}
int currentDepth = utf8JsonReader.CurrentDepth;

Expand Down Expand Up @@ -291,22 +292,24 @@
{{^composedSchemas.oneOf}}
{{^required}}
{{#model.composedSchemas.anyOf}}
Option<{{baseType}}{{>NullConditionalProperty}}> {{#lambda.camelcase_sanitize_param}}{{baseType}}{{/lambda.camelcase_sanitize_param}}ParsedValue = {{#lambda.camelcase_sanitize_param}}{{baseType}}{{/lambda.camelcase_sanitize_param}} == null
Option<{{baseType}}{{>NullConditionalProperty}}> {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}ParsedValue = {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} == null
? default
: new Option<{{baseType}}{{>NullConditionalProperty}}>({{#lambda.camelcase_sanitize_param}}{{baseType}}{{/lambda.camelcase_sanitize_param}});
: new Option<{{baseType}}{{>NullConditionalProperty}}>({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}});
{{/model.composedSchemas.anyOf}}
{{#-last}}

{{/-last}}
{{/required}}
return new {{classname}}({{#lambda.joinWithComma}}{{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{baseType}}{{/lambda.camelcase_sanitize_param}}ParsedValue{{#required}}.Value{{^isNullable}}{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}.Value{{nrt!}}{{^isNullable}}{{#vendorExtensions.x-is-value-type}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/allVars}}{{/lambda.joinWithComma}});
return new {{classname}}({{#lambda.joinWithComma}}{{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}ParsedValue{{#required}}.Value{{^isNullable}}{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}.Value{{nrt!}}{{^isNullable}}{{#vendorExtensions.x-is-value-type}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/allVars}}{{/lambda.joinWithComma}});
{{/composedSchemas.oneOf}}
{{^model.discriminator}}
{{#composedSchemas}}
{{#oneOf}}
{{^vendorExtensions.x-duplicated-data-type}}
if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} != null)
return new {{classname}}({{#lambda.joinWithComma}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}ParsedValue{{/required}} {{/allVars}}{{/lambda.joinWithComma}});

{{/vendorExtensions.x-duplicated-data-type}}
{{#-last}}
throw new JsonException();
{{/-last}}
Expand All @@ -315,6 +318,7 @@
{{/model.discriminator}}
{{/vendorExtensions.x-duplicated-data-type}}
{{/lambda.trimLineBreaks}}
{{/lambda.trimTrailingWithNewLine}}
}

/// <summary>
Expand All @@ -331,22 +335,24 @@

{{#model.discriminator}}
{{#model.hasDiscriminatorWithNonEmptyMapping}}
{{#mappedModels}}
if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{model.classname}} != null) {
{{model.classname}}JsonConverter {{#lambda.camelcase_sanitize_param}}{{model.classname}}JsonConverter{{/lambda.camelcase_sanitize_param}} = ({{model.classname}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{model.classname}}.GetType()));
{{#lambda.camelcase_sanitize_param}}{{model.classname}}JsonConverter{{/lambda.camelcase_sanitize_param}}.WriteProperties(ref writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{model.classname}}, jsonSerializerOptions);
{{#composedSchemas.oneOf}}
{{^vendorExtensions.x-duplicated-data-type}}
if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) {
{{baseType}}JsonConverter {{#lambda.camelcase_sanitize_param}}{{baseType}}JsonConverter{{/lambda.camelcase_sanitize_param}} = ({{baseType}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}.GetType()));
{{#lambda.camelcase_sanitize_param}}{{baseType}}JsonConverter{{/lambda.camelcase_sanitize_param}}.WriteProperties(ref writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions);
}

{{/mappedModels}}
{{/vendorExtensions.x-duplicated-data-type}}
{{/composedSchemas.oneOf}}
{{/model.hasDiscriminatorWithNonEmptyMapping}}
{{/model.discriminator}}
{{^model.discriminator}}
{{#composedSchemas}}
{{#anyOf}}
if ({{#lambda.joinWithAmpersand}}{{^required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{datatypeWithEnum}}Option.IsSet {{/required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{datatypeWithEnum}}{{^required}}Option.Value{{/required}} != null{{/lambda.joinWithAmpersand}})
if ({{#lambda.joinWithAmpersand}}{{^required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.IsSet {{/required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}} != null{{/lambda.joinWithAmpersand}})
{
{{datatypeWithEnum}}JsonConverter {{datatypeWithEnum}}JsonConverter = ({{datatypeWithEnum}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{datatypeWithEnum}}{{^required}}Option.Value{{/required}}.GetType()));
{{datatypeWithEnum}}JsonConverter.WriteProperties(ref writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{datatypeWithEnum}}{{^required}}Option.Value{{/required}}, jsonSerializerOptions);
{{datatypeWithEnum}}JsonConverter {{datatypeWithEnum}}JsonConverter = ({{datatypeWithEnum}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}}.GetType()));
{{datatypeWithEnum}}JsonConverter.WriteProperties(ref writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}}, jsonSerializerOptions);
}

{{/anyOf}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{#model.allVars}}{{^required}}Option<{{/required}}{{{datatypeWithEnum}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#defaultValue}} = {{^required}}default{{/required}}{{#required}}{{^isDateTime}}{{#isString}}{{^isEnum}}@{{/isEnum}}{{/isString}}{{{.}}}{{/isDateTime}}{{#isDateTime}}default{{/isDateTime}}{{/required}}{{/defaultValue}}{{^defaultValue}}{{#lambda.first}}{{#isNullable}} = default {{/isNullable}}{{^required}} = default {{/required}}{{/lambda.first}}{{/defaultValue}} {{/model.allVars}}
{{#model.allVars}}{{^required}}Option<{{/required}}{{{datatypeWithEnum}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}{{#defaultValue}} = {{^required}}default{{/required}}{{#required}}{{^isDateTime}}{{#isString}}{{^isEnum}}@{{/isEnum}}{{/isString}}{{{.}}}{{/isDateTime}}{{#isDateTime}}default{{/isDateTime}}{{/required}}{{/defaultValue}}{{^defaultValue}}{{#lambda.first}}{{#isNullable}} = default {{/isNullable}}{{^required}} = default {{/required}}{{/lambda.first}}{{/defaultValue}} {{/model.allVars}}
Loading

0 comments on commit d677aa2

Please sign in to comment.