Skip to content

Commit 587aaf1

Browse files
committed
Merge branch 'release.24.10' into release.24.12
# Conflicts: # CHANGELOG.md
2 parents 6409972 + 02b8e19 commit 587aaf1

File tree

6 files changed

+246
-135
lines changed

6 files changed

+246
-135
lines changed

CHANGELOG.md

+23
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,29 @@ Dependencies:
4545
- Bump sass from 1.81.0 to 1.83.3
4646
- Bump tslib from 2.7.0 to 2.8.1
4747

48+
## Version 24.10.6
49+
50+
Fixes:
51+
- [push] Using apns-id header as message result in debug mode
52+
- [server-stats] Fix data point calculation in job
53+
- [TopEventsJob] preserver previous state if overwriting fails
54+
- [ui] scroll top on step changes in drawers
55+
56+
Enterprise fixes:
57+
- [drill] Encoding url component before changing history state
58+
- [drill] Fixed drill meta regeneration
59+
- [drill] [license] Update license loader to enable supplying db client
60+
- [users] Format data points displayed in user sidebar
61+
- [cohorts] Unescape drill texts in cohort component
62+
63+
Dependencies:
64+
- Bump fs-extra from 11.2.0 to 11.3.0
65+
- Bump nodemailer from 6.9.16 to 6.10.0
66+
67+
Enterprise Dependencies:
68+
- Bump nanoid in /plugins/cognito from 2.1.11 to 3.3.8
69+
- Bump shortid in /plugins/cognito from 2.2.16 to 2.2.17
70+
4871
## Version 24.10.3
4972
Fixes:
5073
- [dashboards] Fixing issue where dashboard widgets go into single column

