Skip to content

Commit e61c8c8

Browse files
authored
Merge branch 'master' into dependabot/npm_and_yarn/nyc-17.1.0
2 parents ecac4fb + 4412e3e commit e61c8c8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2742
-526
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## Version 24.05.12
2+
Fixes:
3+
- [dashboards] Fixes for dashboards grid
4+
- [dasboards] UI fix for dashboard widget action menu
5+
- [push] Refactored fcm API related code
6+
- [reports] Use config for encryption key in reports
7+
8+
Enterprise fixes:
9+
- [retention] Fixes for byval retention query calculation
10+
111
## Version 24.05.11
212
Fixes:
313
- [cache] Use a cursor without timeout

bin/scripts/fix-data/user-merge.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* Description: This script is used to merge users based on username.
3+
* Server: countly
4+
* Path: $(countly dir)/bin/scripts/fix-data
5+
* Command: node user-merge.js
6+
*/
7+
var pluginManager = require("../../../plugins/pluginManager.js");
8+
var appUsers = require("../../../api/parts/mgmt/app_users.js");
9+
var common = require("../../../api/utils/common.js");
10+
11+
console.log("Merging app users");
12+
13+
var APP_ID = "";
14+
var COLLECTION_NAME = "app_users" + APP_ID;
15+
16+
var RETRY_LIMIT = 3;
17+
var UPDATE_COUNTER = 0;
18+
19+
//Number of requests to be made before checking record count in app_user_merges
20+
var UPDATE_LIMIT = 100;
21+
//Number of records in app_user_merges after which script will sleep
22+
var RECORD_COUNT_LIMIT = 10;
23+
//Cooldown period if record count exceeds limit
24+
var RECORD_OVERLOAD_SLEEP = 2000;
25+
//Cooldown period between requests
26+
var COOLDOWN_PERIOD = 1000;
27+
28+
const sleep = m => new Promise((r) => {
29+
//console.log("Cooling period for " + m + " seconds!");
30+
setTimeout(r, m);
31+
});
32+
33+
pluginManager.dbConnection("countly").then(async(countlyDb) => {
34+
try {
35+
36+
common.db = countlyDb;
37+
38+
await cursor();
39+
40+
console.log("Total updates on the server - ", UPDATE_COUNTER);
41+
console.log("Script ran successfully!");
42+
common.db.close();
43+
process.exit(1);
44+
}
45+
catch (e) {
46+
console.log("Error while running script ", e);
47+
common.db.close();
48+
process.exit(1);
49+
}
50+
51+
async function cursor() {
52+
53+
const duplicates = await common.db.collection(COLLECTION_NAME).aggregate([
54+
{
55+
$group: {
56+
_id: "$username",
57+
count: { $sum: 1 }
58+
}
59+
},
60+
{
61+
$match: {
62+
count: { $gt: 1 },
63+
_id: { $ne: null }
64+
}
65+
}
66+
]).toArray();
67+
68+
console.log("Found", duplicates.length, "duplicate username groups.");
69+
70+
for (var i = 0; i < duplicates.length; i++) {
71+
72+
var mainUser = null;
73+
var mergedUsersUIDs = [];
74+
75+
var query = {
76+
username: duplicates[i]._id
77+
};
78+
79+
var projections = {};
80+
81+
var sort = { ls: -1 };
82+
83+
var cursor = common.db.collection(COLLECTION_NAME).find(query).project(projections).sort(sort);
84+
85+
while (await cursor.hasNext()) {
86+
var doc = await cursor.next();
87+
88+
if (doc.uid && doc.uid !== "") {
89+
if (!mainUser) {
90+
mainUser = doc;
91+
}
92+
else {
93+
await mergeUsers(mainUser, doc);
94+
mergedUsersUIDs.push(doc.uid);
95+
}
96+
}
97+
}
98+
99+
if (mergedUsersUIDs.length > 0) {
100+
console.log("Total", mergedUsersUIDs.length, "users merged into user", mainUser.uid, ": (", mergedUsersUIDs.join(", "), ")");
101+
}
102+
}
103+
}
104+
105+
async function mergeUsers(mainUser, user) {
106+
var retryCounter = 1;
107+
var success = false;
108+
109+
while (!success && retryCounter < RETRY_LIMIT) {
110+
await new Promise(function(resolve) {
111+
var newUser = JSON.parse(JSON.stringify(mainUser));
112+
113+
appUsers.merge(APP_ID, newUser, newUser._id, user._id, newUser.did, user.did, function(err) {
114+
if (err) {
115+
console.log("Error while merging - ", err);
116+
retryCounter += 1;
117+
}
118+
else {
119+
success = true;
120+
}
121+
122+
resolve();
123+
});
124+
});
125+
await sleep(COOLDOWN_PERIOD);
126+
}
127+
128+
if (success) {
129+
if (retryCounter > 1) {
130+
console.log("User ", user.uid, " merged successfully after ", retryCounter, " retries.");
131+
}
132+
UPDATE_COUNTER += 1;
133+
if (UPDATE_COUNTER % UPDATE_LIMIT === 0) {
134+
await checkRecordCount();
135+
}
136+
}
137+
else {
138+
console.log("Retry limit exceeded for users ", mainUser.uid, " and ", user.uid);
139+
}
140+
}
141+
142+
async function checkRecordCount() {
143+
var recordCount = await common.db.collection("app_user_merges").countDocuments();
144+
console.log("Record count in app_user_merges: ", recordCount);
145+
146+
while (recordCount > RECORD_COUNT_LIMIT) {
147+
console.log("Record count exceeds limit. Sleeping for " + RECORD_OVERLOAD_SLEEP / 1000 + "seconds.");
148+
await sleep(RECORD_OVERLOAD_SLEEP);
149+
recordCount = await common.db.collection("app_user_merges").countDocuments();
150+
}
151+
}
152+
});

