diff options
Diffstat (limited to 'server/models/shared/model-builder.ts')
-rw-r--r-- | server/models/shared/model-builder.ts | 118 |
1 files changed, 0 insertions, 118 deletions
diff --git a/server/models/shared/model-builder.ts b/server/models/shared/model-builder.ts deleted file mode 100644 index 07f7c4038..000000000 --- a/server/models/shared/model-builder.ts +++ /dev/null | |||
@@ -1,118 +0,0 @@ | |||
1 | import { isPlainObject } from 'lodash' | ||
2 | import { Model as SequelizeModel, ModelStatic, Sequelize } from 'sequelize' | ||
3 | import { logger } from '@server/helpers/logger' | ||
4 | |||
5 | /** | ||
6 | * | ||
7 | * Build Sequelize models from sequelize raw query (that must use { nest: true } options) | ||
8 | * | ||
9 | * In order to sequelize to correctly build the JSON this class will ingest, | ||
10 | * the columns selected in the raw query should be in the following form: | ||
11 | * * All tables must be Pascal Cased (for example "VideoChannel") | ||
12 | * * Root table must end with `Model` (for example "VideoCommentModel") | ||
13 | * * Joined tables must contain the origin table name + '->JoinedTable'. For example: | ||
14 | * * "Actor" is joined to "Account": "Actor" table must be renamed "Account->Actor" | ||
15 | * * "Account->Actor" is joined to "Server": "Server" table must be renamed to "Account->Actor->Server" | ||
16 | * * Selected columns must be renamed to contain the JSON path: | ||
17 | * * "videoComment"."id": "VideoCommentModel"."id" | ||
18 | * * "Account"."Actor"."Server"."id": "Account.Actor.Server.id" | ||
19 | * * All tables must contain the row id | ||
20 | */ | ||
21 | |||
22 | export class ModelBuilder <T extends SequelizeModel> { | ||
23 | private readonly modelRegistry = new Map<string, T>() | ||
24 | |||
25 | constructor (private readonly sequelize: Sequelize) { | ||
26 | |||
27 | } | ||
28 | |||
29 | createModels (jsonArray: any[], baseModelName: string): T[] { | ||
30 | const result: T[] = [] | ||
31 | |||
32 | for (const json of jsonArray) { | ||
33 | const { created, model } = this.createModel(json, baseModelName, json.id + '.' + baseModelName) | ||
34 | |||
35 | if (created) result.push(model) | ||
36 | } | ||
37 | |||
38 | return result | ||
39 | } | ||
40 | |||
41 | private createModel (json: any, modelName: string, keyPath: string) { | ||
42 | if (!json.id) return { created: false, model: null } | ||
43 | |||
44 | const { created, model } = this.createOrFindModel(json, modelName, keyPath) | ||
45 | |||
46 | for (const key of Object.keys(json)) { | ||
47 | const value = json[key] | ||
48 | if (!value) continue | ||
49 | |||
50 | // Child model | ||
51 | if (isPlainObject(value)) { | ||
52 | const { created, model: subModel } = this.createModel(value, key, keyPath + '.' + json.id + '.' + key) | ||
53 | if (!created || !subModel) continue | ||
54 | |||
55 | const Model = this.findModelBuilder(modelName) | ||
56 | const association = Model.associations[key] | ||
57 | |||
58 | if (!association) { | ||
59 | logger.error('Cannot find association %s of model %s', key, modelName, { associations: Object.keys(Model.associations) }) | ||
60 | continue | ||
61 | } | ||
62 | |||
63 | if (association.isMultiAssociation) { | ||
64 | if (!Array.isArray(model[key])) model[key] = [] | ||
65 | |||
66 | model[key].push(subModel) | ||
67 | } else { | ||
68 | model[key] = subModel | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | return { created, model } | ||
74 | } | ||
75 | |||
76 | private createOrFindModel (json: any, modelName: string, keyPath: string) { | ||
77 | const registryKey = this.getModelRegistryKey(json, keyPath) | ||
78 | if (this.modelRegistry.has(registryKey)) { | ||
79 | return { | ||
80 | created: false, | ||
81 | model: this.modelRegistry.get(registryKey) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | const Model = this.findModelBuilder(modelName) | ||
86 | |||
87 | if (!Model) { | ||
88 | logger.error( | ||
89 | 'Cannot build model %s that does not exist', this.buildSequelizeModelName(modelName), | ||
90 | { existing: this.sequelize.modelManager.all.map(m => m.name) } | ||
91 | ) | ||
92 | return { created: false, model: null } | ||
93 | } | ||
94 | |||
95 | const model = Model.build(json, { raw: true, isNewRecord: false }) | ||
96 | |||
97 | this.modelRegistry.set(registryKey, model) | ||
98 | |||
99 | return { created: true, model } | ||
100 | } | ||
101 | |||
102 | private findModelBuilder (modelName: string) { | ||
103 | return this.sequelize.modelManager.getModel(this.buildSequelizeModelName(modelName)) as ModelStatic<T> | ||
104 | } | ||
105 | |||
106 | private buildSequelizeModelName (modelName: string) { | ||
107 | if (modelName === 'Avatars') return 'ActorImageModel' | ||
108 | if (modelName === 'ActorFollowing') return 'ActorModel' | ||
109 | if (modelName === 'ActorFollower') return 'ActorModel' | ||
110 | if (modelName === 'FlaggedAccount') return 'AccountModel' | ||
111 | |||
112 | return modelName + 'Model' | ||
113 | } | ||
114 | |||
115 | private getModelRegistryKey (json: any, keyPath: string) { | ||
116 | return keyPath + json.id | ||
117 | } | ||
118 | } | ||