]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/shared/model-builder.ts
Fix peertube subtitles import
[github/Chocobozzz/PeerTube.git] / server / models / shared / model-builder.ts
CommitLineData
d0800f76 1import { isPlainObject } from 'lodash'
cde3d90d 2import { Model as SequelizeModel, ModelStatic, Sequelize } from 'sequelize'
d0800f76 3import { logger } from '@server/helpers/logger'
4
cde3d90d
C
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
d0800f76 22export 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 )
cde3d90d 92 return { created: false, model: null }
d0800f76 93 }
94
cde3d90d
C
95 const model = Model.build(json, { raw: true, isNewRecord: false })
96
d0800f76 97 this.modelRegistry.set(registryKey, model)
98
99 return { created: true, model }
100 }
101
102 private findModelBuilder (modelName: string) {
cde3d90d 103 return this.sequelize.modelManager.getModel(this.buildSequelizeModelName(modelName)) as ModelStatic<T>
d0800f76 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}