frontend/express/public/javascripts/countly/vue/components/date.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,9 @@
11381138
},
11391139
methods: {
11401140
loadValue: function(value) {
1141+
if (!value) {
1142+
return;
1143+
}
11411144
var changes = this.valueToInputState(value),
11421145
self = this;
11431146
changes.label = getRangeLabel(changes, this.type);

frontend/express/public/javascripts/countly/vue/components/dropdown.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@
529529
template: '<cly-dropdown class="cly-vue-more-options" ref="dropdown" :placement="placement" :disabled="disabled" v-on="$listeners">\
530530
<template v-slot:trigger>\
531531
<slot name="trigger">\
532-
<el-button :data-test-id="testId + \'-more-option-button\'" :size="size" :icon="icon" :type="type">\
532+
<el-button :data-test-id="testId + \'-more-option-button\'" :size="size" :icon="icon" :type="type" :disabled="disabledButton">\
533533
<span :data-test-id="testId + \'-more-option-text\'" v-if="text">{{text}}</span>\
534534
</el-button>\
535535
</slot>\
@@ -560,6 +560,10 @@
560560
type: Boolean,
561561
default: false
562562
},
563+
disabledButton: {
564+
type: Boolean,
565+
default: false,
566+
},
563567
placement: {
564568
type: String,
565569
default: 'bottom-end'

plugins/alerts/api/alertModules/cohorts.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const commonLib = require("../parts/common-lib.js");
99
const { ObjectId } = require('mongodb');
1010

1111
module.exports.check = async function({ alertConfigs: alert, done, scheduledTo: date }) {
12-
const app = await common.db.collection("apps").findOne({ _id: ObjectId(alert.selectedApps[0]) });
12+
const app = await common.readBatcher.getOne("apps", { _id: new ObjectId(alert.selectedApps[0]) });
1313
if (!app) {
1414
log.e(`App ${alert.selectedApps[0]} couldn't be found`);
1515
return done();
@@ -22,7 +22,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
2222

2323
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
2424
if (metricValue > compareValue) {
25-
await commonLib.trigger({ alert, app, metricValue, date });
25+
await commonLib.trigger({ alert, app, metricValue, date }, log);
2626
}
2727
}
2828
else {
@@ -38,7 +38,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
3838
: change <= -compareValue;
3939

4040
if (shouldTrigger) {
41-
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore });
41+
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
4242
}
4343
}
4444

plugins/alerts/api/alertModules/dataPoints.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
1414
const selectedApp = alert.selectedApps[0];
1515
let apps;
1616
if (selectedApp === "all") {
17-
apps = await common.db.collection("apps").find().toArray();
17+
apps = await common.readBatcher.getMany("apps", {});
1818
}
1919
else {
20-
apps = [await common.db.collection("apps").findOne({ _id: ObjectId(selectedApp) })];
20+
apps = [await common.readBatcher.getOne("apps", { _id: new ObjectId(selectedApp) })];
2121
}
2222

2323
for (let app of apps) {
@@ -33,7 +33,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
3333

3434
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
3535
if (metricValue > compareValue) {
36-
await commonLib.trigger({ alert, app, metricValue, date });
36+
await commonLib.trigger({ alert, app, metricValue, date }, log);
3737
}
3838
}
3939
else {
@@ -49,7 +49,7 @@ module.exports.check = async function({ alertConfigs: alert, done, scheduledTo:
4949
: change <= -compareValue;
5050

5151
if (shouldTrigger) {
52-
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore });
52+
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
5353
}
5454
}
5555
}

