2626import io .swagger .v3 .core .util .Json ;
2727import io .swagger .v3 .oas .models .OpenAPI ;
2828import io .swagger .v3 .oas .models .Operation ;
29+ import io .swagger .v3 .oas .models .PathItem ;
2930import io .swagger .v3 .oas .models .callbacks .Callback ;
3031import io .swagger .v3 .oas .models .examples .Example ;
3132import io .swagger .v3 .oas .models .headers .Header ;
5960import org .openapitools .codegen .templating .mustache .TitlecaseLambda ;
6061import org .openapitools .codegen .templating .mustache .UppercaseLambda ;
6162import org .openapitools .codegen .utils .ModelUtils ;
63+ import org .openapitools .codegen .utils .OneOfImplementorAdditionalData ;
6264import org .slf4j .Logger ;
6365import org .slf4j .LoggerFactory ;
6466
@@ -187,6 +189,11 @@ apiTemplateFiles are for API outputs only (controllers/handlers).
187189 // flag to indicate whether to use environment variable to post process file
188190 protected boolean enablePostProcessFile = false ;
189191 private TemplatingEngineAdapter templatingEngine = new MustacheEngineAdapter ();
192+ // flag to indicate whether to use the utils.OneOfImplementorAdditionalData related logic
193+ protected boolean useOneOfInterfaces = false ;
194+ // whether or not the oneOf imports machinery should add oneOf interfaces as imports in implementing classes
195+ protected boolean addOneOfInterfaceImports = false ;
196+ protected List <CodegenModel > addOneOfInterfaces = new ArrayList <CodegenModel >();
190197
191198 // flag to indicate whether to only update files whose contents have changed
192199 protected boolean enableMinimalUpdate = false ;
@@ -326,6 +333,65 @@ private void registerMustacheLambdas() {
326333 // override with any special post-processing for all models
327334 @ SuppressWarnings ({"static-method" , "unchecked" })
328335 public Map <String , Object > postProcessAllModels (Map <String , Object > objs ) {
336+ if (this .useOneOfInterfaces ) {
337+ // First, add newly created oneOf interfaces
338+ for (CodegenModel cm : addOneOfInterfaces ) {
339+ Map <String , Object > modelValue = new HashMap <String , Object >() {{
340+ putAll (additionalProperties ());
341+ put ("model" , cm );
342+ }};
343+ List <Object > modelsValue = Arrays .asList (modelValue );
344+ List <Map <String , String >> importsValue = new ArrayList <Map <String , String >>();
345+ Map <String , Object > objsValue = new HashMap <String , Object >() {{
346+ put ("models" , modelsValue );
347+ put ("package" , modelPackage ());
348+ put ("imports" , importsValue );
349+ put ("classname" , cm .classname );
350+ putAll (additionalProperties );
351+ }};
352+ objs .put (cm .name , objsValue );
353+ }
354+
355+ // Gather data from all the models that contain oneOf into OneOfImplementorAdditionalData classes
356+ // (see docstring of that class to find out what information is gathered and why)
357+ Map <String , OneOfImplementorAdditionalData > additionalDataMap = new HashMap <String , OneOfImplementorAdditionalData >();
358+ for (Map .Entry modelsEntry : objs .entrySet ()) {
359+ Map <String , Object > modelsAttrs = (Map <String , Object >) modelsEntry .getValue ();
360+ List <Object > models = (List <Object >) modelsAttrs .get ("models" );
361+ List <Map <String , String >> modelsImports = (List <Map <String , String >>) modelsAttrs .getOrDefault ("imports" , new ArrayList <Map <String , String >>());
362+ for (Object _mo : models ) {
363+ Map <String , Object > mo = (Map <String , Object >) _mo ;
364+ CodegenModel cm = (CodegenModel ) mo .get ("model" );
365+ if (cm .oneOf .size () > 0 ) {
366+ cm .vendorExtensions .put ("x-is-one-of-interface" , true );
367+ for (String one : cm .oneOf ) {
368+ if (!additionalDataMap .containsKey (one )) {
369+ additionalDataMap .put (one , new OneOfImplementorAdditionalData (one ));
370+ }
371+ additionalDataMap .get (one ).addFromInterfaceModel (cm , modelsImports );
372+ }
373+ // if this is oneOf interface, make sure we include the necessary imports for it
374+ addImportsToOneOfInterface (modelsImports );
375+ }
376+ }
377+ }
378+
379+ // Add all the data from OneOfImplementorAdditionalData classes to the implementing models
380+ for (Map .Entry modelsEntry : objs .entrySet ()) {
381+ Map <String , Object > modelsAttrs = (Map <String , Object >) modelsEntry .getValue ();
382+ List <Object > models = (List <Object >) modelsAttrs .get ("models" );
383+ List <Map <String , String >> imports = (List <Map <String , String >>) modelsAttrs .get ("imports" );
384+ for (Object _implmo : models ) {
385+ Map <String , Object > implmo = (Map <String , Object >) _implmo ;
386+ CodegenModel implcm = (CodegenModel ) implmo .get ("model" );
387+ String modelName = toModelName (implcm .name );
388+ if (additionalDataMap .containsKey (modelName )) {
389+ additionalDataMap .get (modelName ).addToImplementor (this , implcm , imports , addOneOfInterfaceImports );
390+ }
391+ }
392+ }
393+ }
394+
329395 return objs ;
330396 }
331397
@@ -626,6 +692,62 @@ public void postProcessParameter(CodegenParameter parameter) {
626692 //override with any special handling of the entire OpenAPI spec document
627693 @ SuppressWarnings ("unused" )
628694 public void preprocessOpenAPI (OpenAPI openAPI ) {
695+ if (useOneOfInterfaces ) {
696+ // we process the openapi schema here to find oneOf schemas and create interface models for them
697+ Map <String , Schema > schemas = new HashMap <String , Schema >(openAPI .getComponents ().getSchemas ());
698+ if (schemas == null ) {
699+ schemas = new HashMap <String , Schema >();
700+ }
701+ Map <String , PathItem > pathItems = openAPI .getPaths ();
702+
703+ // we need to add all request and response bodies to processed schemas
704+ if (pathItems != null ) {
705+ for (Map .Entry <String , PathItem > e : pathItems .entrySet ()) {
706+ for (Map .Entry <PathItem .HttpMethod , Operation > op : e .getValue ().readOperationsMap ().entrySet ()) {
707+ String opId = getOrGenerateOperationId (op .getValue (), e .getKey (), op .getKey ().toString ());
708+ // process request body
709+ RequestBody b = ModelUtils .getReferencedRequestBody (openAPI , op .getValue ().getRequestBody ());
710+ Schema requestSchema = null ;
711+ if (b != null ) {
712+ requestSchema = ModelUtils .getSchemaFromRequestBody (b );
713+ }
714+ if (requestSchema != null ) {
715+ schemas .put (opId , requestSchema );
716+ }
717+ // process all response bodies
718+ for (Map .Entry <String , ApiResponse > ar : op .getValue ().getResponses ().entrySet ()) {
719+ ApiResponse a = ModelUtils .getReferencedApiResponse (openAPI , ar .getValue ());
720+ Schema responseSchema = ModelUtils .getSchemaFromResponse (a );
721+ if (responseSchema != null ) {
722+ schemas .put (opId + ar .getKey (), responseSchema );
723+ }
724+ }
725+ }
726+ }
727+ }
728+
729+ // go through all gathered schemas and add them as interfaces to be created
730+ for (Map .Entry <String , Schema > e : schemas .entrySet ()) {
731+ String n = toModelName (e .getKey ());
732+ Schema s = e .getValue ();
733+ String nOneOf = toModelName (n + "OneOf" );
734+ if (ModelUtils .isComposedSchema (s )) {
735+ addOneOfNameExtension ((ComposedSchema ) s , n );
736+ } else if (ModelUtils .isArraySchema (s )) {
737+ Schema items = ((ArraySchema ) s ).getItems ();
738+ if (ModelUtils .isComposedSchema (items )) {
739+ addOneOfNameExtension ((ComposedSchema ) items , nOneOf );
740+ addOneOfInterfaceModel ((ComposedSchema ) items , nOneOf );
741+ }
742+ } else if (ModelUtils .isMapSchema (s )) {
743+ Schema addProps = ModelUtils .getAdditionalProperties (s );
744+ if (addProps != null && ModelUtils .isComposedSchema (addProps )) {
745+ addOneOfNameExtension ((ComposedSchema ) addProps , nOneOf );
746+ addOneOfInterfaceModel ((ComposedSchema ) addProps , nOneOf );
747+ }
748+ }
749+ }
750+ }
629751 }
630752
631753 // override with any special handling of the entire OpenAPI spec document
@@ -950,6 +1072,12 @@ public void setAllowUnicodeIdentifiers(Boolean allowUnicodeIdentifiers) {
9501072 this .allowUnicodeIdentifiers = allowUnicodeIdentifiers ;
9511073 }
9521074
1075+ public Boolean getUseOneOfInterfaces () { return useOneOfInterfaces ; }
1076+
1077+ public void setUseOneOfInterfaces (Boolean useOneOfInterfaces ) {
1078+ this .useOneOfInterfaces = useOneOfInterfaces ;
1079+ }
1080+
9531081 /**
9541082 * Return the regular expression/JSON schema pattern (http://json-schema.org/latest/json-schema-validation.html#anchor33)
9551083 *
@@ -5534,4 +5662,49 @@ public boolean isRemoveEnumValuePrefix() {
55345662 public void setRemoveEnumValuePrefix (final boolean removeEnumValuePrefix ) {
55355663 this .removeEnumValuePrefix = removeEnumValuePrefix ;
55365664 }
5537- }
5665+
5666+ //// Following methods are related to the "useOneOfInterfaces" feature
5667+ /**
5668+ * Add "x-oneOf-name" extension to a given oneOf schema (assuming it has at least 1 oneOf elements)
5669+ * @param s schema to add the extension to
5670+ * @param name name of the parent oneOf schema
5671+ */
5672+ public void addOneOfNameExtension (ComposedSchema s , String name ) {
5673+ if (s .getOneOf () != null && s .getOneOf ().size () > 0 ) {
5674+ s .addExtension ("x-oneOf-name" , name );
5675+ }
5676+ }
5677+
5678+ /**
5679+ * Add a given ComposedSchema as an interface model to be generated
5680+ * @param cs ComposedSchema object to create as interface model
5681+ * @param type name to use for the generated interface model
5682+ */
5683+ public void addOneOfInterfaceModel (ComposedSchema cs , String type ) {
5684+ CodegenModel cm = new CodegenModel ();
5685+
5686+ cm .discriminator = createDiscriminator ("" , (Schema ) cs );
5687+ for (Schema o : cs .getOneOf ()) {
5688+ if (o .get$ref () == null ) {
5689+ if (cm .discriminator != null && o .get$ref () == null ) {
5690+ // OpenAPI spec states that inline objects should not be considered when discriminator is used
5691+ // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminatorObject
5692+ LOGGER .warn ("Ignoring inline object in oneOf definition of {}, since discriminator is used" , type );
5693+ } else {
5694+ LOGGER .warn ("Inline models are not supported in oneOf definition right now" );
5695+ }
5696+ continue ;
5697+ }
5698+ cm .oneOf .add (toModelName (ModelUtils .getSimpleRef (o .get$ref ())));
5699+ }
5700+ cm .name = type ;
5701+ cm .classname = type ;
5702+ cm .vendorExtensions .put ("x-is-one-of-interface" , true );
5703+ cm .interfaceModels = new ArrayList <CodegenModel >();
5704+
5705+ addOneOfInterfaces .add (cm );
5706+ }
5707+
5708+ public void addImportsToOneOfInterface (List <Map <String , String >> imports ) {}
5709+ //// End of methods related to the "useOneOfInterfaces" feature
5710+ }
0 commit comments