Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8692092
Stream mustache example lambdas
wilx Jun 28, 2026
7d86dc3
Add Spring regression test for issue 23849
wilx Jun 28, 2026
71c8a12
Fix OOM by streaming generated examples and specs
wilx Jun 29, 2026
9819ca8
Fix JetBrains HTTP client inline examples
wilx Jun 29, 2026
7651ac2
SpringCodegenTest: Disable OOM test for now.
wilx Jun 29, 2026
20d50b2
Fix streaming example rendering
wilx Jun 29, 2026
260d7fb
Use example lambda in remaining templates
wilx Jun 29, 2026
85d73f9
Regenerate index.html.
wilx Jun 29, 2026
e53384d
Refine JetBrains HTTP request examples
wilx Jun 29, 2026
cd1861e
Fix blank lines.
wilx Jun 29, 2026
9e6fcf2
Escape streamed examples in annotations
wilx Jun 29, 2026
fbea63f
Escape streamed examples in Postman JSON
wilx Jun 29, 2026
c300777
Write streamed templates atomically
wilx Jun 29, 2026
79dc323
Guard C# Functions doc examples
wilx Jun 29, 2026
51b5a40
Include JSON examples in parameter equality
wilx Jun 29, 2026
aeaa7d2
Stream Kotlin Spring line break removal lambda
wilx Jun 29, 2026
2365226
Stream Kotlin Spring string escape lambda
wilx Jun 29, 2026
1319521
Escape GraphQL resolver examples
wilx Jun 29, 2026
9fd6442
Escape C# Functions controller examples
wilx Jun 29, 2026
387d899
Escape ASP.NET Core controller examples
wilx Jun 29, 2026
ba55b0c
Preserve F# Giraffe streamed examples
wilx Jun 29, 2026
3bedd19
Avoid repeated prefix deletion in unique lambda
wilx Jun 29, 2026
342df06
Compare minimal-update files with Commons IO
wilx Jun 29, 2026
f8d3344
Use unique temp files for template writes
wilx Jun 29, 2026
ddc8245
Stream HTML escaping lambda output
wilx Jun 29, 2026
341142e
Escape static HTML response examples
wilx Jun 29, 2026
00d3a48
Avoid extra Postman partial newline
wilx Jun 29, 2026
0174836
Avoid hashing JSON parameter examples
wilx Jun 29, 2026
3cc33b2
Update modules/openapi-generator/src/main/java/org/openapitools/codeg…
wilx Jun 29, 2026
fd590a6
Include JSON examples in property equality
wilx Jun 29, 2026
cef95b7
Render GraphQL examples as typed literals
wilx Jun 29, 2026
b516bf6
Escape F# Giraffe example string literals
wilx Jun 29, 2026
29170d5
Use parameter example setter in generators
wilx Jun 29, 2026
edb018d
Fix HTML escape lambda usage docs
wilx Jun 29, 2026
45192b1
Document HTML escape lambda context
wilx Jun 29, 2026
f7a8c49
Preserve scalar example rendering
wilx Jun 29, 2026
39145da
Make JSON output tests line-ending neutral
wilx Jun 30, 2026
c812434
Pin Rust Axum time dependency below 0.3.52
wilx Jun 30, 2026
41551f2
Pin Rust Salvo time dependency below 0.3.52
wilx Jun 30, 2026
4abf208
Document streaming example template rendering
wilx Jun 30, 2026
3a9e23b
Remove stale C# Functions example fallback
wilx Jun 30, 2026
815295e
Revert "SpringCodegenTest: Disable OOM test for now."
wilx Jun 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
17 changes: 17 additions & 0 deletions docs/templating.md
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,16 @@ Supporting files can either be processed through the templating engine or copied
More variables can be found [here](https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenProperty.java).
### Rendering Examples
When rendering `example` values from `CodegenProperty` or `CodegenParameter` in Mustache templates, use `lambdaExample` instead of direct `example` interpolation:
```mustache
{{#hasExample}}{{#lambdaExample}}{{/lambdaExample}}{{/hasExample}}
```
The `example` field is still present for compatibility and scalar example values, but it is not a complete rendering API for templates. The `lambdaExample` section streams object and array examples as JSON while preserving scalar example rendering. This is a context-specific lambda on example-bearing objects, not an entry in the global `lambda` map. When embedding an example in a language string, JSON string, or HTML text context, wrap `lambdaExample` in the appropriate escaping lambda for that context, such as `lambda.escapeJavaString`, `lambda.escapeJsonString`, or `lambdaEscapeHtml`.
## Mustache Lambdas
Expand All @@ -829,10 +839,17 @@ Many generators (*those extending DefaultCodegen*) come with a small set of lamb
- `forwardslash` - Replaces all occurrences of `\/`, `\` and `//` in a fragment by `/`.
- `backslash` - Replaces all occurrences `/` in a fragment by `\`.
- `doublequote` - Prepends `"` to the beginning and appends `"` to the end of a fragment.
- `escapeJavaString` - Escapes fragment output for use inside Java-compatible string literals.
- `escapeJsonString` - Escapes fragment output for use inside JSON string values.
- `indented` - Prepends 4 spaces indention from second line of a fragment on. First line will be indented by Mustache.
- `indented_8` - Prepends 8 spaces indention from second line of a fragment on. First line will be indented by Mustache.
- `indented_12` - Prepends 12 spaces indention from second line of a fragment on. First line will be indented by Mustache.
- `indented_16` -Prepends 16 spaces indention from second line of a fragment on. First line will be indented by Mustache.
- `trim` - Removes leading and trailing whitespace from a fragment.
- `trimLineBreaks` - Replaces duplicate line breaks in a fragment with a single line break.
- `trimWhitespace` - Replaces duplicate whitespace in a fragment with a single space.
- `trimTrailingWithNewLine` - Removes trailing whitespace from a fragment and appends a line break.
- `trimTrailing` - Removes trailing whitespace from a fragment.
Some generators provide additional lambdas. Lambda is invoked by `lambda.[lambda name]` expression. For example: `{{#lambda.lowercase}}FRAGMENT TO LOWERCASE{{/lambda.lowercase}}` to lower case text between `lambda.lowercase`.
Expand Down
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ OPTIONS
variable templating of servers.
--skip-operation-example
Skip examples defined in operations to avoid out of memory errors.
Skip examples defined in operations.
--skip-validate-spec
Skips the default behavior of validating an input specification.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Locale;
Expand Down Expand Up @@ -67,6 +68,20 @@ default boolean handlesFile(String filename) {
String compileTemplate(TemplatingExecutor executor, Map<String, Object> bundle,
String templateFile) throws IOException;

/**
* Writes a compiled template directly to a writer.
*
* @param executor From where we can fetch the templates content (e.g. an instance of DefaultGenerator)
* @param bundle The map of values to pass to the template
* @param templateFile The name of the template (e.g. model.mustache )
* @param writer Destination writer for the processed template result
* @throws IOException an error occurred in the template processing
*/
default void writeTemplate(TemplatingExecutor executor, Map<String, Object> bundle,
String templateFile, Writer writer) throws IOException {
writer.write(compileTemplate(executor, bundle, templateFile));
}

/**
* Determines whether the template file with supported extensions exists. This may be on the filesystem,
* external filesystem, or classpath (implementation is up to TemplatingGenerator).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,
public static final String REMOVE_OPERATION_ID_PREFIX_COUNT_DESC = "Count of delimiter for the prefix. Use -1 for last Default: 1";

public static final String SKIP_OPERATION_EXAMPLE = "skipOperationExample";
public static final String SKIP_OPERATION_EXAMPLE_DESC = "Skip examples defined in operations to avoid out of memory errors.";
public static final String SKIP_OPERATION_EXAMPLE_DESC = "Skip examples defined in operations.";

public static final String STRIP_PACKAGE_NAME = "stripPackageName";
public static final String STRIP_PACKAGE_NAME_DESC = "Whether to strip leading dot-separated packages from generated model classes";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@
package org.openapitools.codegen;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.ExternalDocumentation;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.templating.mustache.JsonOutputLambda;

import java.util.*;

Expand Down Expand Up @@ -77,7 +81,10 @@ public class CodegenModel implements IJsonSchemaValidationProperties {
@Getter @Setter
public String title;
@Getter @Setter
public String description, classVarName, modelJson, dataType, xmlPrefix, xmlNamespace, xmlName;
public String description, classVarName, dataType, xmlPrefix, xmlNamespace, xmlName;
@Getter
public String modelJson;
private JsonNode modelJsonValue;
@Getter @Setter
public String classFilename; // store the class file name, mainly used for import
@Getter @Setter
Expand Down Expand Up @@ -274,6 +281,31 @@ public Map<String, Object> getExts() {
return vendorExtensions;
}

public Mustache.Lambda getLambdaModelJson() {
if (modelJsonValue != null) {
return new JsonOutputLambda(modelJsonValue);
}
if (modelJson != null) {
return new JsonOutputLambda(modelJson);
}
return null;
}

public void setModelJson(String modelJson) {
this.modelJson = modelJson;
this.modelJsonValue = null;
}

public void setModelJsonValue(Object modelJsonValue) {
this.modelJsonValue = modelJsonValue == null ? null : Json.mapper().valueToTree(modelJsonValue);
this.modelJson = null;
}

public void setModelJson(CodegenModel model) {
this.modelJsonValue = model.modelJsonValue;
this.modelJson = model.modelJson;
}

@Override
public CodegenProperty getContains() {
return contains;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public class CodegenOperation {
public List<CodegenResponse> responses = new ArrayList<CodegenResponse>();
public List<CodegenCallback> callbacks = new ArrayList<>();
public Set<String> imports = new HashSet<String>();
public List<Map<String, String>> examples;
public List<Map<String, String>> requestBodyExamples;
public List<Map<String, Object>> examples;
public List<Map<String, Object>> requestBodyExamples;
public ExternalDocumentation externalDocs;
public Map<String, Object> vendorExtensions = new HashMap<String, Object>();
public String nickname; // legacy support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@

package org.openapitools.codegen;

import com.fasterxml.jackson.databind.JsonNode;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.oas.models.examples.Example;
import lombok.Getter;
import lombok.Setter;
import org.openapitools.codegen.templating.mustache.JsonOutputLambda;

import java.math.BigDecimal;
import java.util.*;

/**
Expand All @@ -41,6 +45,7 @@ public class CodegenParameter implements IJsonSchemaValidationProperties {
public String nameInPascalCase; // property name in pascal case (e.g. ModifiedDate)
public String nameInSnakeCase; // property name in upper snake case
public String example; // example value (x-example)
private JsonNode exampleJsonNode;
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: New private exampleJsonNode can become inconsistent with the existing public example field because direct field assignments bypass the synchronization in setExample.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java, line 47:

<comment>New private `exampleJsonNode` can become inconsistent with the existing public `example` field because direct field assignments bypass the synchronization in `setExample`.</comment>

<file context>
@@ -41,6 +44,7 @@ public class CodegenParameter implements IJsonSchemaValidationProperties {
     public String nameInPascalCase; // property name in pascal case (e.g. ModifiedDate)
     public String nameInSnakeCase; // property name in upper snake case
     public String example; // example value (x-example)
+    private JsonNode exampleJsonNode;
     public Map<String, Example> examples;
     public String jsonSchema;
</file context>

public Map<String, Example> examples;
public String jsonSchema;
public boolean isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal, isByteArray, isBinary,
Expand Down Expand Up @@ -184,6 +189,7 @@ public CodegenParameter copy() {
output.defaultValue = this.defaultValue;
output.enumDefaultValue = this.enumDefaultValue;
output.example = this.example;
output.exampleJsonNode = this.exampleJsonNode;
output.examples = this.examples;
output.isEnum = this.isEnum;
output.isEnumRef = this.isEnumRef;
Expand Down Expand Up @@ -392,6 +398,7 @@ public boolean equals(Object o) {
Objects.equals(isMatrix, that.isMatrix) &&
Objects.equals(isAllowEmptyValue, that.isAllowEmptyValue) &&
Objects.equals(example, that.example) &&
Objects.equals(exampleJsonNode, that.exampleJsonNode) &&
Objects.equals(examples, that.examples) &&
Objects.equals(jsonSchema, that.jsonSchema) &&
Objects.equals(_enum, that._enum) &&
Expand Down Expand Up @@ -543,6 +550,53 @@ public Map<String, Object> getExts() {
return vendorExtensions;
}

public Mustache.Lambda getLambdaExample() {
if (exampleJsonNode != null) {
if (exampleJsonNode.isValueNode()) {
return new JsonOutputLambda(exampleJsonNode.asText());
}
return new JsonOutputLambda(exampleJsonNode);
}
if (example != null) {
return new JsonOutputLambda(example);
}
return null;
}

public Mustache.Lambda getLambdaExampleJsonLiteral() {
if (exampleJsonNode != null) {
return new JsonOutputLambda(exampleJsonNode);
}
if (example == null) {
return null;
}
if (isBoolean) {
return new JsonOutputLambda(Boolean.valueOf(example));
}
if (isNumeric || isInteger || isLong || isNumber || isFloat || isDouble || isDecimal || isShort || isUnboundedInteger) {
try {
return new JsonOutputLambda(new BigDecimal(example));
} catch (NumberFormatException e) {
return new JsonOutputLambda((Object) example);
}
}
return new JsonOutputLambda((Object) example);
}

public boolean getHasExample() {
return exampleJsonNode != null || example != null;
}

public void setExample(String example) {
this.example = example;
this.exampleJsonNode = null;
}

public void setExample(JsonNode exampleJsonNode) {
this.exampleJsonNode = exampleJsonNode;
this.example = exampleJsonNode != null && exampleJsonNode.isValueNode() ? exampleJsonNode.asText() : null;
}

// use schema.getContains or content.mediaType.schema.getContains instead of this
@Override
public CodegenProperty getContains() {
Expand Down Expand Up @@ -1151,4 +1205,3 @@ public void setIsEnum(boolean isEnum) {
this.isEnum = isEnum;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@

package org.openapitools.codegen;

import com.fasterxml.jackson.databind.JsonNode;
import com.samskivert.mustache.Mustache;
import lombok.Getter;
import lombok.Setter;
import org.openapitools.codegen.templating.mustache.JsonOutputLambda;

import java.util.*;

Expand Down Expand Up @@ -96,8 +99,9 @@ public class CodegenProperty implements Cloneable, IJsonSchemaValidationProperti
/**
* A free-form property to include an example of an instance for this schema.
*/
@Getter @Setter
@Getter
public String example;
private JsonNode exampleJsonNode;

@Getter @Setter
public String jsonSchema;
Expand Down Expand Up @@ -605,6 +609,33 @@ public String getRef() {
return ref;
}

public Mustache.Lambda getLambdaExample() {
if (exampleJsonNode != null) {
if (exampleJsonNode.isValueNode()) {
return new JsonOutputLambda(exampleJsonNode.asText());
}
return new JsonOutputLambda(exampleJsonNode);
}
if (example != null) {
return new JsonOutputLambda(example);
}
return null;
}

public boolean getHasExample() {
return exampleJsonNode != null || example != null;
}

public void setExample(String example) {
this.example = example;
this.exampleJsonNode = null;
}

public void setExample(JsonNode exampleJsonNode) {
this.exampleJsonNode = exampleJsonNode;
this.example = exampleJsonNode != null && exampleJsonNode.isValueNode() ? exampleJsonNode.asText() : null;
}

@Override
public CodegenProperty clone() {
try {
Expand Down Expand Up @@ -651,6 +682,7 @@ public CodegenProperty clone() {
if (this.contains != null) {
cp.setContains(this.contains);
}
cp.exampleJsonNode = this.exampleJsonNode;

return cp;
} catch (CloneNotSupportedException e) {
Expand Down Expand Up @@ -1183,6 +1215,7 @@ public boolean equals(Object o) {
Objects.equals(minLength, that.minLength) &&
Objects.equals(pattern, that.pattern) &&
Objects.equals(example, that.example) &&
Objects.equals(exampleJsonNode, that.exampleJsonNode) &&
Objects.equals(jsonSchema, that.jsonSchema) &&
Objects.equals(minimum, that.minimum) &&
Objects.equals(maximum, that.maximum) &&
Expand Down
Loading