# v1.16.11 ## Feature list * [universal channel] new command /join * [universal channel] action on button get started * [universal channel] fix history size 5 -> 20 * [universal channel] add posibility to de-attach facebook page, twilio phone number from universall session. Additional logic with "isMute:true" for member who you this platform * [universal channel] Join/leave event and settings for them(backend) * [universal channel] Invitation in universal session supprt array of users in body ## Bug fixes list * [workspace default roting channel] - workspace edit blade * [refresh api credentials token] - system-users/edit blade ## Services to deploy * rake-resource-defaults | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * rake user app module | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * admin service | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * main worker service | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * core service | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * user app worker | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * permissions service | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * widget | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | * redis-rabbitmq-event-handler | ENV | Status | | --- | ------ | | TEST| | | UAT | Done | | PROD| Done | ## Scripts ### Indexes | ENV | Status | | --- | ------ | | TEST| | | UAT | Done | | PROD| | ``` db.invitations.dropIndex("scope.type_1_scope.id_1_email_1"); db.invitations.createIndex( { "scope.type": 1, "scope.id": 1, email: 1 }, { unique: true, name: "scope.type_1_scope.id_1_email_1", partialFilterExpression: { $and: [ { email: { $exists: true }, }, { invitationTypeId: { $lt: 2 } }, ], } } ) db.getCollection("invitations").createIndex( { email: 1, "sessionId": 1 }, { unique: true, partialFilterExpression: { invitationTypeId: 2, sessionId: { $exists: true } } }) ``` ### SystemApplications (not this release) | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| | ``` db.getCollection("systemApplications").insert({ "systemApplicationId": 25, "name": "facebook-worker", "baseUrl": "", "systemReference": "facebookWorker" }) ``` ### SystemConfig | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | ``` db.systemConfigs.updateMany( { }, { $set: { 'templates.admin.invitation.universalPlatformReport': { templateId: 'd-84f64b4d343e4b9c8e588aabd5dd0b2f', "header": { "logo": "https://uploads-ssl.webflow.com/5d654ea941869b347d09667a/5f722e370db9f222e5b03558_FullColor_Rectangle_Small320x132.png", "title": "We have processed your file import" }, "hasErrors": false, "body": { "greeting": "File processing complete", "workspaceName":"{{workspaceName}}", "p1": "You recently submitted a file for processing in the {{workspaceName}} workspace. This email is to inform you that our processing is complete.", "file": "myemails-20211111.csv", "p2_success": "All records processed successfully", "p2_error": "To view a detailed log of the records that encountered errors, please click or tap the View Log File button or copy and paste this link in your browser.", "p3_error": "For additional information about using Rake's file import processing, please reference our ", "success_link": "https://app.rake.ai", "success_button": "Open Rake App", "error_link": "https://cdn.rake.ai/somefile/location/etc/etc/processingerrors.txt", "error_button": "View Log File", "p3": "Let's get the conversation started!", "sincerely": "Sincerely,", "signature": "https://uploads-ssl.webflow.com/5d654ea941869b347d09667a/5f723e634165e2a3f1edb20a_email_signature_RakeTeam.png" }, "footer": { "company": "Rake LLC | 19740 Governors Highway, Suite 115 | Flossmoor, IL 60422 USA", "website": "https://rake.ai" } } } }) ``` ### Facebook workspaces | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | - rake-scripts, branch: 1.16.11, mongo-db-scripts>1.16.11>facebook-workspace ### Channel owner/creator | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | ``` const sessions = db.sessions.find({ channel: { $exists: true }, 'channel.isDirect': false, $expr: { $gt: [{ $size: '$entities' }, 1] } }).toArray(); sessions.forEach((session, i) => { const entities = session.entities.filter(one => !one.isInitiator); const [entity] = entities; console.log(`[${i}:${sessions.length}]update `, session.sessionId) db.sessions.updateOne({ sessionId: session.sessionId }, { $set: { 'channel.ownerEntityId': entity.entityId, 'channel.creatorEntityId': entity.entityId, } }) }); ``` ### Message Entity Unread #### Directs | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | ``` const directs = db.sessions.aggregate([ { $match: { $or: [{ $and: [{ channel: { $exists: false } }, { sessionTtl: 31536000000 }] }, { 'channel.isDirect': true }] }, }, { $lookup: { from: 'workspaceSystemUsers', localField: 'entities.entityId', foreignField: 'referenceValues.entity.entityId', as: 'workspaceSystemUsers', }, }, { $project: { sessionId: 1, workspaceSystemUsers: 1, entities: 1, workspaceId: 1, channel: 1 }, }, { $addFields: { systemUserId: { $arrayElemAt: [ { $map: { input: '$workspaceSystemUsers', as: 'user', in: '$$user.systemUserId' } }, 0, ], }, }, }, { $addFields: { userWorkspaces: { $map: { input: { $filter: { input: '$workspaceSystemUsers', as: 'user', cond: { $eq: ['$$user.systemUserId', '$systemUserId'], }, }, }, as: 'workspace', in: '$$workspace.workspaceId', }, }, }, }, { $addFields: { intersection: { $setIntersection: [ { $map: { input: { $filter: { input: '$workspaceSystemUsers', as: 'user', cond: { $ne: ['$$user.systemUserId', '$systemUserId'] }, }, }, as: 'workspace', in: '$$workspace.workspaceId', }, }, '$userWorkspaces', ], }, }, }, { $project: { intersection: 1, sessionId: 1, entities: 1, workspaceId: 1, channel: 1 }, }, { $match: { $expr: { $gte: [{ $size: '$intersection' }, 1] } } } ]).toArray(); directs.forEach((session, index) => { console.log(`item: ${index + 1} of ${directs.length}`); const entities = session.channel ? session.entities.filter(one => !one.isInitiator) : session.entities; console.log('sessionId', session.sessionId); if (!entities.length || !session.intersection.length) { console.log('entities is empty', session.intersection.length); return; } entities.forEach((entity) => { db.messageEntityUnread.updateOne({ sessionId: session.sessionId, entityId: entity.entityId, }, { $set: { workspaceIds: session.intersection }, $setOnInsert: { messageIds: [] } }, { upsert: true }); }); }) ``` #### Channels and externals | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | ``` const channelsAndExternals = db.sessions.find({ $or: [ { $and: [ { channel: { $exists: true } }, { 'channel.isDirect': {$ne: true} }, { 'channel.isArchived': { $ne: true } }, { isActive: true }, ] }, { $and: [ { isActive: true }, { sessionTtl: { $ne: 31536000000 } } ] } ] }).toArray(); channelsAndExternals.forEach((session, index) => { console.log(`item: ${index + 1} of ${channelsAndExternals.length}`); console.log('sessionid', session.sessionId); const entities = session.sessionTtl === 31536000000 ? session.entities.filter(one => !one.isInitiator) : session.entities.filter(one => one.platformId.value === 8); if (!entities.length) { console.log('entities is empty'); return; } entities.forEach((entity) => { db.messageEntityUnread.updateOne({ sessionId: session.sessionId, entityId: entity.entityId, }, { $set: { workspaceIds: [session.workspaceId] }, $setOnInsert: { messageIds: [] } }, { upsert: true }); }); }); ``` #### Universal roles | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| | ``` const prototypes = [ {label: 'workspace', roleId: 7774, model: db.workspaces, getId: doc => doc.workspaceId } ]; for (const proto of prototypes){ const parentRoleId = proto.roleId; const [parentRole] = db.roles.find({ roleId: parentRoleId }).toArray(); const elements = proto.model.find({ }).projection({ "workspaceId": 1, "organizationId": 1, "projectId":1, "_id": 0 }).toArray(); for (const [i, doc] of elements.entries()){ const id = +proto.getId(doc).toString(); console.log(`[${i}:${elements.length}] Insert new child role for ${proto.label} ${id}`); const duplicate = db.roles.findOne({ 'assignments.ids': [id], 'assignments.type': proto.label, "parentRoleId": parentRoleId }); if (duplicate) { console.log('dublicate') continue; } const pack = { "systemApplicationId": 1, "name": parentRole.name, "description": parentRole.description, "systemFunctionIds": [], "scope": { type: proto.label }, "createdAt": new Date(), "updatedAt": new Date(), "assignments": {"ids": [id], "type": proto.label }, parentRoleId, defaultEntityTypeId: 12, }; const { count } = db.identitycounters.findOneAndUpdate({ model: 'roles' }, { $inc: { count: 1 } }, { returnNewDocument: true }); pack.roleId = count; db.roles.insert(pack); } } ``` ### System Events | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | ``` db.events.updateMany({ systemEventId: { $in: [2, 5] } }, { $set: { systemEventTypeIds: [0] } }) ``` ## Mongo trigger updates | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | - workspaceSystemUsers_2_3 (branch 1.16.11) | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | - session (branch 1.16.11) ## DB ### Event Triggers | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | ``` { "_id" : ObjectId("5f7454d7c2f15623c4f40728"), "eventIds" : [ -113, -115, -116, -117, -119, -120, -121, -142, -143, -144, -168, -169, -170, -171, -172, -173, -141 ], "filters" : [ ], "customFilter" : "", "actions" : [ { "id" : 1, "arguments" : [ { "method" : "PUT", "url" : "{permissionUrl}/permissions/cache/system-users", "headers" : { "Authorization" : "0.lo22gendj5i" }, "body" : { "systemUserId" : "{rawMessage.systemUserId}", "systemUserIds" : "{rawMessage.systemUserIds}" } } ] } ], "eventTriggerId" : 7 } ``` ``` ``` ### System Functions | ENV | Status | | --- | ------ | | TEST| Done | | UAT | Done | | PROD| Done | ``` db.systemFunctions.insert({ "systemFunctionId" : 621, "parentId" : 107, "function" : "/system-events", "systemApplicationId" : 0, "description" : "Get system events", "permissionLevel" : "public", "availablePermissionTypeIds" : [ 1 ], "name" : "/system-events" }) ```