Repository: asyncapi/modelina
Pull request #233 (2021-06-09 15:37:27)

fix: nested object models are not split
**Description**
This PR fixes a bug where nested models (i.e. first encountered model is not an object model but the nested one is) are not split.


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=VULNERABILITY)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=SECURITY_HOTSPOT) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=SECURITY_HOTSPOT)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=asyncapi_generator-model-sdk&pullRequest=233&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=233) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='2.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=233&metric=new_duplicated_lines_density&view=list) [2.0% Duplication](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=233&metric=new_duplicated_lines_density&view=list)


coveralls: ## Pull Request Test Coverage Report for [Build 922259923](https://coveralls.io/builds/40443403)

* **14** of **14**   **(100.0%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.02%**) to **93.997%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/40443403/badge)](https://coveralls.io/builds/40443403) |
| :-- | --: |
| Change from base [Build 921772376](https://coveralls.io/builds/40437553): |  0.02% |
| Covered Lines: | 1192 |
| Relevant Lines: | 1222 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/src/interpreter/PostInterpreter.ts b/src/interpreter/PostInterpreter.ts
index 24f94371..12c1ad7a 100644
--- a/src/interpreter/PostInterpreter.ts
+++ b/src/interpreter/PostInterpreter.ts
@@ -8,9 +8,9 @@ import { isModelObject } from './Utils';
  * @param model 
  */
 export function postInterpretModel(model: CommonModel): CommonModel[] {
-  const iteratedModels: CommonModel[] = [];
-  ensureModelsAreSplit(model, iteratedModels);
-  return iteratedModels;
+  const splitModels: CommonModel[] = [model];
+  ensureModelsAreSplit(model, splitModels);
+  return splitModels;
 }
 
 /**
@@ -19,15 +19,19 @@ export function postInterpretModel(model: CommonModel): CommonModel[] {
  * @param model check if it should be split up
  * @param iteratedModels which have already been split up
  */
-function trySplitModels(model: CommonModel, iteratedModels: CommonModel[]): CommonModel {
+function trySplitModels(model: CommonModel, splitModels: CommonModel[], iteratedModels: CommonModel[]): CommonModel {
+  let modelToReturn: CommonModel = model;
   if (isModelObject(model)) {
     Logger.info(`Splitting model ${model.$id || 'any'} since it should be on its own`);
     const switchRootModel = new CommonModel();
     switchRootModel.$ref = model.$id;
-    ensureModelsAreSplit(model, iteratedModels);
-    return switchRootModel;
+    modelToReturn = switchRootModel;
+    if (!splitModels.includes(model)) {
+      splitModels.push(model);
+    }
   }
-  return model;
+  ensureModelsAreSplit(model, splitModels, iteratedModels);
+  return modelToReturn;
 }
 
 /**
@@ -36,34 +40,33 @@ function trySplitModels(model: CommonModel, iteratedModels: CommonModel[]): Comm
  * @param model to ensure are split
  * @param iteratedModels which are already split
  */
-function ensureModelsAreSplit(model: CommonModel, iteratedModels: CommonModel[]): void {
+function ensureModelsAreSplit(model: CommonModel, splitModels: CommonModel[], iteratedModels: CommonModel[] = []): void {
   // eslint-disable-next-line sonarjs/no-collapsible-if
   if (iteratedModels.includes(model)) { return; }
-
   iteratedModels.push(model);
   if (model.properties) {
     const existingProperties = model.properties;
     for (const [prop, propSchema] of Object.entries(existingProperties)) {
-      model.properties[String(prop)] = trySplitModels(propSchema, iteratedModels);
+      model.properties[String(prop)] = trySplitModels(propSchema, splitModels, iteratedModels);
     }
   }
   if (model.patternProperties) {
     const existingPatternProperties = model.patternProperties;
     for (const [pattern, patternModel] of Object.entries(existingPatternProperties)) {
-      model.patternProperties[String(pattern)] = trySplitModels(patternModel, iteratedModels);
+      model.patternProperties[String(pattern)] = trySplitModels(patternModel, splitModels, iteratedModels);
     }
   }
   if (model.additionalProperties) {
-    model.additionalProperties = trySplitModels(model.additionalProperties, iteratedModels);
+    model.additionalProperties = trySplitModels(model.additionalProperties, splitModels, iteratedModels);
   }
   if (model.items) {
     let existingItems = model.items;
     if (Array.isArray(existingItems)) {
       for (const [itemIndex, itemModel] of existingItems.entries()) {
-        existingItems[Number(itemIndex)] = trySplitModels(itemModel, iteratedModels);
+        existingItems[Number(itemIndex)] = trySplitModels(itemModel, splitModels, iteratedModels);
       }
     } else {
-      existingItems = trySplitModels(existingItems, iteratedModels);
+      existingItems = trySplitModels(existingItems, splitModels, iteratedModels);
     }
     model.items = existingItems;
   }


Test patch:
diff --git a/test/interpreter/PostInterpreter.spec.ts b/test/interpreter/PostInterpreter.spec.ts
index 8d2e74a1..b48ad12e 100644
--- a/test/interpreter/PostInterpreter.spec.ts
+++ b/test/interpreter/PostInterpreter.spec.ts
@@ -30,6 +30,53 @@ describe('PostInterpreter', () => {
       expect(postProcessedModels).toHaveLength(1);
       expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model);
     });
+    test('should split models when nested models occur', () => {
+      const rawModel = {
+        $id: 'schema1',
+        properties: {
+          testProp: {
+            type: 'array',
+            items: {
+              $id: 'schema2',
+              type: 'object'
+            }
+          }
+        }
+      };
+      const model = CommonModel.toCommonModel(rawModel);
+      (isModelObject as jest.Mock)
+        .mockReturnValueOnce(false)
+        .mockReturnValueOnce(true);
+
+      const postProcessedModels = postInterpretModel(model);
+
+      const expectedSchema1Model = CommonModel.toCommonModel({
+        $id: 'schema1',
+        properties: {
+          testProp: {
+            type: 'array',
+            items: {
+              $ref: 'schema2'
+            }
+          }
+        }
+      });
+      const expectedSchema2Model = CommonModel.toCommonModel({
+        $id: 'schema2',
+        type: 'object'
+      });
+
+      expect(postProcessedModels).toHaveLength(2);
+      expect(isModelObject).toHaveBeenNthCalledWith(1, expect.objectContaining({
+        type: 'array'
+      }));
+      expect(isModelObject).toHaveBeenNthCalledWith(2, {
+        $id: 'schema2',
+        type: 'object'
+      });
+      expect(postProcessedModels[0]).toMatchObject(expectedSchema1Model);
+      expect(postProcessedModels[1]).toMatchObject(expectedSchema2Model);
+    });
     test('should split models if properties contains model object', () => {
       const rawModel = {
         $id: 'schema1',