# 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"
})
```