Skip to content

Commit d64ea09

Browse files
committed
Merge remote-tracking branch 'upstream/master' into feature/jspecify_java_builder
2 parents 2740a6e + 922e593 commit d64ea09

223 files changed

Lines changed: 12701 additions & 79 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bin/configs/csharp-generichost-net10.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ additionalProperties:
1414
operationParameterSorting: alphabetical
1515
treatWarningsAsErrors: true
1616
warningsNotAsErrors: CS0612
17+
injectModelVendorExtensions:
18+
InjectedVendorExtensionsTest.potentiallyOverriddenPropertyToPrivate.x-setter-visibility: private
19+
InjectedVendorExtensionsTest.potentiallyOverriddenPropertyToInternal.x-setter-visibility: internal
20+
InjectedVendorExtensionsTest.potentiallyOverriddenPropertyToPublic.x-setter-visibility: public

docs/generators/kotlin-spring.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
8787
|x-discriminator-value|Used with model inheritance to specify value for discriminator that identifies current model|MODEL|
8888
|x-field-extra-annotation|Custom annotation(s) to be added to property; accepts a string or list of strings|FIELD, OPERATION_PARAMETER|null
8989
|x-operation-extra-annotation|Custom annotation(s) to be added to operation; accepts a string or list of strings|OPERATION|null
90+
|x-extra-imports|Custom import(s) to add to the generated file that declares the annotated model, property, operation, or parameter (e.g. so custom annotations can be referenced by their short name); accepts a string or list of strings. Values are emitted verbatim (Kotlin alias imports supported) and only exact duplicates are removed|MODEL, FIELD, OPERATION, OPERATION_PARAMETER|null
9091
|x-pattern-message|Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable|FIELD, OPERATION_PARAMETER|null
9192
|x-size-message|Add this property whenever you need to customize the invalidation error message for the size or length of a variable|FIELD, OPERATION_PARAMETER|null
9293
|x-minimum-message|Add this property whenever you need to customize the invalidation error message for the minimum value of a variable|FIELD, OPERATION_PARAMETER|null

modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,15 @@ public class Generate extends OpenApiGeneratorCommand {
259259
+ " You can also have multiple occurrences of this option.")
260260
private List<String> operationIdNameMappings = new ArrayList<>();
261261