plugins/alerts/api/alertModules/events.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,8 @@ const METRIC_TO_PROPERTY_MAP = {
2121

2222
const AVERAGE_METRICS = ["average sum", "average duration"];
2323

24-
/**
25-
* Alert triggering logic
26-
* @param {Alert} alert - alert document
27-
* @param {function} done - callback function
28-
* @param {Date} date - scheduled date for the alert (job.next)
29-
*/
3024
module.exports.check = async({ alertConfigs: alert, done, scheduledTo: date }) => {
31-
const app = await common.db.collection("apps").findOne({ _id: ObjectId(alert.selectedApps[0]) });
25+
const app = await common.readBatcher.getOne("apps", { _id: new ObjectId(alert.selectedApps[0]) });
3226
if (!app) {
3327
log.e(`App ${alert.selectedApps[0]} couldn't be found`);
3428
return done();
@@ -55,7 +49,7 @@ module.exports.check = async({ alertConfigs: alert, done, scheduledTo: date }) =
5549

5650
if (compareType === commonLib.COMPARE_TYPE_ENUM.MORE_THAN) {
5751
if (metricValue > compareValue) {
58-
await commonLib.trigger({ alert, app, metricValue, date });
52+
await commonLib.trigger({ alert, app, metricValue, date }, log);
5953
}
6054
}
6155
else {
@@ -81,7 +75,7 @@ module.exports.check = async({ alertConfigs: alert, done, scheduledTo: date }) =
8175
: change <= -compareValue;
8276

8377
if (shouldTrigger) {
84-
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore });
78+
await commonLib.trigger({ alert, app, date, metricValue, metricValueBefore }, log);
8579
}
8680
}
8781
done();
@@ -97,7 +91,7 @@ module.exports.getEventMetricByDate = getEventMetricByDate;
9791
* @param {string} metric - c, s, dur
9892
* @param {Date} date - date of the value you're looking for
9993
* @param {string} period - hourly|daily|monthly
100-
* @param {object} segments - segmentation filter. e.g. {Category:"Electronics"}
94+
* @param {object=} segments - segmentation filter. e.g. {Category:"Electronics"}
10195
* @returns {Promise<number|undefined>} - a promise resolves to metric value or undefined
10296
*/
10397
async function getEventMetricByDate(app, event, metric, date, period, segments) {

0 commit comments

Comments
 (0)