diff --git a/package.json b/package.json index 0c98b78..cc14284 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "lib/index.js", "scripts": { "test": "mocha --reporter spec --require @babel/register test/index.js", + "performance-test": "tsc test/performance/src/index.ts --outDir test/performance/lib && node test/performance/lib/index.js", "test-watch": "mocha --reporter spec --compilers js:babel-core/register test/index.js --recursive --watch", "prepublish": "npm run clean && npm run build && npm run test", "clean": "rm -rf lib/", diff --git a/src/mobx_model.ts b/src/mobx_model.ts index dcdcfaf..dd6782b 100644 --- a/src/mobx_model.ts +++ b/src/mobx_model.ts @@ -43,7 +43,7 @@ export interface MobxModelSetOptions { } export interface MobxModelObservables { - collection: MobxModel[]; + collection: Map; } /* @@ -54,7 +54,7 @@ export interface MobxModelObservables { */ const initObservables = function(target: any) { if (!target.observables) { - target.observables = { collection: [] }; + target.observables = { collection: new Map() }; } }; @@ -119,15 +119,13 @@ class MobxModel { return null; } - const items: any[] = result(this, 'observables.collection'); + const items: Map = result( + this, + 'observables.collection', + ); if (items) { - let l = items.length; - for (let i = 0; i < l; i++) { - if (items[i].id && items[i].id.toString() === id.toString()) { - return items[i]; - } - } + return items.get(id) || null; } return null; @@ -140,7 +138,7 @@ class MobxModel { let { topLevelJson, requestId } = options; /* - requestId is used to allow models to + requestId is used to allow models to prevent loops when setting same attributes multiple times, we set one if none is set */ @@ -163,7 +161,7 @@ class MobxModel { requestId, }); - this.observables.collection.push(model); + this.observables.collection.set(model.id, model); } model.set({ modelJson, topLevelJson, requestId }); @@ -174,16 +172,13 @@ class MobxModel { static remove(model: MobxModel): void { if (this.observables && this.observables.collection) { - this.observables.collection.splice( - this.observables.collection.indexOf(model), - 1, - ); + this.observables.collection.delete(model.id); } } static all(): MobxModel[] { initObservables(this); - return this.observables.collection.slice(); + return Array.from(this.observables.collection.values()); } static addClassAction( diff --git a/test/performance/src/index.ts b/test/performance/src/index.ts new file mode 100644 index 0000000..f320b63 --- /dev/null +++ b/test/performance/src/index.ts @@ -0,0 +1,16 @@ +import * as mobx from 'mobx'; +import * as models from './models'; +import { RootModel } from './models'; +import MobxModel from '../../../lib'; + +MobxModel.config({ + mobx, + models, +}); + +const rootModel = new RootModel(); +const modelJson = rootModel.generateJson(); + +console.time(); +rootModel.set({ modelJson, topLevelJson: modelJson }); +console.timeEnd(); diff --git a/test/performance/src/models/index.ts b/test/performance/src/models/index.ts new file mode 100644 index 0000000..b8a900f --- /dev/null +++ b/test/performance/src/models/index.ts @@ -0,0 +1,33 @@ +import Model12 from './model12'; +import Model14 from './model14'; +import Model3 from './model3'; +import Model2 from './model2'; +import Model6 from './model6'; +import Model8 from './model8'; +import Model4 from './model4'; +import Model5 from './model5'; +import Model7 from './model7'; +import Model11 from './model11'; +import Model1 from './model1'; +import Model13 from './model13'; +import Model9 from './model9'; +import Model10 from './model10'; +import RootModel from './root-model'; + +export { + Model12, + Model14, + Model3, + Model2, + Model6, + Model8, + Model4, + Model7, + Model11, + Model1, + Model13, + Model5, + Model9, + Model10, + RootModel, +}; diff --git a/test/performance/src/models/model1.ts b/test/performance/src/models/model1.ts new file mode 100644 index 0000000..12ec527 --- /dev/null +++ b/test/performance/src/models/model1.ts @@ -0,0 +1,26 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class Model1 extends ProtoTestModel { + static modelName = 'Model1'; + static numberOfItemsInJson = 7165; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: '', + attribute6: '', + }; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model11', + reverseRelation: true, + }, + ]; +} + +export default Model1; diff --git a/test/performance/src/models/model10.ts b/test/performance/src/models/model10.ts new file mode 100644 index 0000000..892c4af --- /dev/null +++ b/test/performance/src/models/model10.ts @@ -0,0 +1,87 @@ +const times = require('lodash/times'); +const random = require('lodash/random'); + +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel, { TestModelAttributeType } from './proto-test-model'; + +interface IItem1 { + item1Attribute1: string; + item1Attribute2: string; + item1Attribute3: string; +} + +interface IItem2 { + item2Attribute1: string; + item2Attribute2: string; + item2Attribute3: string; + item2Attribute4: string; +} + +class Model10 extends ProtoTestModel { + static modelName = 'Model10'; + static numberOfItemsInJson = 133; + + static attributeTypes = { + attribute4: TestModelAttributeType.boolean, + }; + + static generateAttributeValue(attributeName: string): any { + if (attributeName === 'attribute7') { + return this.generateItem1(); + } + + if (attributeName === 'attribute8') { + return times(random(5), this.generateItem1); + } + + if (attributeName === 'attribute9') { + return times(random(5), this.generateItem2); + } + + return super.generateAttributeValue(attributeName); + } + + static generateItem1(): IItem1 { + return { + item1Attribute1: ProtoTestModel.generateRandomString(), + item1Attribute2: ProtoTestModel.generateRandomString(), + item1Attribute3: ProtoTestModel.generateRandomString(), + }; + } + + static generateItem2(): IItem2 { + return { + item2Attribute1: ProtoTestModel.generateRandomString(), + item2Attribute2: ProtoTestModel.generateRandomString(), + item2Attribute3: ProtoTestModel.generateRandomString(), + item2Attribute4: ProtoTestModel.generateRandomString(), + }; + } + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: false, + attribute5: '', + attribute6: '', + attribute7: void 0, + attribute8: [], + attribute9: [], + }; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model2', + reverseRelation: true, + }, + { + type: RelationType.hasMany, + relatedModel: 'Model12', + reverseRelation: true, + }, + ]; +} + +export default Model10; diff --git a/test/performance/src/models/model11.ts b/test/performance/src/models/model11.ts new file mode 100644 index 0000000..9ac9f71 --- /dev/null +++ b/test/performance/src/models/model11.ts @@ -0,0 +1,37 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class Model11 extends ProtoTestModel { + static modelName = 'Model11'; + static numberOfItemsInJson = 3326; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: '', + attribute6: '', + attribute7: '', + attribute8: '', + }; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model2', + reverseRelation: true, + }, + { + type: RelationType.hasOne, + relatedModel: 'Model14', + reverseRelation: true, + }, + { + type: RelationType.hasMany, + relatedModel: 'Model1', + }, + ]; +} + +export default Model11; diff --git a/test/performance/src/models/model12.ts b/test/performance/src/models/model12.ts new file mode 100644 index 0000000..e6b6a1e --- /dev/null +++ b/test/performance/src/models/model12.ts @@ -0,0 +1,57 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel, { TestModelAttributeType } from './proto-test-model'; + +class Model12 extends ProtoTestModel { + static modelName = 'Model12'; + static numberOfItemsInJson = 2605; + + static attributeTypes = { + attribute5: TestModelAttributeType.boolean, + attribute6: TestModelAttributeType.boolean, + attribute7: TestModelAttributeType.number, + attribute18: TestModelAttributeType.number, + attribute19: TestModelAttributeType.boolean, + }; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: false, + attribute6: false, + attribute7: 0, + attribute8: '', + attribute9: '', + attribute10: '', + attribute11: '', + attribute12: '', + attribute13: '', + attribute14: '', + attribute15: '', + attribute16: '', + attribute17: '', + attribute18: 0, + attribute19: true, + }; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model14', + reverseRelation: true, + }, + { + type: RelationType.hasOne, + relatedModel: 'Model2', + reverseRelation: true, + }, + { + type: RelationType.hasOne, + relatedModel: 'Model10', + reverseRelation: true, + }, + ]; +} + +export default Model12; diff --git a/test/performance/src/models/model13.ts b/test/performance/src/models/model13.ts new file mode 100644 index 0000000..fd1b5c4 --- /dev/null +++ b/test/performance/src/models/model13.ts @@ -0,0 +1,14 @@ +import ProtoTestModel from './proto-test-model'; + +class Model13 extends ProtoTestModel { + static modelName = 'Model13'; + static numberOfItemsInJson = 175; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + }; +} + +export default Model13; diff --git a/test/performance/src/models/model14.ts b/test/performance/src/models/model14.ts new file mode 100644 index 0000000..83e5844 --- /dev/null +++ b/test/performance/src/models/model14.ts @@ -0,0 +1,33 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel, { TestModelAttributeType } from './proto-test-model'; + +class Model14 extends ProtoTestModel { + static modelName = 'Model14'; + static numberOfItemsInJson = 40; + + static attributeTypes = { + attribute4: TestModelAttributeType.boolean, + }; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: false, + }; + + static relations = [ + { + type: RelationType.hasMany, + relatedModel: 'Model12', + reverseRelation: true, + }, + { + type: RelationType.hasMany, + relatedModel: 'Model11', + reverseRelation: true, + }, + ]; +} + +export default Model14; diff --git a/test/performance/src/models/model2.ts b/test/performance/src/models/model2.ts new file mode 100644 index 0000000..622e70f --- /dev/null +++ b/test/performance/src/models/model2.ts @@ -0,0 +1,66 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel, { TestModelAttributeType } from './proto-test-model'; + +class Model2 extends ProtoTestModel { + static modelName = 'Model2'; + static numberOfItemsInJson = 115; + + static attributeTypes = { + attribute26: TestModelAttributeType.boolean, + }; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: '', + attribute6: '', + attribute7: '', + attribute8: '', + attribute9: '', + attribute10: '', + attribute11: '', + attribute12: '', + attribute13: '', + attribute14: '', + attribute15: '', + attribute16: '', + attribute17: '', + attribute18: '', + attribute19: '', + attribute20: '', + attribute21: '', + attribute22: '', + attribute23: '', + attribute24: '', + attribute25: '', + attribute26: false, + attribute27: '', + attribute28: '', + attribute29: '', + attribute30: '', + }; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model3', + reverseRelation: true, + }, + { type: RelationType.hasMany, relatedModel: 'Model12' }, + { type: RelationType.hasMany, relatedModel: 'Model5' }, + { type: RelationType.hasMany, relatedModel: 'Model11' }, + { type: RelationType.hasMany, relatedModel: 'Model7' }, + { + type: RelationType.hasOne, + relatedModel: 'Model9', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model10', + }, + ]; +} + +export default Model2; diff --git a/test/performance/src/models/model3.ts b/test/performance/src/models/model3.ts new file mode 100644 index 0000000..3513b69 --- /dev/null +++ b/test/performance/src/models/model3.ts @@ -0,0 +1,26 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel, { TestModelAttributeType } from './proto-test-model'; + +class Model3 extends ProtoTestModel { + static modelName = 'Model3'; + static numberOfItemsInJson = 6; + + static attributeTypes = { + attribute26: TestModelAttributeType.number, + }; + + static attributes = { + attribute1: '', + attribute2: 0, + }; + + static relations = [ + { + type: RelationType.hasMany, + relatedModel: 'Model2', + reverseRelation: true, + }, + ]; +} + +export default Model3; diff --git a/test/performance/src/models/model4.ts b/test/performance/src/models/model4.ts new file mode 100644 index 0000000..8816423 --- /dev/null +++ b/test/performance/src/models/model4.ts @@ -0,0 +1,35 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class Model4 extends ProtoTestModel { + static modelName = 'Model4'; + static numberOfItemsInJson = 32; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: '', + attribute6: '', + attribute7: '', + attribute8: '', + attribute9: '', + attribute10: '', + attribute11: '', + attribute12: '', + attribute13: '', + attribute14: '', + attribute15: '', + attribute16: '', + }; + + static relations = [ + { + type: RelationType.hasMany, + relatedModel: 'Model8', + }, + ]; +} + +export default Model4; diff --git a/test/performance/src/models/model5.ts b/test/performance/src/models/model5.ts new file mode 100644 index 0000000..700ec32 --- /dev/null +++ b/test/performance/src/models/model5.ts @@ -0,0 +1,41 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class Model5 extends ProtoTestModel { + static modelName = 'Model5'; + static numberOfItemsInJson = 1508; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: '', + attribute6: '', + attribute7: '', + attribute8: '', + attribute9: '', + attribute10: '', + attribute11: '', + }; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model2', + reverseRelation: true, + }, + { + type: RelationType.hasOne, + relatedModel: 'Model6', + reverseRelation: true, + }, + { + type: RelationType.hasOne, + relatedModel: 'Model8', + reverseRelation: true, + }, + ]; +} + +export default Model5; diff --git a/test/performance/src/models/model6.ts b/test/performance/src/models/model6.ts new file mode 100644 index 0000000..8c0eb81 --- /dev/null +++ b/test/performance/src/models/model6.ts @@ -0,0 +1,72 @@ +const times = require('lodash/times'); +const random = require('lodash/random'); + +import ProtoTestModel, { TestModelAttributeType } from './proto-test-model'; +import { RelationType } from '../../../../lib/mobx_model'; + +interface IItem { + itemAttribute1: string; + itemAttribute2: number; +} + +class Model6 extends ProtoTestModel { + static modelName = 'Model6'; + static numberOfItemsInJson = 26; + + static attributeTypes = { + attribute24: TestModelAttributeType.number, + }; + + static generateAttributeValue(attributeName: string): any { + if (attributeName === 'attribute5') { + const values: IItem[] = times(random(6), () => ({ + itemAttribute1: ProtoTestModel.generateRandomString(), + itemAttribute2: ProtoTestModel.generateRandomNumber(), + })); + + return values; + } + + return super.generateAttributeValue(attributeName); + } + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: [], + attribute6: '', + attribute7: '', + attribute8: '', + attribute9: '', + attribute10: '', + attribute11: '', + attribute12: '', + attribute13: '', + attribute14: '', + attribute15: '', + attribute16: '', + attribute17: '', + attribute18: '', + attribute19: '', + attribute20: '', + attribute21: '', + attribute22: '', + attribute23: '', + attribute24: 0, + attribute25: '', + attribute26: '', + attribute27: '', + attribute28: '', + attribute29: '', + attribute30: '', + }; + + static relations = [ + { type: RelationType.hasMany, relatedModel: 'Model5' }, + { type: RelationType.hasMany, relatedModel: 'Model7' }, + ]; +} + +export default Model6; diff --git a/test/performance/src/models/model7.ts b/test/performance/src/models/model7.ts new file mode 100644 index 0000000..b6fc9bd --- /dev/null +++ b/test/performance/src/models/model7.ts @@ -0,0 +1,47 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class Model7 extends ProtoTestModel { + static modelName = 'Model7'; + static numberOfItemsInJson = 239; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + attribute5: '', + attribute6: '', + attribute7: '', + attribute8: '', + attribute9: '', + attribute10: '', + attribute11: '', + attribute12: '', + attribute13: '', + attribute14: '', + attribute15: '', + attribute16: '', + attribute17: '', + }; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model2', + reverseRelation: true, + }, + { + type: RelationType.hasOne, + relatedModel: 'Model6', + reverseRelation: true, + }, + { + type: RelationType.hasOne, + relatedModel: 'Model8', + reverseRelation: true, + }, + ]; +} + +export default Model7; diff --git a/test/performance/src/models/model8.ts b/test/performance/src/models/model8.ts new file mode 100644 index 0000000..e10a796 --- /dev/null +++ b/test/performance/src/models/model8.ts @@ -0,0 +1,23 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class Model8 extends ProtoTestModel { + static modelName = 'Model8'; + static numberOfItemsInJson = 71; + + static attributes = { + attribute1: '', + }; + + static relations = [ + { type: RelationType.hasMany, relatedModel: 'Model5' }, + { type: RelationType.hasMany, relatedModel: 'Model7' }, + { + type: RelationType.hasOne, + relatedModel: 'Model4', + reverseRelation: true, + }, + ]; +} + +export default Model8; diff --git a/test/performance/src/models/model9.ts b/test/performance/src/models/model9.ts new file mode 100644 index 0000000..f9334a7 --- /dev/null +++ b/test/performance/src/models/model9.ts @@ -0,0 +1,24 @@ +import { RelationType } from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class Model9 extends ProtoTestModel { + static modelName = 'Model9'; + static numberOfItemsInJson = 46; + + static relations = [ + { + type: RelationType.hasOne, + relatedModel: 'Model2', + reverseRelation: true, + }, + ]; + + static attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + attribute4: '', + }; +} + +export default Model9; diff --git a/test/performance/src/models/proto-test-model.ts b/test/performance/src/models/proto-test-model.ts new file mode 100644 index 0000000..4b839e6 --- /dev/null +++ b/test/performance/src/models/proto-test-model.ts @@ -0,0 +1,135 @@ +import MobxModel, { MobxModelRelation, RelationType } from '../../../../lib'; +import { lowercaseFirstLetter } from '../../../../lib/utils'; +import { foreign_key, pluralize, singularize, tableize } from 'inflection'; +const random = require('lodash/random'); +const times = require('lodash/times'); +const sample = require('lodash/sample'); + +export enum TestModelAttributeType { + string, + number, + boolean, +} + +export default class ProtoTestModel extends MobxModel { + static attributes = {}; + static attributeTypes: { + [name: string]: TestModelAttributeType; + } = {}; + static relations: MobxModelRelation[] = []; + static numberOfItemsInJson: number = 5; + + static generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = (Math.random() * 16) | 0, + v = c == 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } + + static generateRandomString(): string { + return times(20, () => random(35).toString(36)).join(''); + } + + static generateRandomNumber(): number { + return random(100000); + } + + static generateRandomBoolean(): boolean { + return sample(true, false); + } + + static generateAttributeValue(attributeName: string): any { + const attributeType = this.attributeTypes[attributeName]; + + switch (attributeType) { + case TestModelAttributeType.boolean: + return ProtoTestModel.generateRandomBoolean(); + + case TestModelAttributeType.number: + return ProtoTestModel.generateRandomNumber(); + + case TestModelAttributeType.string: + default: + return ProtoTestModel.generateRandomString(); + } + } + + static generateAttributesJson() { + const attributesNames = Object.keys(this.attributes); + + const initialJson = { + id: ProtoTestModel.generateUUID(), + }; + + const json = attributesNames.reduce((acc, attributeName) => { + const attributeValue = this.generateAttributeValue(attributeName); + acc[attributeName] = attributeValue; + + return acc; + }, initialJson); + + return json; + } + + static generateAttributesJsonList() { + return times(this.numberOfItemsInJson, () => this.generateAttributesJson()); + } + + static generateRelationsIds(relationsJson: any) { + const relationsIds = this.relations.reduce((acc, relation) => { + const isHasMany = relation.type === RelationType.hasMany; + + if (isHasMany) return acc; + + const relationTopLevelJsonKey = this.getRelationTopLevelJsonKey(relation); + const foreignKey = this.getRelationForeignKey(relation); + + const relatedEntities = relationsJson[relationTopLevelJsonKey]; + + const randomEntity = sample(relatedEntities); + + acc[foreignKey] = randomEntity ? randomEntity.id : void 0; + + return acc; + }, {}); + + return relationsIds; + } + + private static getRelationPropertyName(relation: MobxModelRelation) { + const relatedModel = MobxModel.getModel(relation.relatedModel); + + let propertyName = + relation.propertyName || + lowercaseFirstLetter(relatedModel.modelName || relatedModel.name); + + if (relation.isHasMany) { + propertyName = pluralize(propertyName); + } + + return propertyName; + } + + private static getRelationTopLevelJsonKey(relation: MobxModelRelation) { + if (relation.topLevelJsonKey) return relation.topLevelJsonKey; + + const propertyName = this.getRelationPropertyName(relation); + + return tableize(propertyName); + } + + private static getRelationForeignKey(relation: MobxModelRelation) { + if (relation.foreignKey) return relation.foreignKey; + + const isHasMany = relation.type === RelationType.hasMany; + + const propertyName = this.getRelationPropertyName(relation); + + if (isHasMany) { + return foreign_key(singularize(propertyName)) + 's'; + } else { + return foreign_key(propertyName); + } + } +} diff --git a/test/performance/src/models/root-model.ts b/test/performance/src/models/root-model.ts new file mode 100644 index 0000000..dc927f2 --- /dev/null +++ b/test/performance/src/models/root-model.ts @@ -0,0 +1,112 @@ +import MobxModel, { + RelationType, + MobxModelRelationItem, + MobxModelSetOptions, +} from '../../../../lib/mobx_model'; +import ProtoTestModel from './proto-test-model'; + +class RootModel extends MobxModel { + constructor() { + super({ modelJson: {} }); + } + + set(options: MobxModelSetOptions) { + super.set(options); + } + + static relations = [ + { + type: RelationType.hasMany, + relatedModel: 'Model1', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model2', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model3', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model4', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model5', + }, + { type: RelationType.hasMany, relatedModel: 'Model6' }, + { + type: RelationType.hasMany, + relatedModel: 'Model7', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model8', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model9', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model10', + }, + { + type: RelationType.hasMany, + relatedModel: 'Model11', + }, + { type: RelationType.hasMany, relatedModel: 'Model12' }, + { + type: RelationType.hasMany, + relatedModel: 'Model13', + }, + { type: RelationType.hasMany, relatedModel: 'Model14' }, + ]; + + generateAttributesJson() { + const json = RootModel.relations.reduce( + (acc, relation: MobxModelRelationItem) => { + const { relatedModel, topLevelJsonKey } = relation; + + if (relatedModel.prototype instanceof ProtoTestModel) { + acc[topLevelJsonKey] = relatedModel.generateAttributesJsonList(); + } + + return acc; + }, + {}, + ); + + return json; + } + + setRelationsIdsToJson(attributesJson: any) { + const json = RootModel.relations.reduce( + (acc, relation: MobxModelRelationItem) => { + const { relatedModel, jsonKey } = relation; + + if (relatedModel.prototype instanceof ProtoTestModel) { + acc[jsonKey] = acc[jsonKey].map(item => ({ + ...item, + ...relatedModel.generateRelationsIds(attributesJson), + })); + } + + return acc; + }, + attributesJson, + ); + + return json; + } + + generateJson() { + const attributesJson = this.generateAttributesJson(); + const json = this.setRelationsIdsToJson(attributesJson); + + return json; + } +} + +export default RootModel;