Skip to content

Commit aa61616

Browse files
authored
give clients a way to inject vendor overrides (#24571)
1 parent 41917fb commit aa61616

166 files changed

Lines changed: 10638 additions & 9 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

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: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public class CodegenConfigurator {
7777
private Map<String, String> modelNameMappings = new HashMap<>();
7878
private Map<String, String> enumNameMappings = new HashMap<>();
7979
private Map<String, String> operationIdNameMappings = new HashMap<>();
80+
private Map<String, String> injectModelVendorExtensions = new HashMap<>();
8081
private Map<String, String> openapiNormalizer = new HashMap<>();
8182
private Set<String> languageSpecificPrimitives = new HashSet<>();
8283
private Set<String> openapiGeneratorIgnoreList = new HashSet<>();
@@ -149,6 +150,9 @@ public static CodegenConfigurator fromFile(String configFile, Module... modules)
149150
if (generatorSettings.getOperationIdNameMappings() != null) {
150151
configurator.operationIdNameMappings.putAll(generatorSettings.getOperationIdNameMappings());
151152
}
153+
if (generatorSettings.getInjectModelVendorExtensions() != null) {
154+
configurator.injectModelVendorExtensions.putAll(generatorSettings.getInjectModelVendorExtensions());
155+
}
152156
if (generatorSettings.getOpenapiNormalizer() != null) {
153157
configurator.openapiNormalizer.putAll(generatorSettings.getOpenapiNormalizer());
154158
}
@@ -295,6 +299,18 @@ public CodegenConfigurator addOperationIdNameMapping(String key, String value) {
295299
return this;
296300
}
297301

302+
public CodegenConfigurator addInjectModelVendorExtension(String key, String value) {
303+
this.injectModelVendorExtensions.put(key, value);
304+
generatorSettingsBuilder.withInjectModelVendorExtension(key, value);
305+
return this;
306+
}
307+
308+
public CodegenConfigurator setInjectModelVendorExtensions(Map<String, String> extensions) {
309+
this.injectModelVendorExtensions = extensions;
310+
generatorSettingsBuilder.withInjectModelVendorExtensions(extensions);
311+
return this;
312+
}
313+
298314
public CodegenConfigurator addOpenapiNormalizer(String key, String value) {
299315
this.openapiNormalizer.put(key, value);
300316
generatorSettingsBuilder.withOpenapiNormalizer(key, value);
@@ -815,6 +831,7 @@ public ClientOptInput toClientOptInput() {
815831
config.modelNameMapping().putAll(generatorSettings.getModelNameMappings());
816832
config.enumNameMapping().putAll(generatorSettings.getEnumNameMappings());
817833
config.operationIdNameMapping().putAll(generatorSettings.getOperationIdNameMappings());
834+
config.injectModelVendorExtensions().putAll(generatorSettings.getInjectModelVendorExtensions());
818835
config.openapiNormalizer().putAll(generatorSettings.getOpenapiNormalizer());
819836
config.languageSpecificPrimitives().addAll(generatorSettings.getLanguageSpecificPrimitives());
820837
config.openapiGeneratorIgnoreList().addAll(generatorSettings.getOpenapiGeneratorIgnoreList());

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,19 @@ public static void applyOpenapiNormalizerKvp(String openapiNormalizer, CodegenCo
204204
}
205205
}
206206

207+
public static void applyInjectModelVendorExtensionsKvpList(List<String> injectModelVendorExtensions, CodegenConfigurator configurator) {
208+
for (String propString : injectModelVendorExtensions) {
209+
applyInjectModelVendorExtensionsKvp(propString, configurator);
210+
}
211+
}
212+
213+
public static void applyInjectModelVendorExtensionsKvp(String injectModelVendorExtensions, CodegenConfigurator configurator) {
214+
final Map<String, String> map = createMapFromKeyValuePairs(injectModelVendorExtensions);
215+
for (Map.Entry<String, String> entry : map.entrySet()) {
216+
configurator.addInjectModelVendorExtension(entry.getKey().trim(), entry.getValue().trim());
217+
}
218+
}
219+
207220
public static void applyTypeMappingsKvpList(List<String> typeMappings, CodegenConfigurator configurator) {
208221
for (String propString : typeMappings) {
209222
applyTypeMappingsKvp(propString, configurator);

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,19 @@ protected void patchProperty(Map<String, CodegenModel> enumRefs, CodegenModel mo
883883
if (property.datatypeWithEnum.equals("decimal")) {
884884
property.isDecimal = true;
885885
}
886+
887+
// Normalize x-setter-visibility:
888+
// "public" -> remove extension, set isReadOnly=false (public setter = default, no modifier needed)
889+
// any other value -> set isReadOnly=true (template emits "{{.}} set;" using the extension value)
890+
Object setterVisibilityObj = property.vendorExtensions.get("x-setter-visibility");
891+
if (setterVisibilityObj instanceof String) {
892+
if ("public".equals(setterVisibilityObj)) {
893+
property.vendorExtensions.remove("x-setter-visibility");
894+
property.isReadOnly = false;
895+
} else {
896+
property.isReadOnly = true;
897+
}
898+
}
886899
}
887900

888901
@Override

0 commit comments

Comments
 (0)