plugins/push/api/send/platforms/i.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ class APN extends Base {
708708
status = headers[':status'];
709709
// self.log.d('%d: status %d: %j', i, status, self.session.state);
710710
if (status === 200) {
711-
const apnsUniqueId = headers["apns-unique-id"];
711+
const apnsUniqueId = headers["apns-id"] ?? headers["apns-unique-id"];
712712
oks.push({ p: p._id, r: apnsUniqueId });
713713
stream.destroy();
714714
streamDone();

plugins/server-stats/api/jobs/stats.js

+165-130
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@
33
const job = require('../../../../api/parts/jobs/job.js'),
44
tracker = require('../../../../api/parts/mgmt/tracker.js'),
55
log = require('../../../../api/utils/log.js')('job:stats'),
6-
config = require("../../../../frontend/express/config.js"),
6+
config = require('../../../../frontend/express/config.js'),
77
pluginManager = require('../../../pluginManager.js'),
8+
serverStats = require('../parts/stats.js'),
89
moment = require('moment-timezone'),
9-
request = require('countly-request')(pluginManager.getConfig("security"));
10+
request = require('countly-request')(pluginManager.getConfig('security'));
11+
12+
let drill;
13+
try {
14+
drill = require('../../../drill/api/parts/data/drill.js');
15+
}
16+
catch (ex) {
17+
log.e(ex);
18+
drill = null;
19+
}
1020

1121
const promisedLoadConfigs = function(db) {
1222
return new Promise((resolve) => {
@@ -27,146 +37,171 @@ class StatsJob extends job.Job {
2737
super(name, data);
2838
}
2939

40+
/**
41+
* @param {Object} allData - All server stats data from the beginning of time
42+
* @returns {Object} Sum of all data, average data per month, and last three month data
43+
*/
44+
static generateDataSummary(allData) {
45+
const data = {};
46+
data.all = 0;
47+
data.month3 = [];
48+
const utcMoment = moment.utc();
49+
const months = {};
50+
for (let i = 0; i < 3; i++) {
51+
months[utcMoment.format('YYYY:M')] = true;
52+
utcMoment.subtract(1, 'months');
53+
}
54+
for (const [key, value] of Object.entries(allData)) {
55+
data.all += value;
56+
if (months[key]) {
57+
data.month3.push(key + ' - ' + (value));
58+
}
59+
}
60+
data.avg = Math.round((data.all / Object.keys(allData).length) * 100) / 100;
61+
62+
return data;
63+
}
64+
65+
/**
66+
* @param {Object} allData - All server stats data from the beginning of time
67+
* @returns {Object} Monthly data
68+
*/
69+
static generateDataMonthly(allData) {
70+
const data = {};
71+
const utcMoment = moment.utc();
72+
const ids = {};
73+
const ids6 = {};
74+
const ids0 = {};
75+
const order = [];
76+
77+
for (let i = 0; i < 12; i++) {
78+
order.push(utcMoment.format('MMM YYYY'));
79+
ids[utcMoment.format('YYYY:M')] = utcMoment.format('MMM YYYY');
80+
if (i < 7) {
81+
ids6[utcMoment.format('YYYY:M')] = utcMoment.format('MMM YYYY');
82+
}
83+
if (i === 0) {
84+
ids0[utcMoment.format('YYYY:M')] = utcMoment.format('MMM YYYY');
85+
}
86+
utcMoment.subtract(1, 'months');
87+
}
88+
89+
const DP = {};
90+
data.DP = [];
91+
let avg12monthDP = 0;
92+
let avg6monthDP = 0;
93+
94+
let avg12 = 0;
95+
let avg6 = 0;
96+
for (const [key, value] of Object.entries(allData)) {
97+
if (ids[key]) {
98+
DP[ids[key]] = value;
99+
100+
if (!ids0[key]) {
101+
avg12monthDP += DP[ids[key]];
102+
avg12++;
103+
}
104+
if (ids6[key] && !ids0[key]) {
105+
avg6monthDP += DP[ids[key]];
106+
avg6++;
107+
}
108+
}
109+
}
110+
111+
for (let i = 0; i < order.length; i++) {
112+
data.DP.push((i < 9 ? '0' + (i + 1) : i + 1) + '. ' + order[i] + ': ' + ((DP[order[i]] || 0).toLocaleString()));
113+
}
114+
if (avg12) {
115+
data['Last 12 months'] = Math.round(avg12monthDP / avg12).toLocaleString();
116+
}
117+
if (avg6) {
118+
data['Last 6 months'] = Math.round(avg6monthDP / avg6).toLocaleString();
119+
}
120+
121+
return data;
122+
}
123+
30124
/**
31125
* Returns a human readable name given application id.
32126
* @param {Object} db - Database object, used for querying
33127
* @param {function} done - Completion callback
34128
* @returns {undefined} Returns nothing, only callback
35129
**/
36130
run(db, done) {
37-
if (config.web.track !== "none") {
38-
db.collection("members").find({global_admin: true}).toArray(function(err, members) {
131+
if (config.web.track !== 'none') {
132+
db.collection('members').find({global_admin: true}).toArray(async(err, members) => {
39133
if (!err && members.length > 0) {
40-
db.collection("server_stats_data_points").aggregate([
41-
{
42-
$group: {
43-
_id: "$m",
44-
e: { $sum: "$e"},
45-
s: { $sum: "$s"}
46-
}
134+
let license = {};
135+
if (drill) {
136+
try {
137+
license = await drill.loadLicense(undefined, db);
47138
}
48-
], { allowDiskUse: true }, async function(error, allData) {
49-
if (!error) {
50-
var data = {};
51-
data.all = 0;
52-
data.month3 = [];
53-
var utcMoment = moment.utc();
54-
var months = {};
55-
for (let i = 0; i < 3; i++) {
56-
months[utcMoment.format("YYYY:M")] = true;
57-
utcMoment.subtract(1, 'months');
58-
}
59-
for (let i = 0; i < allData.length; i++) {
60-
data.all += allData[i].e + allData[i].s;
61-
if (months[allData[i]._id]) {
62-
data.month3.push(allData[i]._id + " - " + (allData[i].e + allData[i].s));
63-
}
64-
}
65-
data.avg = Math.round((data.all / allData.length) * 100) / 100;
66-
var date = new Date();
67-
var usersData = [];
68-
69-
await promisedLoadConfigs(db);
70-
71-
let domain = '';
72-
73-
try {
74-
// try to extract hostname from full domain url
75-
const urlObj = new URL(pluginManager.getConfig('api').domain);
76-
domain = urlObj.hostname;
77-
}
78-
catch (_) {
79-
// do nothing, domain from config will be used as is
80-
}
81-
82-
usersData.push({
83-
device_id: domain,
84-
timestamp: Math.floor(date.getTime() / 1000),
85-
hour: date.getHours(),
86-
dow: date.getDay(),
87-
user_details: JSON.stringify({
88-
custom: {
89-
dataPointsAll: data.all,
90-
dataPointsMonthlyAvg: data.avg,
91-
dataPointsLast3Months: data.month3
92-
}
93-
})
94-
});
139+
catch (error) {
140+
log.e(error);
141+
// do nothing, most likely there is no license
142+
}
143+
}
95144

96-
var formData = {
97-
app_key: "e70ec21cbe19e799472dfaee0adb9223516d238f",
98-
requests: JSON.stringify(usersData)
99-
};
100-
101-
request.post({
102-
url: 'https://stats.count.ly/i/bulk',
103-
body: formData
104-
}, function(a) {
105-
log.d('Done running stats job: %j', a);
106-
done();
107-
});
145+
const options = {
146+
monthlyBreakdown: true,
147+
license_hosting: license.license_hosting,
148+
};
149+
150+
serverStats.fetchDatapoints(db, {}, options, async(allData) => {
151+
const dataSummary = StatsJob.generateDataSummary(allData);
108152

109-
if (tracker.isEnabled()) {
110-
utcMoment = moment.utc();
111-
data = {};
112-
var ids = {};
113-
var ids6 = {};
114-
var ids0 = {};
115-
var order = [];
116-
var Countly = tracker.getSDK();
117-
for (let i = 0; i < 12; i++) {
118-
order.push(utcMoment.format("MMM YYYY"));
119-
ids[utcMoment.format("YYYY:M")] = utcMoment.format("MMM YYYY");
120-
if (i < 7) {
121-
ids6[utcMoment.format("YYYY:M")] = utcMoment.format("MMM YYYY");
122-
}
123-
if (i === 0) {
124-
ids0[utcMoment.format("YYYY:M")] = utcMoment.format("MMM YYYY");
125-
}
126-
utcMoment.subtract(1, 'months');
127-
}
128-
129-
var DP = {};
130-
data.DP = [];
131-
var avg12monthDP = 0;
132-
var avg6monthDP = 0;
133-
134-
var avg12 = 0;
135-
var avg6 = 0;
136-
for (let i = 0; i < allData.length; i++) {
137-
if (ids[allData[i]._id]) {
138-
var val = allData[i].e + allData[i].s;
139-
DP[ids[allData[i]._id]] = val;
140-
if (!ids0[allData[i]._id]) {
141-
avg12monthDP += DP[ids[allData[i]._id]];
142-
avg12++;
143-
}
144-
if (ids6[allData[i]._id] && !ids0[allData[i]._id]) {
145-
avg6monthDP += DP[ids[allData[i]._id]];
146-
avg6++;
147-
}
148-
}
149-
}
150-
151-
for (let i = 0; i < order.length; i++) {
152-
data.DP.push((i < 9 ? "0" + (i + 1) : i + 1) + ". " + order[i] + ": " + ((DP[order[i]] || 0).toLocaleString()));
153-
}
154-
155-
if (avg12) {
156-
data["Last 12 months"] = Math.round(avg12monthDP / avg12).toLocaleString();
157-
}
158-
if (avg6) {
159-
data["Last 6 months"] = Math.round(avg6monthDP / avg6).toLocaleString();
160-
}
161-
Countly.user_details({
162-
"custom": data
163-
});
164-
165-
Countly.userData.save();
166-
}
153+
let date = new Date();
154+
const usersData = [];
155+
156+
await promisedLoadConfigs(db);
157+
158+
let domain = '';
159+
160+
try {
161+
// try to extract hostname from full domain url
162+
const urlObj = new URL(pluginManager.getConfig('api').domain);
163+
domain = urlObj.hostname;
167164
}
168-
else {
165+
catch (_) {
166+
// do nothing, domain from config will be used as is
167+
}
168+
169+
usersData.push({
170+
device_id: domain,
171+
timestamp: Math.floor(date.getTime() / 1000),
172+
hour: date.getHours(),
173+
dow: date.getDay(),
174+
user_details: JSON.stringify({
175+
custom: {
176+
dataPointsAll: dataSummary.all,
177+
dataPointsMonthlyAvg: dataSummary.avg,
178+
dataPointsLast3Months: dataSummary.month3,
179+
},
180+
}),
181+
});
182+
183+
var formData = {
184+
app_key: 'e70ec21cbe19e799472dfaee0adb9223516d238f',
185+
requests: JSON.stringify(usersData)
186+
};
187+
188+
request.post({
189+
url: 'https://stats.count.ly/i/bulk',
190+
json: formData
191+
}, function(a) {
192+
log.d('Done running stats job: %j', a);
169193
done();
194+
});
195+
196+
if (tracker.isEnabled()) {
197+
const dataMonthly = StatsJob.generateDataMonthly(allData);
198+
199+
const Countly = tracker.getSDK();
200+
Countly.user_details({
201+
'custom': dataMonthly,
202+
});
203+
204+
Countly.userData.save();
170205
}
171206
});
172207
}
@@ -184,4 +219,4 @@ class StatsJob extends job.Job {
184219
}
185220
}
186221

187-
module.exports = StatsJob;
222+
module.exports = StatsJob;

plugins/server-stats/tests.js plugins/server-stats/tests/api.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
var request = require('supertest');
22
var should = require('should');
3-
var testUtils = require("../../test/testUtils");
4-
const pluginManager = require('../pluginManager.js');
5-
var statInternalEvents = require('../server-stats/api/parts/stats.js').internalEventsEnum;
3+
var testUtils = require('../../../test/testUtils');
4+
const pluginManager = require('../../pluginManager.js');
5+
var statInternalEvents = require('../../server-stats/api/parts/stats.js').internalEventsEnum;
66
request = request.agent(testUtils.url);
77
const dataPointTimeout = 1100;
88

@@ -617,4 +617,4 @@ describe('Testing data points plugin', function() {
617617
});
618618
});
619619
});
620-
});
620+
});

plugins/server-stats/tests/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
require('./api.js');
2+
require('./job.js');

0 commit comments

Comments
 (0)