Fixes on DAU and MAU aggregations (#14418)

* Fixes on SAU and MAU aggregations

* Report new data from DAU/MAU

* Run tests agains a mongodb container in CI

* Try to run CI correctly

* Fix drop database

* Parse desktop app User Agent correctly

* Fix aggregation of past sessions

* Return past month today

* Fix bug

* Add migration

* Fixed migration

* Migration improvements
pull/14442/head
Rodrigo Nascimento 6 years ago committed by GitHub
parent 7ef639d18b
commit 2f8e15f238
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .circleci/config.yml
  2. 521
      app/models/server/models/Sessions.js
  3. 7
      app/models/server/models/Sessions.mocks.js
  4. 821
      app/models/server/models/Sessions.tests.js
  5. 2
      app/statistics/server/functions/get.js
  6. 47
      app/statistics/server/lib/SAUMonitor.js
  7. 41
      app/statistics/server/lib/UAParserCustom.js
  8. 77
      app/statistics/server/lib/UAParserCustom.tests.js
  9. 1
      package.json
  10. 1
      server/startup/migrations/index.js
  11. 24
      server/startup/migrations/v144.js

@ -69,6 +69,7 @@ jobs:
<<: *defaults
docker:
- image: circleci/node:8.11-stretch
- image: mongo:3.2
steps:
- checkout
@ -129,7 +130,7 @@ jobs:
- run:
name: Unit Test
command: |
meteor npm run testunit
MONGO_URL=mongodb://localhost:27017 meteor npm run testunit
- restore_cache:
keys:

@ -1,5 +1,374 @@
import { Base } from './_Base';
export const aggregates = {
dailySessionsOfYesterday(collection, { year, month, day }) {
return collection.aggregate([{
$match: {
userId: { $exists: true },
lastActivityAt: { $exists: true },
device: { $exists: true },
type: 'session',
$or: [{
year: { $lt: year },
}, {
year,
month: { $lt: month },
}, {
year,
month,
day: { $lte: day },
}],
},
}, {
$sort: {
_id: 1,
},
}, {
$project: {
userId: 1,
device: 1,
day: 1,
month: 1,
year: 1,
time: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } },
},
}, {
$match: {
time: { $gt: 0 },
},
}, {
$group: {
_id: {
userId: '$userId',
device: '$device',
day: '$day',
month: '$month',
year: '$year',
},
time: { $sum: '$time' },
sessions: { $sum: 1 },
},
}, {
$group: {
_id: {
userId: '$_id.userId',
day: '$_id.day',
month: '$_id.month',
year: '$_id.year',
},
time: { $sum: '$time' },
sessions: { $sum: '$sessions' },
devices: {
$push: {
sessions: '$sessions',
time: '$time',
device: '$_id.device',
},
},
},
}, {
$project: {
_id: 0,
type: { $literal: 'user_daily' },
_computedAt: { $literal: new Date() },
day: '$_id.day',
month: '$_id.month',
year: '$_id.year',
userId: '$_id.userId',
time: 1,
sessions: 1,
devices: 1,
},
}], { allowDiskUse: true });
},
getUniqueUsersOfYesterday(collection, { year, month, day }) {
return collection.aggregate([{
$match: {
year,
month,
day,
type: 'user_daily',
},
}, {
$group: {
_id: {
day: '$day',
month: '$month',
year: '$year',
},
count: {
$sum: 1,
},
sessions: {
$sum: '$sessions',
},
time: {
$sum: '$time',
},
},
}, {
$project: {
_id: 0,
count: 1,
sessions: 1,
time: 1,
},
}]).toArray();
},
getUniqueUsersOfLastMonth(collection, { year, month, day }) {
return collection.aggregate([{
$match: {
type: 'user_daily',
...aggregates.getMatchOfLastMonthToday({ year, month, day }),
},
}, {
$group: {
_id: {
userId: '$userId',
},
sessions: {
$sum: '$sessions',
},
time: {
$sum: '$time',
},
},
}, {
$group: {
_id: 1,
count: {
$sum: 1,
},
sessions: {
$sum: '$sessions',
},
time: {
$sum: '$time',
},
},
}, {
$project: {
_id: 0,
count: 1,
sessions: 1,
time: 1,
},
}], { allowDiskUse: true }).toArray();
},
getMatchOfLastMonthToday({ year, month, day }) {
const pastMonthLastDay = (new Date(year, month - 1, 0)).getDate();
const currMonthLastDay = (new Date(year, month, 0)).getDate();
const lastMonthToday = new Date(year, month - 1, day);
lastMonthToday.setMonth(lastMonthToday.getMonth() - 1, (currMonthLastDay === day ? pastMonthLastDay : Math.min(pastMonthLastDay, day)) + 1);
const lastMonthTodayObject = {
year: lastMonthToday.getFullYear(),
month: lastMonthToday.getMonth() + 1,
day: lastMonthToday.getDate(),
};
if (year === lastMonthTodayObject.year && month === lastMonthTodayObject.month) {
return {
year,
month,
day: { $gte: lastMonthTodayObject.day, $lte: day },
};
}
if (year === lastMonthTodayObject.year) {
return {
year,
$and: [{
$or: [{
month: { $gt: lastMonthTodayObject.month },
}, {
month: lastMonthTodayObject.month,
day: { $gte: lastMonthTodayObject.day },
}],
}, {
$or: [{
month: { $lt: month },
}, {
month,
day: { $lte: day },
}],
}],
};
}
return {
$and: [{
$or: [{
year: { $gt: lastMonthTodayObject.year },
}, {
year: lastMonthTodayObject.year,
month: { $gt: lastMonthTodayObject.month },
}, {
year: lastMonthTodayObject.year,
month: lastMonthTodayObject.month,
day: { $gte: lastMonthTodayObject.day },
}],
}, {
$or: [{
year: { $lt: year },
}, {
year,
month: { $lt: month },
}, {
year,
month,
day: { $lte: day },
}],
}],
};
},
getUniqueDevicesOfLastMonth(collection, { year, month, day }) {
return collection.aggregate([{
$match: {
type: 'user_daily',
...aggregates.getMatchOfLastMonthToday({ year, month, day }),
},
}, {
$unwind: '$devices',
}, {
$group: {
_id: {
type : '$devices.device.type',
name : '$devices.device.name',
version : '$devices.device.version',
},
count: {
$sum: '$devices.sessions',
},
time: {
$sum: '$devices.time',
},
},
}, {
$project: {
_id: 0,
type: '$_id.type',
name: '$_id.name',
version: '$_id.version',
count: 1,
time: 1,
},
}], { allowDiskUse: true }).toArray();
},
getUniqueDevicesOfYesterday(collection, { year, month, day }) {
return collection.aggregate([{
$match: {
year,
month,
day,
type: 'user_daily',
},
}, {
$unwind: '$devices',
}, {
$group: {
_id: {
type : '$devices.device.type',
name : '$devices.device.name',
version : '$devices.device.version',
},
count: {
$sum: '$devices.sessions',
},
time: {
$sum: '$devices.time',
},
},
}, {
$project: {
_id: 0,
type: '$_id.type',
name: '$_id.name',
version: '$_id.version',
count: 1,
time: 1,
},
}]).toArray();
},
getUniqueOSOfLastMonth(collection, { year, month, day }) {
return collection.aggregate([{
$match: {
type: 'user_daily',
'devices.device.os.name': {
$exists: true,
},
...aggregates.getMatchOfLastMonthToday({ year, month, day }),
},
}, {
$unwind: '$devices',
}, {
$group: {
_id: {
name : '$devices.device.os.name',
version : '$devices.device.os.version',
},
count: {
$sum: '$devices.sessions',
},
time: {
$sum: '$devices.time',
},
},
}, {
$project: {
_id: 0,
name: '$_id.name',
version: '$_id.version',
count: 1,
time: 1,
},
}], { allowDiskUse: true }).toArray();
},
getUniqueOSOfYesterday(collection, { year, month, day }) {
return collection.aggregate([{
$match: {
year,
month,
day,
type: 'user_daily',
'devices.device.os.name': {
$exists: true,
},
},
}, {
$unwind: '$devices',
}, {
$group: {
_id: {
name : '$devices.device.os.name',
version : '$devices.device.os.version',
},
count: {
$sum: '$devices.sessions',
},
time: {
$sum: '$devices.time',
},
},
}, {
$project: {
_id: 0,
name: '$_id.name',
version: '$_id.version',
count: 1,
time: 1,
},
}]).toArray();
},
};
export class Sessions extends Base {
constructor(...args) {
super(...args);
@ -8,6 +377,7 @@ export class Sessions extends Base {
this.tryEnsureIndex({ instanceId: 1, sessionId: 1, userId: 1 });
this.tryEnsureIndex({ instanceId: 1, sessionId: 1 });
this.tryEnsureIndex({ year: 1, month: 1, day: 1, type: 1 });
this.tryEnsureIndex({ type: 1 });
this.tryEnsureIndex({ _computedAt: 1 }, { expireAfterSeconds: 60 * 60 * 24 * 45 });
}
@ -23,34 +393,7 @@ export class Sessions extends Base {
year,
month,
day,
data: Promise.await(this.model.rawCollection().aggregate([{
$match: {
year,
month,
day,
type: 'user_daily',
},
}, {
$group: {
_id: {
day: '$day',
month: '$month',
year: '$year',
},
count: {
$sum: '$count',
},
time: {
$sum: '$time',
},
},
}, {
$project: {
_id: 0,
count: 1,
time: 1,
},
}]).toArray()),
data: Promise.await(aggregates.getUniqueUsersOfYesterday(this.model.rawCollection(), { year, month, day })),
};
}
@ -60,36 +403,13 @@ export class Sessions extends Base {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return {
year,
month,
data: Promise.await(this.model.rawCollection().aggregate([{
$match: {
year,
month,
type: 'user_daily',
},
}, {
$group: {
_id: {
month: '$month',
year: '$year',
},
count: {
$sum: '$count',
},
time: {
$sum: '$time',
},
},
}, {
$project: {
_id: 0,
count: 1,
time: 1,
},
}]).toArray()),
day,
data: Promise.await(aggregates.getUniqueUsersOfLastMonth(this.model.rawCollection(), { year, month, day })),
};
}
@ -105,35 +425,23 @@ export class Sessions extends Base {
year,
month,
day,
data: Promise.await(this.model.rawCollection().aggregate([{
$match: {
year,
month,
day,
type: 'user_daily',
},
}, {
$unwind: '$devices',
}, {
$group: {
_id: {
type : '$devices.type',
name : '$devices.name',
version : '$devices.version',
},
count: {
$sum: '$count',
},
},
}, {
$project: {
_id: 0,
type: '$_id.type',
name: '$_id.name',
version: '$_id.version',
count: 1,
},
}]).toArray()),
data: Promise.await(aggregates.getUniqueDevicesOfYesterday(this.model.rawCollection(), { year, month, day })),
};
}
getUniqueDevicesOfLastMonth() {
const date = new Date();
date.setDate(date.getDate() - 1);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return {
year,
month,
day,
data: Promise.await(aggregates.getUniqueDevicesOfLastMonth(this.model.rawCollection(), { year, month, day })),
};
}
@ -149,36 +457,23 @@ export class Sessions extends Base {
year,
month,
day,
data: Promise.await(this.model.rawCollection().aggregate([{
$match: {
year,
month,
day,
type: 'user_daily',
'devices.os.name': {
$exists: true,
},
},
}, {
$unwind: '$devices',
}, {
$group: {
_id: {
name : '$devices.os.name',
version : '$devices.os.version',
},
count: {
$sum: '$count',
},
},
}, {
$project: {
_id: 0,
name: '$_id.name',
version: '$_id.version',
count: 1,
},
}]).toArray()),
data: Promise.await(aggregates.getUniqueOSOfYesterday(this.model.rawCollection(), { year, month, day })),
};
}
getUniqueOSOfLastMonth() {
const date = new Date();
date.setDate(date.getDate() - 1);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return {
year,
month,
day,
data: Promise.await(aggregates.getUniqueOSOfLastMonth(this.model.rawCollection(), { year, month, day })),
};
}