262+
@Option(
263+
name = {"--inject-model-vendor-extensions"},
264+
title = "inject model vendor extensions",
265+
description = "injects vendor extensions into model classes or their properties."
266+
+ " Class-level format: ModelName.x-extension-name=value."
267+
+ " Property-level format: ModelName.propertyBaseName.x-extension-name=value."
268+
+ " You can also have multiple occurrences of this option.")
269+
private List<String> injectModelVendorExtensions = new ArrayList<>();
270+
262271
@Option(
263272
name = {"--openapi-normalizer"},
264273
title = "OpenAPI normalizer rules",
@@ -606,6 +615,7 @@ public void execute() {
606615
applyModelNameMappingsKvpList(modelNameMappings, configurator);
607616
applyEnumNameMappingsKvpList(enumNameMappings, configurator);
608617
applyOperationIdNameMappingsKvpList(operationIdNameMappings, configurator);
618+
applyInjectModelVendorExtensionsKvpList(injectModelVendorExtensions, configurator);
609619
applyOpenapiNormalizerKvpList(openapiNormalizer, configurator);
610620
applyTypeMappingsKvpList(typeMappings, configurator);
611621
applyAdditionalPropertiesKvpList(additionalProperties, configurator);

modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public final class GeneratorSettings implements Serializable {
5959
private final Map<String, String> modelNameMappings;
6060
private final Map<String, String> enumNameMappings;
6161
private final Map<String, String> operationIdNameMappings;
62+
private final Map<String, String> injectModelVendorExtensions;
6263
private final Map<String, String> openapiNormalizer;
6364
private final Set<String> languageSpecificPrimitives;
6465
private final Set<String> openapiGeneratorIgnoreList;
@@ -327,6 +328,15 @@ public Map<String, String> getOperationIdNameMappings() {
327328
return operationIdNameMappings;
328329
}
329330

331+
/**
332+
* Gets inject model vendor extensions.
333+
*
334+
* @return a map of ModelName.x-extension-name or ModelName.propertyBaseName.x-extension-name to extension value
335+
*/
336+
public Map<String, String> getInjectModelVendorExtensions() {
337+
return injectModelVendorExtensions;
338+
}
339+
330340
/**
331341
* Gets OpenAPI normalizer rules
332342
*
@@ -469,6 +479,7 @@ private GeneratorSettings(Builder builder) {
469479
modelNameMappings = Collections.unmodifiableMap(builder.modelNameMappings);
470480
enumNameMappings = Collections.unmodifiableMap(builder.enumNameMappings);
471481
operationIdNameMappings = Collections.unmodifiableMap(builder.operationIdNameMappings);
482+
injectModelVendorExtensions = Collections.unmodifiableMap(builder.injectModelVendorExtensions);
472483
openapiNormalizer = Collections.unmodifiableMap(builder.openapiNormalizer);
473484
languageSpecificPrimitives = Collections.unmodifiableSet(builder.languageSpecificPrimitives);
474485
openapiGeneratorIgnoreList = Collections.unmodifiableSet(builder.openapiGeneratorIgnoreList);
@@ -550,6 +561,7 @@ public GeneratorSettings() {
550561
modelNameMappings = Collections.unmodifiableMap(new HashMap<>(0));
551562
enumNameMappings = Collections.unmodifiableMap(new HashMap<>(0));
552563
operationIdNameMappings = Collections.unmodifiableMap(new HashMap<>(0));
564+
injectModelVendorExtensions = Collections.unmodifiableMap(new HashMap<>(0));
553565
openapiNormalizer = Collections.unmodifiableMap(new HashMap<>(0));
554566
languageSpecificPrimitives = Collections.unmodifiableSet(new HashSet<>(0));
555567
openapiGeneratorIgnoreList = Collections.unmodifiableSet(new HashSet<>(0));
@@ -630,6 +642,9 @@ public static Builder newBuilder(GeneratorSettings copy) {
630642
if (copy.getOperationIdNameMappings() != null) {
631643
builder.operationIdNameMappings.putAll(copy.getOperationIdNameMappings());
632644
}
645+
if (copy.getInjectModelVendorExtensions() != null) {
646+
builder.injectModelVendorExtensions.putAll(copy.getInjectModelVendorExtensions());
647+
}
633648
if (copy.getOpenapiNormalizer() != null) {
634649
builder.openapiNormalizer.putAll(copy.getOpenapiNormalizer());
635650
}
@@ -684,6 +699,7 @@ public static final class Builder {
684699
private Map<String, String> modelNameMappings;
685700
private Map<String, String> enumNameMappings;
686701
private Map<String, String> operationIdNameMappings;
702+
private Map<String, String> injectModelVendorExtensions;
687703
private Map<String, String> openapiNormalizer;
688704
private Set<String> languageSpecificPrimitives;
689705
private Set<String> openapiGeneratorIgnoreList;
@@ -712,6 +728,7 @@ public Builder() {
712728
modelNameMappings = new HashMap<>();
713729
enumNameMappings = new HashMap<>();
714730
operationIdNameMappings = new HashMap<>();
731+
injectModelVendorExtensions = new HashMap<>();
715732
openapiNormalizer = new HashMap<>();
716733
languageSpecificPrimitives = new HashSet<>();
717734
openapiGeneratorIgnoreList = new HashSet<>();
@@ -1193,6 +1210,32 @@ public Builder withOperationIdNameMapping(String key, String value) {
11931210
return this;
11941211
}
11951212

1213+
/**
1214+
* Sets the {@code injectModelExtensions} and returns a reference to this Builder so that the methods can be chained together.
1215+
*
1216+
* @param injectModelExtensions the {@code injectModelExtensions} to set
1217+
* @return a reference to this Builder
1218+
*/
1219+
public Builder withInjectModelVendorExtensions(Map<String, String> injectModelVendorExtensions) {
1220+
this.injectModelVendorExtensions = injectModelVendorExtensions;
1221+
return this;
1222+
}
1223+
1224+
/**
1225+
* Sets a single {@code injectModelVendorExtension} and returns a reference to this Builder so that the methods can be chained together.
1226+
*
1227+
* @param key A key in the format ModelName.x-extension-name or ModelName.propertyBaseName.x-extension-name
1228+
* @param value The extension value
1229+
* @return a reference to this Builder
1230+
*/
1231+
public Builder withInjectModelVendorExtension(String key, String value) {
1232+
if (this.injectModelVendorExtensions == null) {
1233+
this.injectModelVendorExtensions = new HashMap<>();
1234+
}
1235+
this.injectModelVendorExtensions.put(key, value);
1236+
return this;
1237+
}
1238+
11961239
/**
11971240
* Sets the {@code openapiNormalizer} and returns a reference to this Builder so that the methods can be chained together.
11981241
*

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ public interface CodegenConfig {
169169

170170
Map<String, String> operationIdNameMapping();
171171

172+
Map<String, String> injectModelVendorExtensions();
173+
172174
Map<String, String> openapiNormalizer();
173175

174176
Map<String, String> apiTemplateFiles();

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Lines changed: 92 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
import static org.openapitools.codegen.CodegenConstants.*;
9595
import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
9696
import static org.openapitools.codegen.utils.DiscriminatorUtils.*;
97-
import static org.openapitools.codegen.utils.EnumUtils.getEnumValues;
97+
import static org.openapitools.codegen.utils.EnumUtils.*;
9898
import static org.openapitools.codegen.utils.OnceLogger.once;
9999
import static org.openapitools.codegen.utils.StringUtils.*;
100100

@@ -199,6 +199,8 @@ public class DefaultCodegen implements CodegenConfig {
199199
protected Map<String, String> enumNameMapping = new HashMap<>();
200200
// a map to store the mapping between operation id name and the name provided by the user
201201
protected Map<String, String> operationIdNameMapping = new HashMap<>();
202+
// a map to inject vendor extensions into model classes or their properties: key=ModelName.x-extension-name or ModelName.propertyBaseName.x-extension-name, value=extensionValue
203+
protected Map<String, String> injectModelVendorExtensions = new HashMap<>();
202204
// a map to store the rules in OpenAPI Normalizer
203205
protected Map<String, String> openapiNormalizer = new HashMap<>();
204206
@Setter
@@ -547,6 +549,42 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
547549
}
548550
}
549551

552+
// Inject vendor extensions from --inject-property-extensions into matching schema properties
553+
if (!injectModelVendorExtensions.isEmpty()) {
554+
for (Map.Entry<String, ModelsMap> entry : objs.entrySet()) {
555+
CodegenModel model = ModelUtils.getModelByName(entry.getKey(), objs);
556+
if (model == null) continue;
557+
558+
for (Map.Entry<String, String> extEntry : injectModelVendorExtensions.entrySet()) {
559+
String[] parts = extEntry.getKey().split("\\.", 3);
560+
if (parts.length < 2) continue;
561+
String modelName = parts[0];
562+
String extensionValue = extEntry.getValue();
563+
564+
if (!modelName.equals(entry.getKey())) continue;
565+
566+
if (parts.length == 2) {
567+
// class-level extension: ModelName.x-extension-name
568+
model.vendorExtensions.put(parts[1], extensionValue);
569+
} else {
570+
// property-level extension: ModelName.propertyBaseName.x-extension-name
571+
String propertyBaseName = parts[1];
572+
String extensionName = parts[2];
573+
List<List<CodegenProperty>> allPropertyLists = Arrays.asList(
574+
model.vars, model.allVars, model.readWriteVars, model.requiredVars,
575+
model.optionalVars, model.parentVars, model.readOnlyVars, model.nonNullableVars);
576+
for (List<CodegenProperty> properties : allPropertyLists) {
577+
for (CodegenProperty property : properties) {
578+
if (propertyBaseName.equals(property.baseName)) {
579+
property.vendorExtensions.put(extensionName, extensionValue);
580+
}
581+
}
582+
}
583+
}
584+
}
585+
}
586+
}
587+
550588
if (this.useOneOfInterfaces) {
551589
// First, add newly created oneOf interfaces
552590
for (CodegenModel cm : addOneOfInterfaces) {
@@ -1366,6 +1404,11 @@ public Map<String, String> operationIdNameMapping() {
13661404
return operationIdNameMapping;
13671405
}
13681406

1407+
@Override
1408+
public Map<String, String> injectModelVendorExtensions() {
1409+
return injectModelVendorExtensions;
1410+
}
1411+
13691412
@Override
13701413
public Map<String, String> openapiNormalizer() {
13711414
return openapiNormalizer;
@@ -6799,31 +6842,59 @@ protected List<Map<String, Object>> buildEnumVars(List<Object> values, String da
67996842
}
68006843

68016844
if (enumUnknownDefaultCase) {
6802-
// If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.
6803-
// 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.
6804-
Map<String, Object> enumVar = new HashMap<>();
6805-
String enumName = enumUnknownDefaultCaseName;
6806-
6807-
String enumValue = isDataTypeString(dataType)
6808-
? enumUnknownDefaultCaseName
6809-
: // This is a dummy value that attempts to avoid collisions with previously specified cases.
6810-
// Int.max / 192
6811-
// The number 192 that is used to calculate this random value, is the Swift Evolution proposal for frozen/non-frozen enums.
6812-
// [SE-0192](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md)
6813-
// Since this functionality was born in the Swift 5 generator and latter on broth to all generators
6814-
// https://github.com/OpenAPITools/openapi-generator/pull/11013
6815-
String.valueOf(11184809);
6816-
6817-
enumVar.put(ENUM_NAME, toEnumVarName(enumName, dataType));
6818-
enumVar.put(ENUM_VALUE, toEnumValue(enumValue, dataType));
6819-
enumVar.put(ENUM_IS_STRING, isDataTypeString(dataType));
6820-
// TODO: add isNumeric
6821-
enumVars.add(enumVar);
6845+
injectEnumUnknownDefaultCase(enumVars, dataType);
68226846
}
68236847

68246848
return enumVars;
68256849
}
68266850

6851+
/**
6852+
* If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.
6853+
* This adds a default case to the enum, {@link DefaultCodegen#enumUnknownDefaultCaseName}, that can be used as a fallback for unknown values.
6854+
*
6855+
* @param enumVars the enumVars
6856+
* @param dataType the data type of the enum parameter
6857+
*/
6858+
private void injectEnumUnknownDefaultCase(List<Map<String, Object>> enumVars, String dataType) {
6859+
Map<String, Object> enumVar = new HashMap<>();
6860+
String enumName = enumUnknownDefaultCaseName;
6861+
6862+
String enumValue = isDataTypeString(dataType)
6863+
? enumUnknownDefaultCaseName
6864+
: // This is a dummy value that attempts to avoid collisions with previously specified cases.
6865+
// Int.max / 192
6866+
// The number 192 that is used to calculate this random value, is the Swift Evolution proposal for frozen/non-frozen enums.
6867+
// [SE-0192](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md)
6868+
// Since this functionality was born in the Swift 5 generator and latter on broth to all generators
6869+
// https://github.com/OpenAPITools/openapi-generator/pull/11013
6870+
String.valueOf(11184809);
6871+
6872+
enumVar.put(ENUM_NAME, toEnumVarName(enumName, dataType));
6873+
enumVar.put(ENUM_VALUE, toEnumValue(enumValue, dataType));
6874+
enumVar.put(ENUM_IS_STRING, isDataTypeString(dataType));
6875+
// TODO: add isNumeric
6876+
enumVars.add(enumVar);
6877+
}
6878+
6879+
/**
6880+
* Removes any injected default enum value that was created with {@link DefaultCodegen#injectEnumUnknownDefaultCase}
6881+
* from an operation's non-body enum parameters. This can for example be of interest when generating client code
6882+
* where a fallback is superfluous for a value that is only sent and never received.
6883+
*
6884+
* @param operation operation to be processed
6885+
*/
6886+
protected void removeEnumUnknownDefaultCase(CodegenOperation operation) {
6887+
for (CodegenParameter param : operation.allParams) {
6888+
if (!param.isBodyParam && param.isEnum && hasEnumVars(param.allowableValues)) {
6889+
List<Map<String, Object>> enumVars = getEnumVars(param.allowableValues);
6890+
if (enumVars != null) {
6891+
String unknownName = toEnumVarName(enumUnknownDefaultCaseName, param.dataType);
6892+
enumVars.removeIf(ev -> unknownName.equals(ev.get(ENUM_NAME)));
6893+
}
6894+
}
6895+
}
6896+
}
6897+
68276898
protected void postProcessEnumVars(List<Map<String, Object>> enumVars) {
68286899
Collections.reverse(enumVars);
68296900
enumVars.forEach(v -> {

modules/openapi-generator/src/main/java/org/openapitools/codegen/VendorExtension.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public enum VendorExtension {
2626
X_CLASS_EXTRA_ANNOTATION("x-class-extra-annotation", ExtensionLevel.MODEL, "Custom annotation(s) to be added to model; accepts a string or list of strings", null),
2727
X_FIELD_EXTRA_ANNOTATION("x-field-extra-annotation", Arrays.asList(ExtensionLevel.FIELD, ExtensionLevel.OPERATION_PARAMETER), "Custom annotation(s) to be added to property; accepts a string or list of strings", null),
2828
X_OPERATION_EXTRA_ANNOTATION("x-operation-extra-annotation", ExtensionLevel.OPERATION, "Custom annotation(s) to be added to operation; accepts a string or list of strings", null),
29+
X_EXTRA_IMPORTS("x-extra-imports", Arrays.asList(ExtensionLevel.MODEL, ExtensionLevel.FIELD, ExtensionLevel.OPERATION, ExtensionLevel.OPERATION_PARAMETER), "Custom import(s) to add to the generated file that declares the annotated model, property, operation, or parameter (e.g. so custom annotations can be referenced by their short name); accepts a string or list of strings. Values are emitted verbatim (Kotlin alias imports supported) and only exact duplicates are removed", null),
2930
X_VERSION_PARAM("x-version-param", ExtensionLevel.OPERATION_PARAMETER, "Marker property that tells that this parameter would be used for endpoint versioning. Applicable for headers & query params. true/false", null),
3031
X_PATTERN_MESSAGE("x-pattern-message", Arrays.asList(ExtensionLevel.FIELD, ExtensionLevel.OPERATION_PARAMETER), "Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable", null),
3132
X_SIZE_MESSAGE("x-size-message", Arrays.asList(ExtensionLevel.FIELD, ExtensionLevel.OPERATION_PARAMETER), "Add this property whenever you need to customize the invalidation error message for the size or length of a variable", null),

0 commit comments

Comments
 (0)