2929import io .swagger .v3 .oas .models .parameters .Parameter ;
3030import io .swagger .v3 .oas .models .security .*;
3131import io .swagger .v3 .oas .models .tags .Tag ;
32+
3233import org .apache .commons .io .FilenameUtils ;
3334import org .apache .commons .io .comparator .PathFileComparator ;
3435import org .apache .commons .lang3 .ObjectUtils ;
6566import java .util .*;
6667import java .util .concurrent .ConcurrentSkipListSet ;
6768import java .util .function .Function ;
69+ import java .util .function .Supplier ;
6870import java .util .stream .Collectors ;
6971
7072import static org .apache .commons .lang3 .StringUtils .removeStart ;
@@ -81,6 +83,7 @@ public class DefaultGenerator implements Generator {
8183 protected CodegenIgnoreProcessor ignoreProcessor ;
8284 private Boolean generateApis = null ;
8385 private Boolean generateModels = null ;
86+ private Boolean generateRecursiveDependentModels = null ;
8487 private Boolean generateSupportingFiles = null ;
8588 private Boolean generateWebhooks = null ;
8689 private Boolean generateApiTests = null ;
@@ -232,6 +235,7 @@ void configureGeneratorProperties() {
232235 generateModelDocumentation = GlobalSettings .getProperty (CodegenConstants .MODEL_DOCS ) != null ? Boolean .valueOf (GlobalSettings .getProperty (CodegenConstants .MODEL_DOCS )) : getGeneratorPropertyDefaultSwitch (CodegenConstants .MODEL_DOCS , true );
233236 generateApiTests = GlobalSettings .getProperty (CodegenConstants .API_TESTS ) != null ? Boolean .valueOf (GlobalSettings .getProperty (CodegenConstants .API_TESTS )) : getGeneratorPropertyDefaultSwitch (CodegenConstants .API_TESTS , true );
234237 generateApiDocumentation = GlobalSettings .getProperty (CodegenConstants .API_DOCS ) != null ? Boolean .valueOf (GlobalSettings .getProperty (CodegenConstants .API_DOCS )) : getGeneratorPropertyDefaultSwitch (CodegenConstants .API_DOCS , true );
238+ generateRecursiveDependentModels = GlobalSettings .getProperty (CodegenConstants .GENERATE_RECURSIVE_DEPENDENT_MODELS ) != null ? Boolean .valueOf (GlobalSettings .getProperty (CodegenConstants .GENERATE_RECURSIVE_DEPENDENT_MODELS )) : getGeneratorPropertyDefaultSwitch (CodegenConstants .GENERATE_RECURSIVE_DEPENDENT_MODELS , false );
235239
236240 // Additional properties added for tests to exclude references in project related files
237241 config .additionalProperties ().put (CodegenConstants .GENERATE_API_TESTS , generateApiTests );
@@ -243,6 +247,7 @@ void configureGeneratorProperties() {
243247 config .additionalProperties ().put (CodegenConstants .GENERATE_APIS , generateApis );
244248 config .additionalProperties ().put (CodegenConstants .GENERATE_MODELS , generateModels );
245249 config .additionalProperties ().put (CodegenConstants .GENERATE_WEBHOOKS , generateWebhooks );
250+ config .additionalProperties ().put (CodegenConstants .GENERATE_RECURSIVE_DEPENDENT_MODELS , generateRecursiveDependentModels );
246251
247252 if (!generateApiTests && !generateModelTests ) {
248253 config .additionalProperties ().put (CodegenConstants .EXCLUDE_TESTS , true );
@@ -443,36 +448,21 @@ private void generateModel(List<File> files, Map<String, Object> models, String
443448 }
444449
445450 void generateModels (List <File > files , List <ModelMap > allModels , List <String > unusedModels , List <ModelMap > aliasModels ) {
451+ generateModels (files , allModels , unusedModels , aliasModels , new ArrayList <>(), DefaultGenerator .this ::modelKeys );
452+ }
453+
454+ void generateModels (List <File > files , List <ModelMap > allModels , List <String > unusedModels , List <ModelMap > aliasModels , List <String > processedModels , Supplier <Set <String >> modelKeysSupplier ) {
446455 if (!generateModels ) {
447456 // TODO: Process these anyway and add to dryRun info
448457 LOGGER .info ("Skipping generation of models." );
449458 return ;
450459 }
451460
452- final Map <String , Schema > schemas = ModelUtils .getSchemas (this .openAPI );
453- if (schemas == null ) {
454- LOGGER .warn ("Skipping generation of models because specification document has no schemas." );
461+ Set <String > modelKeys = modelKeysSupplier .get ();
462+ if (modelKeys .isEmpty ()) {
455463 return ;
456464 }
457465
458- String modelNames = GlobalSettings .getProperty ("models" );
459- Set <String > modelsToGenerate = null ;
460- if (modelNames != null && !modelNames .isEmpty ()) {
461- modelsToGenerate = new HashSet <>(Arrays .asList (modelNames .split ("," )));
462- }
463-
464- Set <String > modelKeys = schemas .keySet ();
465- if (modelsToGenerate != null && !modelsToGenerate .isEmpty ()) {
466- Set <String > updatedKeys = new HashSet <>();
467- for (String m : modelKeys ) {
468- if (modelsToGenerate .contains (m )) {
469- updatedKeys .add (m );
470- }
471- }
472-
473- modelKeys = updatedKeys ;
474- }
475-
476466 // store all processed models
477467 Map <String , ModelsMap > allProcessedModels = new TreeMap <>((o1 , o2 ) -> ObjectUtils .compare (config .toModelName (o1 ), config .toModelName (o2 )));
478468
@@ -508,7 +498,7 @@ void generateModels(List<File> files, List<ModelMap> allModels, List<String> unu
508498 }
509499 }
510500
511- Schema schema = schemas .get (name );
501+ Schema schema = ModelUtils . getSchemas ( this . openAPI ) .get (name );
512502
513503 if (schema .getExtensions () != null && Boolean .TRUE .equals (schema .getExtensions ().get ("x-internal" ))) {
514504 LOGGER .info ("Model {} not generated since x-internal is set to true" , name );
@@ -549,6 +539,24 @@ void generateModels(List<File> files, List<ModelMap> allModels, List<String> unu
549539 // post process all processed models
550540 allProcessedModels = config .postProcessAllModels (allProcessedModels );
551541
542+ if (generateRecursiveDependentModels ) {
543+ for (ModelsMap modelsMap : allProcessedModels .values ()) {
544+ for (ModelMap mm : modelsMap .getModels ()) {
545+ CodegenModel cm = mm .getModel ();
546+ if (cm != null ) {
547+ for (CodegenProperty variable : cm .getVars ()) {
548+ generateModelsForVariable (files , allModels , unusedModels , aliasModels , processedModels , variable );
549+ }
550+ //TODO: handle interfaces
551+ String parentSchema = cm .getParentSchema ();
552+ if (parentSchema != null && !processedModels .contains (parentSchema ) && ModelUtils .getSchemas (this .openAPI ).containsKey (parentSchema )) {
553+ generateModels (files , allModels , unusedModels , aliasModels , processedModels , () -> Set .of (parentSchema ));
554+ }
555+ }
556+ }
557+ }
558+ }
559+
552560 // generate files based on processed models
553561 for (String modelName : allProcessedModels .keySet ()) {
554562 ModelsMap models = allProcessedModels .get (modelName );
@@ -592,7 +600,71 @@ void generateModels(List<File> files, List<ModelMap> allModels, List<String> unu
592600 LOGGER .info ("############ Model info ############" );
593601 Json .prettyPrint (allModels );
594602 }
603+ }
604+
605+ /**
606+ * this method guesses the schema type of in parent model used variable and if the schema type is available it let the generate the model for the type of this variable
607+ */
608+ private void generateModelsForVariable (List <File > files , List <ModelMap > allModels , List <String > unusedModels , List <ModelMap > aliasModels , List <String > processedModels , CodegenProperty variable ) {
609+ if (variable == null ) {
610+ return ;
611+ }
612+
613+ final String schemaKey = calculateModelKey (variable .getOpenApiType (), variable .getRef ());
614+ Map <String , Schema > allSchemas = ModelUtils .getSchemas (this .openAPI );
615+ if (!processedModels .contains (schemaKey ) && allSchemas .containsKey (schemaKey )) {
616+ generateModels (files , allModels , unusedModels , aliasModels , processedModels , () -> Set .of (schemaKey ));
617+ } else if (variable .getComplexType () != null && variable .getComposedSchemas () == null ) {
618+ String ref = variable .getHasItems () ? variable .getItems ().getRef () : variable .getRef ();
619+ final String key = calculateModelKey (variable .getComplexType (), ref );
620+ if (allSchemas .containsKey (key )) {
621+ generateModels (files , allModels , unusedModels , aliasModels , processedModels , () -> Set .of (key ));
622+ } else {
623+ LOGGER .info ("Type " + variable .getComplexType ()+" of variable " + variable .getName () + " could not be resolve because it is not declared as a model." );
624+ }
625+ } else {
626+ LOGGER .info ("Type " + variable .getOpenApiType ()+" of variable " + variable .getName () + " could not be resolve because it is not declared as a model." );
627+ }
628+ }
629+
630+ private String calculateModelKey (String type , String ref ) {
631+ Map <String , Schema > schemaMap = ModelUtils .getSchemas (this .openAPI );
632+ Set <String > keys = schemaMap .keySet ();
633+ String simpleRef ;
634+ if (keys .contains (type )) {
635+ return type ;
636+ } else if (keys .contains (simpleRef = ModelUtils .getSimpleRef (ref ))) {
637+ return simpleRef ;
638+ } else {
639+ return type ;
640+ }
641+ }
642+
643+ private Set <String > modelKeys () {
644+ final Map <String , Schema > schemas = ModelUtils .getSchemas (this .openAPI );
645+ if (schemas == null ) {
646+ LOGGER .warn ("Skipping generation of models because specification document has no schemas." );
647+ return Collections .emptySet ();
648+ }
595649
650+ String modelNames = GlobalSettings .getProperty ("models" );
651+ Set <String > modelsToGenerate = null ;
652+ if (modelNames != null && !modelNames .isEmpty ()) {
653+ modelsToGenerate = new HashSet <>(Arrays .asList (modelNames .split ("," )));
654+ }
655+
656+ Set <String > modelKeys = schemas .keySet ();
657+ if (modelsToGenerate != null && !modelsToGenerate .isEmpty ()) {
658+ Set <String > updatedKeys = new HashSet <>();
659+ for (String m : modelKeys ) {
660+ if (modelsToGenerate .contains (m )) {
661+ updatedKeys .add (m );
662+ }
663+ }
664+
665+ modelKeys = updatedKeys ;
666+ }
667+ return modelKeys ;
596668 }
597669
598670 @ SuppressWarnings ("unchecked" )
0 commit comments