@ -0,0 +1,7 @@
import mock from 'mock-require';
mock('./_Base', {
Base: class Base {
tryEnsureIndex() {}
},
});

@ -0,0 +1,821 @@
/* eslint-env mocha */
import assert from 'assert';
import './Sessions.mocks.js';
const mongoUnit = require('mongo-unit');
const { MongoClient } = require('mongodb');
const { aggregates } = require('./Sessions');
const sessions_dates = [];
const baseDate = new Date(2018, 6, 1);
for (let index = 0; index < 365; index++) {
sessions_dates.push({
_id: `${ baseDate.getFullYear() }-${ baseDate.getMonth() + 1 }-${ baseDate.getDate() }`,
year: baseDate.getFullYear(),
month: baseDate.getMonth() + 1,
day: baseDate.getDate(),
});
baseDate.setDate(baseDate.getDate() + 1);
}
const DATA = {
sessions: [{
_id : 'fNFyFcjszvoN6Grip2',
day : 30,
instanceId : 'HvbqxukP8E65LAGMY',
month : 4,
sessionId : 'kiA4xX33AyzPgpBNs2',
year : 2019,
_updatedAt : new Date('2019-04-30T16:33:24.311Z'),
createdAt : new Date('2019-04-30T00:11:34.047Z'),
device : {
type : 'browser',
name : 'Firefox',
longVersion : '66.0.3',
os : {
name : 'Linux',
version : '12',
},
version : '66.0.3',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-04-30T00:11:34.047Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse',
lastActivityAt : new Date('2019-04-30T00:16:20.349Z'),
closedAt : new Date('2019-04-30T00:16:20.349Z'),
}, {
_id : 'fNFyFcjszvoN6Grip',
day : 2,
instanceId : 'HvbqxukP8E65LAGMY',
month : 5,
sessionId : 'kiA4xX33AyzPgpBNs',
year : 2019,
_updatedAt : new Date('2019-05-06T16:33:24.311Z'),
createdAt : new Date('2019-05-03T00:11:34.047Z'),
device : {
type : 'browser',
name : 'Firefox',
longVersion : '66.0.3',
os : {
name : 'Linux',
version : '12',
},
version : '66.0.3',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-05-03T00:11:34.047Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse',
lastActivityAt : new Date('2019-05-03T00:16:20.349Z'),
closedAt : new Date('2019-05-03T00:16:20.349Z'),
}, {
_id : 'oZMkfR3gFB6kuKDK2',
day : 2,
instanceId : 'HvbqxukP8E65LAGMY',
month : 5,
sessionId : 'i8uJFekr9np4x88kS',
year : 2019,
_updatedAt : new Date('2019-05-06T16:33:24.311Z'),
createdAt : new Date('2019-05-03T00:16:21.847Z'),
device : {
type : 'browser',
name : 'Chrome',
longVersion : '73.0.3683.103',
os : {
name : 'Mac OS',
version : '10.14.1',
},
version : '73.0.3683',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-05-03T00:16:21.846Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse',
lastActivityAt : new Date('2019-05-03T00:17:21.081Z'),
closedAt : new Date('2019-05-03T00:17:21.081Z'),
}, {
_id : 'ABXKoXKTZpPpzLjKd',
day : 2,
instanceId : 'HvbqxukP8E65LAGMY',
month : 5,
sessionId : 'T8MB28cpx2ZjfEDXr',
year : 2019,
_updatedAt : new Date('2019-05-06T16:33:24.311Z'),
createdAt : new Date('2019-05-03T00:17:22.375Z'),
device : {
type : 'browser',
name : 'Chrome',
longVersion : '73.0.3683.103',
os : {
name : 'Mac OS',
version : '10.14.1',
},
version : '73.0.3683',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-05-03T00:17:22.375Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse',
lastActivityAt : new Date('2019-05-03T01:48:31.695Z'),
closedAt : new Date('2019-05-03T01:48:31.695Z'),
}, {
_id : 's4ucvvcfBjnTEtYEb',
day : 2,
instanceId : 'HvbqxukP8E65LAGMY',
month : 5,
sessionId : '8mHbJJypgeRG27TYF',
year : 2019,
_updatedAt : new Date('2019-05-06T16:33:24.311Z'),
createdAt : new Date('2019-05-03T01:48:43.521Z'),
device : {
type : 'browser',
name : 'Chrome',
longVersion : '73.0.3683.103',
os : {
name : 'Mac OS',
version : '10.14.1',
},
version : '73.0.3683',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-05-03T01:48:43.521Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse',
closedAt : new Date('2019-05-03T01:48:43.761Z'),
lastActivityAt : new Date('2019-05-03T01:48:43.761Z'),
}, {
_id : 'MDs9SzQKmwaDmXL8s',
day : 2,
instanceId : 'HvbqxukP8E65LAGMY',
month : 5,
sessionId : 'GmoBDPKy9RW2eXdCG',
year : 2019,
_updatedAt : new Date('2019-05-06T16:33:24.311Z'),
createdAt : new Date('2019-05-03T01:48:45.064Z'),
device : {
type : 'browser',
name : 'Chrome',
longVersion : '73.0.3683.103',
os : {
name : 'Mac OS',
version : '10.14.1',
},
version : '73.0.3683',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-05-03T01:48:45.064Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse',
}, {
_id : 'CJwfxASo62FHDgqog',
day : 2,
instanceId : 'Nmwo2ttFeWZSrowNh',
month : 5,
sessionId : 'LMrrL4sbpNMLWYomA',
year : 2019,
_updatedAt : new Date('2019-05-06T16:33:24.311Z'),
createdAt : new Date('2019-05-03T01:50:31.098Z'),
device : {
type : 'browser',
name : 'Chrome',
longVersion : '73.0.3683.103',
os : {
name : 'Mac OS',
version : '10.14.1',
},
version : '73.0.3683',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-05-03T01:50:31.092Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse',
closedAt : new Date('2019-05-03T01:50:31.355Z'),
lastActivityAt : new Date('2019-05-03T01:50:31.355Z'),
}, {
_id : 'iGAcPobWfTQtN6s4K',
day : 1,
instanceId : 'Nmwo2ttFeWZSrowNh',
month : 5,
sessionId : 'AsbjZRLNQMqfbyYFS',
year : 2019,
_updatedAt : new Date('2019-05-06T16:33:24.311Z'),
createdAt : new Date('2019-05-03T01:50:32.765Z'),
device : {
type : 'browser',
name : 'Chrome',
longVersion : '73.0.3683.103',
os : {
name : 'Mac OS',
version : '10.14.1',
},
version : '73.0.3683',
},
host : 'localhost:3000',
ip : '127.0.0.1',
loginAt : new Date('2019-05-03T01:50:32.765Z'),
type : 'session',
userId : 'xPZXw9xqM3kKshsse2',
lastActivityAt : new Date('2019-05-03T02:59:59.999Z'),
}],
sessions_dates,
}; // require('./fixtures/testData.json')
describe.only('Sessions Aggregates', () => {
let db;
if (!process.env.MONGO_URL) {
before(function() {
this.timeout(120000);
return mongoUnit.start({ version: '3.2.22' })
.then((testMongoUrl) => process.env.MONGO_URL = testMongoUrl);
});
after(() => { mongoUnit.stop(); });
}
before(function() {
return MongoClient.connect(process.env.MONGO_URL)
.then((client) => db = client.db('test'));
});
before(() => db.dropDatabase().then(() => {
const sessions = db.collection('sessions');
const sessions_dates = db.collection('sessions_dates');
return Promise.all([
sessions.insertMany(DATA.sessions),
sessions_dates.insertMany(DATA.sessions_dates),
]);
}));
after(() => { db.close(); });
it('should have sessions_dates data saved', () => {
const collection = db.collection('sessions_dates');
return collection.find().toArray()
.then((docs) => assert.equal(docs.length, DATA.sessions_dates.length));
});
it('should match sessions between 2018-12-11 and 2019-1-10', () => {
const collection = db.collection('sessions_dates');
const $match = aggregates.getMatchOfLastMonthToday({ year: 2019, month: 1, day: 10 });
assert.deepEqual($match, {
$and: [{
$or: [
{ year: { $gt: 2018 } },
{ year: 2018, month: { $gt: 12 } },
{ year: 2018, month: 12, day: { $gte: 11 } },
],
}, {
$or: [
{ year: { $lt: 2019 } },
{ year: 2019, month: { $lt: 1 } },
{ year: 2019, month: 1, day: { $lte: 10 } },
],
}],
});
return collection.aggregate([{
$match,
}]).toArray()
.then((docs) => {
assert.equal(docs.length, 31);
assert.deepEqual(docs, [
{ _id: '2018-12-11', year: 2018, month: 12, day: 11 },
{ _id: '2018-12-12', year: 2018, month: 12, day: 12 },
{ _id: '2018-12-13', year: 2018, month: 12, day: 13 },
{ _id: '2018-12-14', year: 2018, month: 12, day: 14 },
{ _id: '2018-12-15', year: 2018, month: 12, day: 15 },
{ _id: '2018-12-16', year: 2018, month: 12, day: 16 },
{ _id: '2018-12-17', year: 2018, month: 12, day: 17 },
{ _id: '2018-12-18', year: 2018, month: 12, day: 18 },
{ _id: '2018-12-19', year: 2018, month: 12, day: 19 },
{ _id: '2018-12-20', year: 2018, month: 12, day: 20 },
{ _id: '2018-12-21', year: 2018, month: 12, day: 21 },
{ _id: '2018-12-22', year: 2018, month: 12, day: 22 },
{ _id: '2018-12-23', year: 2018, month: 12, day: 23 },
{ _id: '2018-12-24', year: 2018, month: 12, day: 24 },
{ _id: '2018-12-25', year: 2018, month: 12, day: 25 },
{ _id: '2018-12-26', year: 2018, month: 12, day: 26 },
{ _id: '2018-12-27', year: 2018, month: 12, day: 27 },
{ _id: '2018-12-28', year: 2018, month: 12, day: 28 },
{ _id: '2018-12-29', year: 2018, month: 12, day: 29 },
{ _id: '2018-12-30', year: 2018, month: 12, day: 30 },
{ _id: '2018-12-31', year: 2018, month: 12, day: 31 },
{ _id: '2019-1-1', year: 2019, month: 1, day: 1 },
{ _id: '2019-1-2', year: 2019, month: 1, day: 2 },
{ _id: '2019-1-3', year: 2019, month: 1, day: 3 },
{ _id: '2019-1-4', year: 2019, month: 1, day: 4 },
{ _id: '2019-1-5', year: 2019, month: 1, day: 5 },
{ _id: '2019-1-6', year: 2019, month: 1, day: 6 },
{ _id: '2019-1-7', year: 2019, month: 1, day: 7 },
{ _id: '2019-1-8', year: 2019, month: 1, day: 8 },
{ _id: '2019-1-9', year: 2019, month: 1, day: 9 },
{ _id: '2019-1-10', year: 2019, month: 1, day: 10 },
]);
});
});
it('should match sessions between 2019-1-11 and 2019-2-10', () => {
const collection = db.collection('sessions_dates');
const $match = aggregates.getMatchOfLastMonthToday({ year: 2019, month: 2, day: 10 });
assert.deepEqual($match, {
year: 2019,
$and: [{
$or: [
{ month: { $gt: 1 } },
{ month: 1, day: { $gte: 11 } },
],
}, {
$or: [
{ month: { $lt: 2 } },
{ month: 2, day: { $lte: 10 } },
],
}],
});
return collection.aggregate([{
$match,
}]).toArray()
.then((docs) => {
assert.equal(docs.length, 31);
assert.deepEqual(docs, [
{ _id: '2019-1-11', year: 2019, month: 1, day: 11 },
{ _id: '2019-1-12', year: 2019, month: 1, day: 12 },
{ _id: '2019-1-13', year: 2019, month: 1, day: 13 },
{ _id: '2019-1-14', year: 2019, month: 1, day: 14 },
{ _id: '2019-1-15', year: 2019, month: 1, day: 15 },
{ _id: '2019-1-16', year: 2019, month: 1, day: 16 },
{ _id: '2019-1-17', year: 2019, month: 1, day: 17 },
{ _id: '2019-1-18', year: 2019, month: 1, day: 18 },
{ _id: '2019-1-19', year: 2019, month: 1, day: 19 },
{ _id: '2019-1-20', year: 2019, month: 1, day: 20 },
{ _id: '2019-1-21', year: 2019, month: 1, day: 21 },
{ _id: '2019-1-22', year: 2019, month: 1, day: 22 },
{ _id: '2019-1-23', year: 2019, month: 1, day: 23 },
{ _id: '2019-1-24', year: 2019, month: 1, day: 24 },
{ _id: '2019-1-25', year: 2019, month: 1, day: 25 },
{ _id: '2019-1-26', year: 2019, month: 1, day: 26 },
{ _id: '2019-1-27', year: 2019, month: 1, day: 27 },
{ _id: '2019-1-28', year: 2019, month: 1, day: 28 },
{ _id: '2019-1-29', year: 2019, month: 1, day: 29 },
{ _id: '2019-1-30', year: 2019, month: 1, day: 30 },
{ _id: '2019-1-31', year: 2019, month: 1, day: 31 },
{ _id: '2019-2-1', year: 2019, month: 2, day: 1 },
{ _id: '2019-2-2', year: 2019, month: 2, day: 2 },
{ _id: '2019-2-3', year: 2019, month: 2, day: 3 },
{ _id: '2019-2-4', year: 2019, month: 2, day: 4 },
{ _id: '2019-2-5', year: 2019, month: 2, day: 5 },
{ _id: '2019-2-6', year: 2019, month: 2, day: 6 },
{ _id: '2019-2-7', year: 2019, month: 2, day: 7 },
{ _id: '2019-2-8', year: 2019, month: 2, day: 8 },
{ _id: '2019-2-9', year: 2019, month: 2, day: 9 },
{ _id: '2019-2-10', year: 2019, month: 2, day: 10 },
]);
});
});
it('should match sessions between 2019-5-1 and 2019-5-31', () => {
const collection = db.collection('sessions_dates');
const $match = aggregates.getMatchOfLastMonthToday({ year: 2019, month: 5, day: 31 });
assert.deepEqual($match, {
year: 2019,
month: 5,
day: { $gte: 1, $lte: 31 },
});
return collection.aggregate([{
$match,
}]).toArray()
.then((docs) => {
assert.equal(docs.length, 31);
assert.deepEqual(docs, [
{ _id: '2019-5-1', year: 2019, month: 5, day: 1 },
{ _id: '2019-5-2', year: 2019, month: 5, day: 2 },
{ _id: '2019-5-3', year: 2019, month: 5, day: 3 },
{ _id: '2019-5-4', year: 2019, month: 5, day: 4 },
{ _id: '2019-5-5', year: 2019, month: 5, day: 5 },
{ _id: '2019-5-6', year: 2019, month: 5, day: 6 },
{ _id: '2019-5-7', year: 2019, month: 5, day: 7 },
{ _id: '2019-5-8', year: 2019, month: 5, day: 8 },
{ _id: '2019-5-9', year: 2019, month: 5, day: 9 },
{ _id: '2019-5-10', year: 2019, month: 5, day: 10 },
{ _id: '2019-5-11', year: 2019, month: 5, day: 11 },
{ _id: '2019-5-12', year: 2019, month: 5, day: 12 },
{ _id: '2019-5-13', year: 2019, month: 5, day: 13 },
{ _id: '2019-5-14', year: 2019, month: 5, day: 14 },
{ _id: '2019-5-15', year: 2019, month: 5, day: 15 },
{ _id: '2019-5-16', year: 2019, month: 5, day: 16 },
{ _id: '2019-5-17', year: 2019, month: 5, day: 17 },
{ _id: '2019-5-18', year: 2019, month: 5, day: 18 },
{ _id: '2019-5-19', year: 2019, month: 5, day: 19 },
{ _id: '2019-5-20', year: 2019, month: 5, day: 20 },
{ _id: '2019-5-21', year: 2019, month: 5, day: 21 },
{ _id: '2019-5-22', year: 2019, month: 5, day: 22 },
{ _id: '2019-5-23', year: 2019, month: 5, day: 23 },
{ _id: '2019-5-24', year: 2019, month: 5, day: 24 },
{ _id: '2019-5-25', year: 2019, month: 5, day: 25 },
{ _id: '2019-5-26', year: 2019, month: 5, day: 26 },
{ _id: '2019-5-27', year: 2019, month: 5, day: 27 },
{ _id: '2019-5-28', year: 2019, month: 5, day: 28 },
{ _id: '2019-5-29', year: 2019, month: 5, day: 29 },
{ _id: '2019-5-30', year: 2019, month: 5, day: 30 },
{ _id: '2019-5-31', year: 2019, month: 5, day: 31 },
]);
});
});
it('should match sessions between 2019-4-1 and 2019-4-30', () => {
const collection = db.collection('sessions_dates');
const $match = aggregates.getMatchOfLastMonthToday({ year: 2019, month: 4, day: 30 });
assert.deepEqual($match, {
year: 2019,
month: 4,
day: { $gte: 1, $lte: 30 },
});
return collection.aggregate([{
$match,
}]).toArray()
.then((docs) => {
assert.equal(docs.length, 30);
assert.deepEqual(docs, [
{ _id: '2019-4-1', year: 2019, month: 4, day: 1 },
{ _id: '2019-4-2', year: 2019, month: 4, day: 2 },
{ _id: '2019-4-3', year: 2019, month: 4, day: 3 },
{ _id: '2019-4-4', year: 2019, month: 4, day: 4 },
{ _id: '2019-4-5', year: 2019, month: 4, day: 5 },
{ _id: '2019-4-6', year: 2019, month: 4, day: 6 },
{ _id: '2019-4-7', year: 2019, month: 4, day: 7 },
{ _id: '2019-4-8', year: 2019, month: 4, day: 8 },
{ _id: '2019-4-9', year: 2019, month: 4, day: 9 },
{ _id: '2019-4-10', year: 2019, month: 4, day: 10 },
{ _id: '2019-4-11', year: 2019, month: 4, day: 11 },
{ _id: '2019-4-12', year: 2019, month: 4, day: 12 },
{ _id: '2019-4-13', year: 2019, month: 4, day: 13 },
{ _id: '2019-4-14', year: 2019, month: 4, day: 14 },
{ _id: '2019-4-15', year: 2019, month: 4, day: 15 },
{ _id: '2019-4-16', year: 2019, month: 4, day: 16 },
{ _id: '2019-4-17', year: 2019, month: 4, day: 17 },
{ _id: '2019-4-18', year: 2019, month: 4, day: 18 },
{ _id: '2019-4-19', year: 2019, month: 4, day: 19 },
{ _id: '2019-4-20', year: 2019, month: 4, day: 20 },
{ _id: '2019-4-21', year: 2019, month: 4, day: 21 },
{ _id: '2019-4-22', year: 2019, month: 4, day: 22 },
{ _id: '2019-4-23', year: 2019, month: 4, day: 23 },
{ _id: '2019-4-24', year: 2019, month: 4, day: 24 },
{ _id: '2019-4-25', year: 2019, month: 4, day: 25 },
{ _id: '2019-4-26', year: 2019, month: 4, day: 26 },
{ _id: '2019-4-27', year: 2019, month: 4, day: 27 },
{ _id: '2019-4-28', year: 2019, month: 4, day: 28 },
{ _id: '2019-4-29', year: 2019, month: 4, day: 29 },
{ _id: '2019-4-30', year: 2019, month: 4, day: 30 },
]);
});
});
it('should match sessions between 2019-2-1 and 2019-2-28', () => {
const collection = db.collection('sessions_dates');
const $match = aggregates.getMatchOfLastMonthToday({ year: 2019, month: 2, day: 28 });
assert.deepEqual($match, {
year: 2019,
month: 2,
day: { $gte: 1, $lte: 28 },
});
return collection.aggregate([{
$match,
}]).toArray()
.then((docs) => {
assert.equal(docs.length, 28);
assert.deepEqual(docs, [
{ _id: '2019-2-1', year: 2019, month: 2, day: 1 },
{ _id: '2019-2-2', year: 2019, month: 2, day: 2 },
{ _id: '2019-2-3', year: 2019, month: 2, day: 3 },
{ _id: '2019-2-4', year: 2019, month: 2, day: 4 },
{ _id: '2019-2-5', year: 2019, month: 2, day: 5 },
{ _id: '2019-2-6', year: 2019, month: 2, day: 6 },
{ _id: '2019-2-7', year: 2019, month: 2, day: 7 },
{ _id: '2019-2-8', year: 2019, month: 2, day: 8 },
{ _id: '2019-2-9', year: 2019, month: 2, day: 9 },
{ _id: '2019-2-10', year: 2019, month: 2, day: 10 },
{ _id: '2019-2-11', year: 2019, month: 2, day: 11 },
{ _id: '2019-2-12', year: 2019, month: 2, day: 12 },
{ _id: '2019-2-13', year: 2019, month: 2, day: 13 },
{ _id: '2019-2-14', year: 2019, month: 2, day: 14 },
{ _id: '2019-2-15', year: 2019, month: 2, day: 15 },
{ _id: '2019-2-16', year: 2019, month: 2, day: 16 },
{ _id: '2019-2-17', year: 2019, month: 2, day: 17 },
{ _id: '2019-2-18', year: 2019, month: 2, day: 18 },
{ _id: '2019-2-19', year: 2019, month: 2, day: 19 },
{ _id: '2019-2-20', year: 2019, month: 2, day: 20 },
{ _id: '2019-2-21', year: 2019, month: 2, day: 21 },
{ _id: '2019-2-22', year: 2019, month: 2, day: 22 },
{ _id: '2019-2-23', year: 2019, month: 2, day: 23 },
{ _id: '2019-2-24', year: 2019, month: 2, day: 24 },
{ _id: '2019-2-25', year: 2019, month: 2, day: 25 },
{ _id: '2019-2-26', year: 2019, month: 2, day: 26 },
{ _id: '2019-2-27', year: 2019, month: 2, day: 27 },
{ _id: '2019-2-28', year: 2019, month: 2, day: 28 },
]);
});
});
it('should match sessions between 2019-1-28 and 2019-2-27', () => {
const collection = db.collection('sessions_dates');
const $match = aggregates.getMatchOfLastMonthToday({ year: 2019, month: 2, day: 27 });
assert.deepEqual($match, {
year: 2019,
$and: [{
$or: [
{ month: { $gt: 1 } },
{ month: 1, day: { $gte: 28 } },
],
}, {
$or: [
{ month: { $lt: 2 } },
{ month: 2, day: { $lte: 27 } },
],
}],
});
return collection.aggregate([{
$match,
}]).toArray()
.then((docs) => {
assert.equal(docs.length, 31);
assert.deepEqual(docs, [
{ _id: '2019-1-28', year: 2019, month: 1, day: 28 },
{ _id: '2019-1-29', year: 2019, month: 1, day: 29 },
{ _id: '2019-1-30', year: 2019, month: 1, day: 30 },
{ _id: '2019-1-31', year: 2019, month: 1, day: 31 },
{ _id: '2019-2-1', year: 2019, month: 2, day: 1 },
{ _id: '2019-2-2', year: 2019, month: 2, day: 2 },
{ _id: '2019-2-3', year: 2019, month: 2, day: 3 },
{ _id: '2019-2-4', year: 2019, month: 2, day: 4 },
{ _id: '2019-2-5', year: 2019, month: 2, day: 5 },
{ _id: '2019-2-6', year: 2019, month: 2, day: 6 },
{ _id: '2019-2-7', year: 2019, month: 2, day: 7 },
{ _id: '2019-2-8', year: 2019, month: 2, day: 8 },
{ _id: '2019-2-9', year: 2019, month: 2, day: 9 },
{ _id: '2019-2-10', year: 2019, month: 2, day: 10 },
{ _id: '2019-2-11', year: 2019, month: 2, day: 11 },
{ _id: '2019-2-12', year: 2019, month: 2, day: 12 },
{ _id: '2019-2-13', year: 2019, month: 2, day: 13 },
{ _id: '2019-2-14', year: 2019, month: 2, day: 14 },
{ _id: '2019-2-15', year: 2019, month: 2, day: 15 },
{ _id: '2019-2-16', year: 2019, month: 2, day: 16 },
{ _id: '2019-2-17', year: 2019, month: 2, day: 17 },
{ _id: '2019-2-18', year: 2019, month: 2, day: 18 },
{ _id: '2019-2-19', year: 2019, month: 2, day: 19 },
{ _id: '2019-2-20', year: 2019, month: 2, day: 20 },
{ _id: '2019-2-21', year: 2019, month: 2, day: 21 },
{ _id: '2019-2-22', year: 2019, month: 2, day: 22 },
{ _id: '2019-2-23', year: 2019, month: 2, day: 23 },
{ _id: '2019-2-24', year: 2019, month: 2, day: 24 },
{ _id: '2019-2-25', year: 2019, month: 2, day: 25 },
{ _id: '2019-2-26', year: 2019, month: 2, day: 26 },
{ _id: '2019-2-27', year: 2019, month: 2, day: 27 },
]);
});
});
it('should have sessions data saved', () => {
const collection = db.collection('sessions');
return collection.find().toArray()
.then((docs) => assert.equal(docs.length, DATA.sessions.length));
});
it('should generate daily sessions', () => {
const collection = db.collection('sessions');
return aggregates.dailySessionsOfYesterday(collection, { year: 2019, month: 5, day: 2 }).toArray()
.then((docs) => {
docs.forEach((doc) => {
doc._id = `${ doc.userId }-${ doc.year }-${ doc.month }-${ doc.day }`;
});
assert.equal(docs.length, 3);
assert.deepEqual(docs, [{
_id: 'xPZXw9xqM3kKshsse-2019-5-2',
time: 5814,
sessions: 3,
devices: [{
sessions: 1,
time: 286,
device: {
type: 'browser',
name: 'Firefox',
longVersion: '66.0.3',
os: {
name: 'Linux',
version: '12',
},
version: '66.0.3',
},
}, {
sessions: 2,
time: 5528,
device: {
type: 'browser',
name: 'Chrome',
longVersion: '73.0.3683.103',
os: {
name: 'Mac OS',
version: '10.14.1',
},
version: '73.0.3683',
},
}],
type: 'user_daily',
_computedAt: docs[0]._computedAt,
day: 2,
month: 5,
year: 2019,
userId: 'xPZXw9xqM3kKshsse',
}, {
_id: 'xPZXw9xqM3kKshsse-2019-4-30',
day: 30,
devices: [{
device: {
longVersion: '66.0.3',
name: 'Firefox',
os: {
name: 'Linux',
version: '12',
},
type: 'browser',
version: '66.0.3',
},
sessions: 1,
time: 286,
}],
month: 4,
sessions: 1,
time: 286,
type: 'user_daily',
_computedAt: docs[1]._computedAt,
userId: 'xPZXw9xqM3kKshsse',
year: 2019,
}, {
_id: 'xPZXw9xqM3kKshsse2-2019-5-1',
time: 4167,
sessions: 1,
devices: [{
sessions: 1,
time: 4167,
device: {
type: 'browser',
name: 'Chrome',
longVersion: '73.0.3683.103',
os: {
name: 'Mac OS',
version: '10.14.1',
},
version: '73.0.3683',
},
}],
type: 'user_daily',
_computedAt: docs[2]._computedAt,
day: 1,
month: 5,
year: 2019,
userId: 'xPZXw9xqM3kKshsse2',
}]);
return collection.insertMany(docs);
});
});
it('should have 2 unique users for month 5 of 2019', () => {
const collection = db.collection('sessions');
return aggregates.getUniqueUsersOfLastMonth(collection, { year: 2019, month: 5, day: 31 })
.then((docs) => {
assert.equal(docs.length, 1);
assert.deepEqual(docs, [{
count: 2,
sessions: 4,
time: 9981,
}]);
});
});
it('should have 1 unique user for 1st of month 5 of 2019', () => {
const collection = db.collection('sessions');
return aggregates.getUniqueUsersOfYesterday(collection, { year: 2019, month: 5, day: 1 })
.then((docs) => {
assert.equal(docs.length, 1);
assert.deepEqual(docs, [{
count: 1,
sessions: 1,
time: 4167,
}]);
});
});
it('should have 1 unique user for 2nd of month 5 of 2019', () => {
const collection = db.collection('sessions');
return aggregates.getUniqueUsersOfYesterday(collection, { year: 2019, month: 5, day: 2 })
.then((docs) => {
assert.equal(docs.length, 1);
assert.deepEqual(docs, [{
count: 1,
sessions: 3,
time: 5814,
}]);
});
});
it('should have 2 unique devices for month 5 of 2019', () => {
const collection = db.collection('sessions');
return aggregates.getUniqueDevicesOfLastMonth(collection, { year: 2019, month: 5, day: 31 })
.then((docs) => {
assert.equal(docs.length, 2);
assert.deepEqual(docs, [{
count: 3,
time: 9695,
type: 'browser',
name: 'Chrome',
version: '73.0.3683',
}, {
count: 1,
time: 286,
type: 'browser',
name: 'Firefox',
version: '66.0.3',
}]);
});
});
it('should have 2 unique devices for 2nd of month 5 of 2019', () => {
const collection = db.collection('sessions');
return aggregates.getUniqueDevicesOfYesterday(collection, { year: 2019, month: 5, day: 2 })
.then((docs) => {
assert.equal(docs.length, 2);
assert.deepEqual(docs, [{
count: 2,
time: 5528,
type: 'browser',
name: 'Chrome',
version: '73.0.3683',
}, {
count: 1,
time: 286,
type: 'browser',
name: 'Firefox',
version: '66.0.3',
}]);
});
});
it('should have 2 unique OS for month 5 of 2019', () => {
const collection = db.collection('sessions');
return aggregates.getUniqueOSOfLastMonth(collection, { year: 2019, month: 5, day: 31 })
.then((docs) => {
assert.equal(docs.length, 2);
assert.deepEqual(docs, [{
count: 3,
time: 9695,
name: 'Mac OS',
version: '10.14.1',
}, {
count: 1,
time: 286,
name: 'Linux',
version: '12',
}]);
});
});
it('should have 2 unique OS for 2nd of month 5 of 2019', () => {
const collection = db.collection('sessions');
return aggregates.getUniqueOSOfYesterday(collection, { year: 2019, month: 5, day: 2 })
.then((docs) => {
assert.equal(docs.length, 2);
assert.deepEqual(docs, [{
count: 2,
time: 5528,
name: 'Mac OS',
version: '10.14.1',
}, {
count: 1,
time: 286,
name: 'Linux',
version: '12',
}]);
});
});
});

@ -143,7 +143,9 @@ statistics.get = function _getStatistics() {
statistics.uniqueUsersOfYesterday = Sessions.getUniqueUsersOfYesterday();
statistics.uniqueUsersOfLastMonth = Sessions.getUniqueUsersOfLastMonth();
statistics.uniqueDevicesOfYesterday = Sessions.getUniqueDevicesOfYesterday();
statistics.uniqueDevicesOfLastMonth = Sessions.getUniqueDevicesOfLastMonth();
statistics.uniqueOSOfYesterday = Sessions.getUniqueOSOfYesterday();
statistics.uniqueOSOfLastMonth = Sessions.getUniqueOSOfLastMonth();
return statistics;
};

@ -1,10 +1,11 @@
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import UAParser from 'ua-parser-js';
import { UAParserMobile } from './UAParserMobile';
import { Sessions } from '../../../models';
import { UAParserMobile, UAParserDesktop } from './UAParserCustom';
import { Sessions } from '../../../models/server';
import { Logger } from '../../../logger';
import { SyncedCron } from 'meteor/littledata:synced-cron';
import { aggregates } from '../../../models/server/models/Sessions';
const getDateObj = (dateTime = new Date()) => ({
day: dateTime.getDate(),
@ -199,6 +200,8 @@ export class SAUMonitorClass {
if (UAParserMobile.isMobileApp(uaString)) {
result = UAParserMobile.uaObject(uaString);
} else if (UAParserDesktop.isDesktopApp(uaString)) {
result = UAParserDesktop.uaObject(uaString);
} else {
const ua = new UAParser(uaString);
result = ua.getResult();
@ -224,7 +227,7 @@ export class SAUMonitorClass {
}
if (result.device && (result.device.type || result.device.model)) {
info.type = 'mobile-app';
info.type = result.device.type;
if (result.app && result.app.name) {
info.name = result.app.name;
@ -331,43 +334,7 @@ export class SAUMonitorClass {
day: { $lte: yesterday.day },
};
Sessions.model.rawCollection().aggregate([{
$match: {
userId: { $exists: true },
lastActivityAt: { $exists: true },
device: { $exists: true },
...match,
},
}, {
$group: {
_id: {
userId: '$userId',
day: '$day',
month: '$month',
year: '$year',
},
times: { $push: { $trunc: { $divide: [{ $subtract: ['$lastActivityAt', '$loginAt'] }, 1000] } } },
devices: { $addToSet: '$device' },
},
}, {
$project: {
_id: '$_id',
times: { $filter: { input: '$times', as: 'item', cond: { $gt: ['$$item', 0] } } },
devices: '$devices',
},
}, {
$project: {
type: 'user_daily',
_computedAt: new Date(),
day: '$_id.day',
month: '$_id.month',
year: '$_id.year',
userId: '$_id.userId',
time: { $sum: '$times' },
count: { $size: '$times' },
devices: '$devices',
},
}]).forEach(Meteor.bindEnvironment((record) => {
aggregates.dailySessionsOfYesterday(Sessions.model.rawCollection(), yesterday).forEach(Meteor.bindEnvironment((record) => {
record._id = `${ record.userId }-${ record.year }-${ record.month }-${ record.day }`;
Sessions.upsert({ _id: record._id }, record);
}));

@ -1,3 +1,5 @@
import UAParser from 'ua-parser-js';
const mergeDeep = ((target, source) => {
if (!(typeof target === 'object' && typeof source === 'object')) {
return target;
@ -20,9 +22,9 @@ const mergeDeep = ((target, source) => {
return target;
});
const UAParserMobile = {
export const UAParserMobile = {
appName: 'RC Mobile',
device: 'mobile',
device: 'mobile-app',
uaSeparator: ';',
props: {
os: {
@ -103,4 +105,37 @@ const UAParserMobile = {
},
};
export { UAParserMobile };
export const UAParserDesktop = {
device: 'desktop-app',
isDesktopApp(uaString) {
if (!uaString || typeof uaString !== 'string') {
return false;
}
return uaString.includes(' Electron/');
},
uaObject(uaString) {
if (!this.isDesktopApp(uaString)) {
return {};
}
const ua = new UAParser(uaString);
const uaParsed = ua.getResult();
const [, name, version] = uaString.match(/(Rocket\.Chat)\/(\d+(\.\d+)+)/) || [];
return {
device: {
type: this.device,
},
os: uaParsed.os,
app: {
name,
version,
},
};
},
};

@ -0,0 +1,77 @@
/* eslint-env mocha */
import { expect } from 'chai';
import { UAParserMobile, UAParserDesktop } from './UAParserCustom';
const UAMobile = 'RC Mobile; iOS 12.2; v3.4.0 (250)';
const UADesktop = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Rocket.Chat/2.15.2 Chrome/69.0.3497.128 Electron/4.1.4 Safari/537.36';
const UAChrome = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36';
describe('UAParserCustom', () => {
describe('UAParserMobile', () => {
it('should identify mobile UA', () => {
expect(UAParserMobile.isMobileApp(UAMobile)).to.be.true;
});
it('should not identify desktop UA', () => {
expect(UAParserMobile.isMobileApp(UADesktop)).to.be.false;
});
it('should not identify chrome UA', () => {
expect(UAParserMobile.isMobileApp(UAChrome)).to.be.false;
});
it('should parse mobile UA', () => {
expect(UAParserMobile.uaObject(UAMobile)).to.be.deep.equal({
device: {
type: 'mobile-app',
},
app: {
name: 'RC Mobile',
version: '3.4.0',
bundle: '250',
},
os: {
name: 'iOS',
version: '12.2',
},
});
});
});
describe('UAParserDesktop', () => {
it('should not identify mobile UA', () => {
expect(UAParserDesktop.isDesktopApp(UAMobile)).to.be.false;
});
it('should identify desktop UA', () => {
expect(UAParserDesktop.isDesktopApp(UADesktop)).to.be.true;
});
it('should not identify chrome UA', () => {
expect(UAParserDesktop.isDesktopApp(UAChrome)).to.be.false;
});
it('should parse desktop UA', () => {
expect(UAParserDesktop.uaObject(UADesktop)).to.be.deep.equal({
device: {
type: 'desktop-app',
},
app: {
name: 'Rocket.Chat',
version: '2.15.2',
},
os: {
name: 'Mac OS',
version: '10.14.1',
},
});
});
});
});

@ -104,6 +104,7 @@
"husky": "^1.2.0",
"mocha": "^5.2.0",
"mock-require": "^3.0.2",
"mongo-unit": "^1.4.4",
"postcss": "^7.0.6",
"postcss-custom-properties": "^8.0.9",
"postcss-easy-import": "^3.0.0",

@ -141,4 +141,5 @@ import './v140';
import './v141';
import './v142';
import './v143';
import './v144';
import './xrun';

@ -0,0 +1,24 @@
import { Migrations } from '../../../app/migrations/server';
import { Sessions } from '../../../app/models/server';
Migrations.add({
version: 144,
up() {
console.log('Restoring sessions data');
Promise.await(Sessions.model.rawCollection().removeMany({
type: 'user_daily',
}));
Promise.await(Sessions.model.rawCollection().updateMany({
type: 'computed-session',
}, {
$set: {
type: 'session',
},
$unset: {
_computedAt: 1,
},
}));
console.log('Restoring sessions data - Done');
},
});
Loading…
Cancel
Save