diff options
Diffstat (limited to 'server/models/shared/model-builder.ts')
-rw-r--r-- | server/models/shared/model-builder.ts | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/server/models/shared/model-builder.ts b/server/models/shared/model-builder.ts new file mode 100644 index 000000000..c015ca4f5 --- /dev/null +++ b/server/models/shared/model-builder.ts | |||
@@ -0,0 +1,101 @@ | |||
1 | import { isPlainObject } from 'lodash' | ||
2 | import { Model as SequelizeModel, Sequelize } from 'sequelize' | ||
3 | import { logger } from '@server/helpers/logger' | ||
4 | |||
5 | export class ModelBuilder <T extends SequelizeModel> { | ||
6 | private readonly modelRegistry = new Map<string, T>() | ||
7 | |||
8 | constructor (private readonly sequelize: Sequelize) { | ||
9 | |||
10 | } | ||
11 | |||
12 | createModels (jsonArray: any[], baseModelName: string): T[] { | ||
13 | const result: T[] = [] | ||
14 | |||
15 | for (const json of jsonArray) { | ||
16 | const { created, model } = this.createModel(json, baseModelName, json.id + '.' + baseModelName) | ||
17 | |||
18 | if (created) result.push(model) | ||
19 | } | ||
20 | |||
21 | return result | ||
22 | } | ||
23 | |||
24 | private createModel (json: any, modelName: string, keyPath: string) { | ||
25 | if (!json.id) return { created: false, model: null } | ||
26 | |||
27 | const { created, model } = this.createOrFindModel(json, modelName, keyPath) | ||
28 | |||
29 | for (const key of Object.keys(json)) { | ||
30 | const value = json[key] | ||
31 | if (!value) continue | ||
32 | |||
33 | // Child model | ||
34 | if (isPlainObject(value)) { | ||
35 | const { created, model: subModel } = this.createModel(value, key, keyPath + '.' + json.id + '.' + key) | ||
36 | if (!created || !subModel) continue | ||
37 | |||
38 | const Model = this.findModelBuilder(modelName) | ||
39 | const association = Model.associations[key] | ||
40 | |||
41 | if (!association) { | ||
42 | logger.error('Cannot find association %s of model %s', key, modelName, { associations: Object.keys(Model.associations) }) | ||
43 | continue | ||
44 | } | ||
45 | |||
46 | if (association.isMultiAssociation) { | ||
47 | if (!Array.isArray(model[key])) model[key] = [] | ||
48 | |||
49 | model[key].push(subModel) | ||
50 | } else { | ||
51 | model[key] = subModel | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | return { created, model } | ||
57 | } | ||
58 | |||
59 | private createOrFindModel (json: any, modelName: string, keyPath: string) { | ||
60 | const registryKey = this.getModelRegistryKey(json, keyPath) | ||
61 | if (this.modelRegistry.has(registryKey)) { | ||
62 | return { | ||
63 | created: false, | ||
64 | model: this.modelRegistry.get(registryKey) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | const Model = this.findModelBuilder(modelName) | ||
69 | |||
70 | if (!Model) { | ||
71 | logger.error( | ||
72 | 'Cannot build model %s that does not exist', this.buildSequelizeModelName(modelName), | ||
73 | { existing: this.sequelize.modelManager.all.map(m => m.name) } | ||
74 | ) | ||
75 | return undefined | ||
76 | } | ||
77 | |||
78 | // FIXME: typings | ||
79 | const model = new (Model as any)(json) | ||
80 | this.modelRegistry.set(registryKey, model) | ||
81 | |||
82 | return { created: true, model } | ||
83 | } | ||
84 | |||
85 | private findModelBuilder (modelName: string) { | ||
86 | return this.sequelize.modelManager.getModel(this.buildSequelizeModelName(modelName)) | ||
87 | } | ||
88 | |||
89 | private buildSequelizeModelName (modelName: string) { | ||
90 | if (modelName === 'Avatars') return 'ActorImageModel' | ||
91 | if (modelName === 'ActorFollowing') return 'ActorModel' | ||
92 | if (modelName === 'ActorFollower') return 'ActorModel' | ||
93 | if (modelName === 'FlaggedAccount') return 'AccountModel' | ||
94 | |||
95 | return modelName + 'Model' | ||
96 | } | ||
97 | |||
98 | private getModelRegistryKey (json: any, keyPath: string) { | ||
99 | return keyPath + json.id | ||
100 | } | ||
101 | } | ||