Repository: asyncapi/modelina
Pull request #676 (2022-03-08 16:02:33)

refactor: add splitter 
**Description**
This PR adds the splitter functionality that generators can use to split the MetaModel input based on what they support.

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; ![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')

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

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=676) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=676&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; ![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')

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

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=676) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=asyncapi_generator-model-sdk&pullRequest=676&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/src/helpers/Splitter.ts b/src/helpers/Splitter.ts
new file mode 100644
index 00000000..16f0b0cf
--- /dev/null
+++ b/src/helpers/Splitter.ts
@@ -0,0 +1,82 @@
+import { MetaModel, ReferenceModel, EnumModel, UnionModel, ArrayModel, TupleModel, StringModel, IntegerModel, FloatModel, BooleanModel, ObjectModel, DictionaryModel} from '../models';
+
+export type SplitOptions = {
+  splitEnum?: boolean,
+  splitUnion?: boolean,
+  splitArray?: boolean,
+  splitTuple?: boolean,
+  splitString?: boolean,
+  splitInteger?: boolean,
+  splitFloat?: boolean,
+  splitBoolean?: boolean,
+  splitObject?: boolean,
+  splitDictionary?: boolean
+}
+
+/**
+ * Try split the model
+ * @param model 
+ * @param options 
+ * @param models 
+ * @returns whether the new or old MetaModel to use.
+ */
+// eslint-disable-next-line sonarjs/cognitive-complexity
+const trySplitModel = (model: MetaModel, options: SplitOptions, models: MetaModel[]): MetaModel => {
+  const shouldSplit = options.splitEnum === true && model instanceof EnumModel ||
+    options.splitUnion === true && model instanceof UnionModel ||
+    options.splitArray === true && model instanceof ArrayModel ||
+    options.splitTuple === true && model instanceof TupleModel ||
+    options.splitString === true && model instanceof StringModel ||
+    options.splitInteger === true && model instanceof IntegerModel ||
+    options.splitFloat === true && model instanceof FloatModel ||
+    options.splitBoolean === true && model instanceof BooleanModel ||
+    options.splitObject === true && model instanceof ObjectModel ||
+    options.splitDictionary === true && model instanceof DictionaryModel;
+
+  if (shouldSplit) {
+    models.push(model);
+    return new ReferenceModel(model.name, model.originalInput, model);
+  } 
+  return model;
+};
+
+/**
+ * Overwrite the nested models with references where required.
+ * 
+ * @param model 
+ * @param options 
+ * @param models 
+ * @returns an array of all the split models
+ */
+export const split = (model: MetaModel, options: SplitOptions, models: MetaModel[] = [model]): MetaModel[] => {
+  if (model instanceof ObjectModel) {
+    for (const [prop, propModel] of Object.entries(model.properties)) {
+      model.properties[String(prop)] = trySplitModel(propModel, options, models);
+      split(propModel, options, models);
+    }
+  } else if (model instanceof UnionModel) {
+    for (let index = 0; index < model.union.length; index++) {
+      const unionModel = model.union[Number(index)];
+      model.union[Number(index)] = trySplitModel(unionModel, options, models);
+      split(unionModel, options, models);
+    }
+  } else if (model instanceof ArrayModel) {
+    const valueModel = model.valueModel;
+    model.valueModel = trySplitModel(valueModel, options, models);
+    split(valueModel, options, models);
+  } else if (model instanceof TupleModel) {
+    for (const tuple of model.tuple) {
+      const tupleModel = tuple.value;
+      tuple.value = trySplitModel(tupleModel, options, models);
+      split(tupleModel, options, models);
+    }
+  } else if (model instanceof DictionaryModel) {
+    const keyModel = model.key;
+    const valueModel = model.value;
+    model.key = trySplitModel(keyModel, options, models);
+    model.value = trySplitModel(valueModel, options, models);
+    split(keyModel, options, models);
+    split(valueModel, options, models);
+  }
+  return models;
+};
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
index f7d84c8e..2e5cfb8c 100644
--- a/src/helpers/index.ts
+++ b/src/helpers/index.ts
@@ -2,3 +2,4 @@ export * from './FormatHelpers';
 export * from './TypeHelpers';
 export * from './NameHelpers';
 export * from './FileHelpers';
+export * from './Splitter';


Test patch:
diff --git a/test/helpers/Splitter.spec.ts b/test/helpers/Splitter.spec.ts
new file mode 100644
index 00000000..89d94647
--- /dev/null
+++ b/test/helpers/Splitter.spec.ts
@@ -0,0 +1,50 @@
+import { split, SplitOptions } from '../../src/helpers';
+import { ObjectModel, ReferenceModel, StringModel } from '../../src/models';
+describe('Splitter', () => {
+  afterEach(() => {
+    jest.restoreAllMocks();
+  });
+  test('should split models when asked for it', () => { 
+    const stringModel = new StringModel('testString', undefined);
+    const model = new ObjectModel('testObj', undefined, {
+      test: stringModel
+    });
+    const options: SplitOptions = {
+      splitString: true
+    };
+    const splittedModels = split(model, options);
+    
+    const expectedObjectModel = model;
+    expectedObjectModel.properties['test'] = new ReferenceModel(stringModel.name, stringModel.originalInput, stringModel);
+    
+    expect(splittedModels.length).toEqual(2);
+    expect(splittedModels[0] instanceof ObjectModel).toEqual(true);
+    expect(splittedModels[0]).toEqual(expectedObjectModel);
+    expect(splittedModels[1] instanceof StringModel).toEqual(true);
+  });
+  test('should not split models when not asked for', () => { 
+    const stringModel = new StringModel('testString', undefined);
+    const model = new ObjectModel('testObj', undefined, {
+      test: stringModel
+    });
+    const options: SplitOptions = {
+      splitString: false
+    };
+    const splittedModels = split(model, options);
+    expect(splittedModels.length).toEqual(1);
+    
+    expect(splittedModels[0]).toEqual(model);
+  });
+  test('should not split models when asked for something else', () => { 
+    const stringModel = new StringModel('testString', undefined);
+    const model = new ObjectModel('testObj', undefined, {
+      test: stringModel
+    });
+    const options: SplitOptions = {
+      splitBoolean: true
+    };
+    const splittedModels = split(model, options);
+    expect(splittedModels.length).toEqual(1);
+    expect(splittedModels[0]).toEqual(model);
+  });
+});