universo-platform-3d
361 строка · 10.1 Кб
1import {2ForbiddenException,3Injectable,4NotFoundException
5} from '@nestjs/common'6import { InjectModel } from '@nestjs/mongoose'7import { Model, PipelineStage } from 'mongoose'8import { CreateScriptEntityDto } from './dto/create-script-entity.dto'9import { UpdateScriptEntityDto } from './dto/update-script-entity.dto'10import { ScriptEntity, ScriptEntityDocument } from './script-entity.schema'11import { UserId, aggregationMatchId } from '../util/mongo-object-id-helpers'12import { UserService } from '../user/user.service'13import { AggregationPipelines } from '../util/aggregation-pipelines/aggregation-pipelines'14import { ObjectId } from 'mongodb'15import { ROLE } from '../roles/models/role.enum'16import { RoleService } from '../roles/role.service'17
18// Example with Postman: https://www.loom.com/share/3e115ba20b2f4e4c9d3aba85f1f4f72e?from_recorder=1&focus_title=1
19@Injectable()20export class ScriptEntityService {21constructor(22@InjectModel(ScriptEntity.name)23private scriptEntityModel: Model<ScriptEntityDocument>,24private readonly userService: UserService,25private readonly roleService: RoleService26) {}27
28async create(29userId: UserId,30createScriptEntityDto: CreateScriptEntityDto31): Promise<ScriptEntityDocument> {32const created = new this.scriptEntityModel(createScriptEntityDto)33const role = await this.roleService.create({34defaultRole:35createScriptEntityDto.defaultRole ?? this._getDefaultRoleForScripts,36creator: userId37})38created.role = role39const createdScript = await created.save()40
41await this.addScriptToUserRecents(userId, createdScript._id)42return createdScript43}44
45async findOne(id: string): Promise<ScriptEntityDocument> {46return await this.scriptEntityModel.findById(id).exec()47}48
49// get script with role check50public async findOneWithRolesCheck(id: string, userId: UserId) {51let pipeline = this.roleService.getRoleCheckAggregationPipeline(52userId,53ROLE.OBSERVER54)55
56// change standart roles check pipline, because there may be scripts without the role field57pipeline = this.updateRoleCheckPipelineForEntityWithoutRoleField(pipeline)58
59pipeline.unshift(aggregationMatchId(id))60
61const [script] = await this.scriptEntityModel.aggregate(pipeline)62
63if (!script) {64throw new NotFoundException('Script not found')65}66
67return script68}69
70async update(71id: string,72updateScriptEntityDto: UpdateScriptEntityDto73): Promise<ScriptEntityDocument> {74return await this.scriptEntityModel75.findByIdAndUpdate(id, updateScriptEntityDto, { new: true })76.exec()77}78
79// update script with role check80public async updateWithRolesCheck(81id: string,82updateScriptEntityDto: UpdateScriptEntityDto,83userId: UserId84) {85const script = await this.findOne(id)86
87if (!script) {88throw new NotFoundException('Script not found')89}90
91// check if the user have the role to update the script92// if the script has a role field93if (script.role) {94const roleCheck = await this.roleService.checkUserRoleForEntity(95userId,96id,97ROLE.MANAGER,98this.scriptEntityModel99)100
101if (!roleCheck) {102throw new ForbiddenException(103'You do not have permission to update this script'104)105}106}107
108return await this.update(id, updateScriptEntityDto)109}110
111async delete(id: string): Promise<ScriptEntityDocument> {112return await this.scriptEntityModel113.findOneAndDelete({ _id: id }, { new: true })114.exec()115}116
117// delete script with role check118public async deleteWithRolesCheck(id: string, userId: UserId) {119const script = await this.findOne(id)120
121if (!script) {122throw new NotFoundException('Script not found')123}124
125// check if the user have the role to delete the script126// if the script has a role field127if (script.role) {128const roleCheck = await this.roleService.checkUserRoleForEntity(129userId,130id,131ROLE.MANAGER,132this.scriptEntityModel133)134
135if (!roleCheck) {136throw new ForbiddenException(137'You do not have permission to delete this script'138)139}140}141return await this.delete(id)142}143
144async getRecentScripts(userId: UserId) {145const userRecents = await this.userService.getUserRecents(userId)146const scriptsIds = userRecents?.scripts || []147
148const pipelineQuery: PipelineStage[] =149AggregationPipelines.getPipelineForGetByIdOrdered(scriptsIds)150
151return await this.scriptEntityModel.aggregate(pipelineQuery)152}153
154async addScriptToUserRecents(userId: UserId, scriptId: string) {155const userRecents = await this.userService.getUserRecents(userId)156const scripts = userRecents?.scripts || []157
158const existingSpaceIndex = scripts.indexOf(scriptId)159
160if (existingSpaceIndex >= 0) {161scripts.splice(existingSpaceIndex, 1)162} else if (scripts.length === 10) {163scripts.pop()164}165
166scripts.unshift(scriptId)167
168await this.userService.updateUserRecentScripts(userId, scripts)169}170
171async duplicateScriptsAndScriptInstanceScripts(172scriptIds: string[],173scriptInstances: any[],174userId: UserId175) {176// array of unique script ids177const duplicatedScriptIds = [178...new Set(179scriptIds.concat(180scriptInstances.map((instance) => instance.get('script_id'))181)182)183]184
185const 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 value190const duplicatedScriptsIdsWithNewIds = []191const duplicatedScripts = await Promise.all(192scripts.map(async (script) => {193script = script.toObject()194const newObjectId = new ObjectId().toString()195duplicatedScriptsIdsWithNewIds.push({196[script._id.toString()]: newObjectId197})198script._id = newObjectId199
200script.role = await this.roleService.create({201defaultRole: this._getDefaultRoleForScripts,202creator: userId203})204
205// remove id if exists206if (script.id) {207delete script.id208}209return script210})211)212
213// new scriptIds214const filterNewScriptIds = duplicatedScriptsIdsWithNewIds215.filter((obj) => scriptIds.includes(Object.keys(obj)[0]))216.map((obj) => obj[Object.keys(obj)[0]])217
218// scriptInstances with updated scriptIds219const filterAndUpdatedNewScriptInstances = scriptInstances.map(220(instance) => {221duplicatedScriptsIdsWithNewIds.map((el) => {222const objKey = Object.keys(el)[0]223if (objKey === instance.get('script_id')) {224instance.set('script_id', el[objKey])225}226})227return instance228}229)230//save duplicated scripts231await this.scriptEntityModel.insertMany(duplicatedScripts)232return {233scriptIds: filterNewScriptIds,234scriptInstances: filterAndUpdatedNewScriptInstances235}236}237
238async duplicateSpaceObjectScripts(spaceObjects: any[], userId: UserId) {239// array script ids240const scriptsIds = []241
242//get all script ids from spaceObjects.scriptEvents243spaceObjects.map((spaceObject) => {244spaceObject.scriptEvents.map((script) => {245scriptsIds.push(script.script_id)246})247})248
249const 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 value254const duplicatedScriptsIdsWithNewIds = []255const duplicatedScripts = await Promise.all(256scripts.map(async (script) => {257script = script.toObject()258const newObjectId = new ObjectId().toString()259duplicatedScriptsIdsWithNewIds.push({260[script._id.toString()]: newObjectId261})262script._id = newObjectId263
264script.role = await this.roleService.create({265defaultRole: this._getDefaultRoleForScripts,266creator: userId267})268
269// remove id if exists270if (script.id) {271delete script.id272}273return script274})275)276
277//save duplicated scripts278await this.scriptEntityModel.insertMany(duplicatedScripts)279
280// geenerate bulk operations for update spaceObjects281const bulkOps = []282for (let i = 0; i < spaceObjects.length; i++) {283const spaceObject = spaceObjects[i]284
285const updatedScriptEvents = []286spaceObject.scriptEvents.map((script) => {287duplicatedScriptsIdsWithNewIds.map((el) => {288const objKey = Object.keys(el)[0]289if (objKey === script.script_id) {290script.script_id = el[objKey]291updatedScriptEvents.push(script)292}293})294})295
296const bulkOp = {297updateOne: {298filter: { _id: spaceObjects[i]._id },299update: [300{301$set: {302scriptEvents: updatedScriptEvents303}304}305]306}307}308bulkOps.push(bulkOp)309}310return bulkOps311}312
313async restoreScriptEntities(314scriptEntities: Map<string, any>[],315userId: UserId316) {317const newScriptIds = []318
319const newScriptEntities = scriptEntities.map(async (script) => {320const newScript = Object.fromEntries(script)321const newScriptId = new ObjectId()322
323newScriptIds.push({324[script.get('_id'.toString())]: newScriptId.toString()325})326
327newScript._id = newScriptId328newScript.role = await this.roleService.create({329defaultRole: this._getDefaultRoleForScripts,330creator: userId331})332
333return newScript334})335
336await this.scriptEntityModel.insertMany(newScriptEntities)337
338return newScriptIds339}340
341private readonly _getDefaultRoleForScripts = ROLE.OBSERVER342
343// We need this because we can't set the author for all scripts and assign them roles344private updateRoleCheckPipelineForEntityWithoutRoleField(345pipeline: PipelineStage[]346) {347const match = pipeline[0]['$match']348
349// return element without role field without checking for a role350pipeline[0]['$match'] = {351$or: [352{353role: { $exists: false }354},355match
356]357}358
359return pipeline360}361}
362