universo-platform-3d

Форк
0
361 строка · 10.1 Кб
1
import {
2
  ForbiddenException,
3
  Injectable,
4
  NotFoundException
5
} from '@nestjs/common'
6
import { InjectModel } from '@nestjs/mongoose'
7
import { Model, PipelineStage } from 'mongoose'
8
import { CreateScriptEntityDto } from './dto/create-script-entity.dto'
9
import { UpdateScriptEntityDto } from './dto/update-script-entity.dto'
10
import { ScriptEntity, ScriptEntityDocument } from './script-entity.schema'
11
import { UserId, aggregationMatchId } from '../util/mongo-object-id-helpers'
12
import { UserService } from '../user/user.service'
13
import { AggregationPipelines } from '../util/aggregation-pipelines/aggregation-pipelines'
14
import { ObjectId } from 'mongodb'
15
import { ROLE } from '../roles/models/role.enum'
16
import { RoleService } from '../roles/role.service'
17

18
// Example with Postman: https://www.loom.com/share/3e115ba20b2f4e4c9d3aba85f1f4f72e?from_recorder=1&focus_title=1
19
@Injectable()
20
export class ScriptEntityService {
21
  constructor(
22
    @InjectModel(ScriptEntity.name)
23
    private scriptEntityModel: Model<ScriptEntityDocument>,
24
    private readonly userService: UserService,
25
    private readonly roleService: RoleService
26
  ) {}
27

28
  async create(
29
    userId: UserId,
30
    createScriptEntityDto: CreateScriptEntityDto
31
  ): Promise<ScriptEntityDocument> {
32
    const created = new this.scriptEntityModel(createScriptEntityDto)
33
    const role = await this.roleService.create({
34
      defaultRole:
35
        createScriptEntityDto.defaultRole ?? this._getDefaultRoleForScripts,
36
      creator: userId
37
    })
38
    created.role = role
39
    const createdScript = await created.save()
40

41
    await this.addScriptToUserRecents(userId, createdScript._id)
42
    return createdScript
43
  }
44

45
  async findOne(id: string): Promise<ScriptEntityDocument> {
46
    return await this.scriptEntityModel.findById(id).exec()
47
  }
48

49
  // get script with role check
50
  public async findOneWithRolesCheck(id: string, userId: UserId) {
51
    let pipeline = this.roleService.getRoleCheckAggregationPipeline(
52
      userId,
53
      ROLE.OBSERVER
54
    )
55

56
    // change standart roles check pipline, because there may be scripts without the role field
57
    pipeline = this.updateRoleCheckPipelineForEntityWithoutRoleField(pipeline)
58

59
    pipeline.unshift(aggregationMatchId(id))
60

61
    const [script] = await this.scriptEntityModel.aggregate(pipeline)
62

63
    if (!script) {
64
      throw new NotFoundException('Script not found')
65
    }
66

67
    return script
68
  }
69

70
  async update(
71
    id: string,
72
    updateScriptEntityDto: UpdateScriptEntityDto
73
  ): Promise<ScriptEntityDocument> {
74
    return await this.scriptEntityModel
75
      .findByIdAndUpdate(id, updateScriptEntityDto, { new: true })
76
      .exec()
77
  }
78

79
  // update script with role check
80
  public async updateWithRolesCheck(
81
    id: string,
82
    updateScriptEntityDto: UpdateScriptEntityDto,
83
    userId: UserId
84
  ) {
85
    const script = await this.findOne(id)
86

87
    if (!script) {
88
      throw new NotFoundException('Script not found')
89
    }
90

91
    // check if the user have the role to update the script
92
    // if the script has a role field
93
    if (script.role) {
94
      const roleCheck = await this.roleService.checkUserRoleForEntity(
95
        userId,
96
        id,
97
        ROLE.MANAGER,
98
        this.scriptEntityModel
99
      )
100

101
      if (!roleCheck) {
102
        throw new ForbiddenException(
103
          'You do not have permission to update this script'
104
        )
105
      }
106
    }
107

108
    return await this.update(id, updateScriptEntityDto)
109
  }
110

111
  async delete(id: string): Promise<ScriptEntityDocument> {
112
    return await this.scriptEntityModel
113
      .findOneAndDelete({ _id: id }, { new: true })
114
      .exec()
115
  }
116

117
  // delete script with role check
118
  public async deleteWithRolesCheck(id: string, userId: UserId) {
119
    const script = await this.findOne(id)
120

121
    if (!script) {
122
      throw new NotFoundException('Script not found')
123
    }
124

125
    // check if the user have the role to delete the script
126
    // if the script has a role field
127
    if (script.role) {
128
      const roleCheck = await this.roleService.checkUserRoleForEntity(
129
        userId,
130
        id,
131
        ROLE.MANAGER,
132
        this.scriptEntityModel
133
      )
134

135
      if (!roleCheck) {
136
        throw new ForbiddenException(
137
          'You do not have permission to delete this script'
138
        )
139
      }
140
    }
141
    return await this.delete(id)
142
  }
143

144
  async getRecentScripts(userId: UserId) {
145
    const userRecents = await this.userService.getUserRecents(userId)
146
    const scriptsIds = userRecents?.scripts || []
147

148
    const pipelineQuery: PipelineStage[] =
149
      AggregationPipelines.getPipelineForGetByIdOrdered(scriptsIds)
150

151
    return await this.scriptEntityModel.aggregate(pipelineQuery)
152
  }
153

154
  async addScriptToUserRecents(userId: UserId, scriptId: string) {
155
    const userRecents = await this.userService.getUserRecents(userId)
156
    const scripts = userRecents?.scripts || []
157

158
    const existingSpaceIndex = scripts.indexOf(scriptId)
159

160
    if (existingSpaceIndex >= 0) {
161
      scripts.splice(existingSpaceIndex, 1)
162
    } else if (scripts.length === 10) {
163
      scripts.pop()
164
    }
165

166
    scripts.unshift(scriptId)
167

168
    await this.userService.updateUserRecentScripts(userId, scripts)
169
  }
170

171
  async duplicateScriptsAndScriptInstanceScripts(
172
    scriptIds: string[],
173
    scriptInstances: any[],
174
    userId: UserId
175
  ) {
176
    // array of unique script ids
177
    const duplicatedScriptIds = [
178
      ...new Set(
179
        scriptIds.concat(
180
          scriptInstances.map((instance) => instance.get('script_id'))
181
        )
182
      )
183
    ]
184

185
    const scripts = await this.scriptEntityModel.find({
186
      _id: { $in: duplicatedScriptIds }
187
    })
188

189
    // array of objects with old and new ids where old id is the key and new id is the value
190
    const duplicatedScriptsIdsWithNewIds = []
191
    const duplicatedScripts = await Promise.all(
192
      scripts.map(async (script) => {
193
        script = script.toObject()
194
        const newObjectId = new ObjectId().toString()
195
        duplicatedScriptsIdsWithNewIds.push({
196
          [script._id.toString()]: newObjectId
197
        })
198
        script._id = newObjectId
199

200
        script.role = await this.roleService.create({
201
          defaultRole: this._getDefaultRoleForScripts,
202
          creator: userId
203
        })
204

205
        // remove id if exists
206
        if (script.id) {
207
          delete script.id
208
        }
209
        return script
210
      })
211
    )
212

213
    // new scriptIds
214
    const filterNewScriptIds = duplicatedScriptsIdsWithNewIds
215
      .filter((obj) => scriptIds.includes(Object.keys(obj)[0]))
216
      .map((obj) => obj[Object.keys(obj)[0]])
217

218
    // scriptInstances with updated scriptIds
219
    const filterAndUpdatedNewScriptInstances = scriptInstances.map(
220
      (instance) => {
221
        duplicatedScriptsIdsWithNewIds.map((el) => {
222
          const objKey = Object.keys(el)[0]
223
          if (objKey === instance.get('script_id')) {
224
            instance.set('script_id', el[objKey])
225
          }
226
        })
227
        return instance
228
      }
229
    )
230
    //save duplicated scripts
231
    await this.scriptEntityModel.insertMany(duplicatedScripts)
232
    return {
233
      scriptIds: filterNewScriptIds,
234
      scriptInstances: filterAndUpdatedNewScriptInstances
235
    }
236
  }
237

238
  async duplicateSpaceObjectScripts(spaceObjects: any[], userId: UserId) {
239
    // array script ids
240
    const scriptsIds = []
241

242
    //get all script ids from spaceObjects.scriptEvents
243
    spaceObjects.map((spaceObject) => {
244
      spaceObject.scriptEvents.map((script) => {
245
        scriptsIds.push(script.script_id)
246
      })
247
    })
248

249
    const scripts = await this.scriptEntityModel.find({
250
      _id: { $in: scriptsIds }
251
    })
252

253
    // array of objects with old and new ids where old id is the key and new id is the value
254
    const duplicatedScriptsIdsWithNewIds = []
255
    const duplicatedScripts = await Promise.all(
256
      scripts.map(async (script) => {
257
        script = script.toObject()
258
        const newObjectId = new ObjectId().toString()
259
        duplicatedScriptsIdsWithNewIds.push({
260
          [script._id.toString()]: newObjectId
261
        })
262
        script._id = newObjectId
263

264
        script.role = await this.roleService.create({
265
          defaultRole: this._getDefaultRoleForScripts,
266
          creator: userId
267
        })
268

269
        // remove id if exists
270
        if (script.id) {
271
          delete script.id
272
        }
273
        return script
274
      })
275
    )
276

277
    //save duplicated scripts
278
    await this.scriptEntityModel.insertMany(duplicatedScripts)
279

280
    // geenerate bulk operations for update spaceObjects
281
    const bulkOps = []
282
    for (let i = 0; i < spaceObjects.length; i++) {
283
      const spaceObject = spaceObjects[i]
284

285
      const updatedScriptEvents = []
286
      spaceObject.scriptEvents.map((script) => {
287
        duplicatedScriptsIdsWithNewIds.map((el) => {
288
          const objKey = Object.keys(el)[0]
289
          if (objKey === script.script_id) {
290
            script.script_id = el[objKey]
291
            updatedScriptEvents.push(script)
292
          }
293
        })
294
      })
295

296
      const bulkOp = {
297
        updateOne: {
298
          filter: { _id: spaceObjects[i]._id },
299
          update: [
300
            {
301
              $set: {
302
                scriptEvents: updatedScriptEvents
303
              }
304
            }
305
          ]
306
        }
307
      }
308
      bulkOps.push(bulkOp)
309
    }
310
    return bulkOps
311
  }
312

313
  async restoreScriptEntities(
314
    scriptEntities: Map<string, any>[],
315
    userId: UserId
316
  ) {
317
    const newScriptIds = []
318

319
    const newScriptEntities = scriptEntities.map(async (script) => {
320
      const newScript = Object.fromEntries(script)
321
      const newScriptId = new ObjectId()
322

323
      newScriptIds.push({
324
        [script.get('_id'.toString())]: newScriptId.toString()
325
      })
326

327
      newScript._id = newScriptId
328
      newScript.role = await this.roleService.create({
329
        defaultRole: this._getDefaultRoleForScripts,
330
        creator: userId
331
      })
332

333
      return newScript
334
    })
335

336
    await this.scriptEntityModel.insertMany(newScriptEntities)
337

338
    return newScriptIds
339
  }
340

341
  private readonly _getDefaultRoleForScripts = ROLE.OBSERVER
342

343
  // We need this because we can't set the author for all scripts and assign them roles
344
  private updateRoleCheckPipelineForEntityWithoutRoleField(
345
    pipeline: PipelineStage[]
346
  ) {
347
    const match = pipeline[0]['$match']
348

349
    // return element without role field without checking for a role
350
    pipeline[0]['$match'] = {
351
      $or: [
352
        {
353
          role: { $exists: false }
354
        },
355
        match
356
      ]
357
    }
358

359
    return pipeline
360
  }
361
}
362

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.