Skip to content

Commit 1da45a0

Browse files
authored
Merge branch 'master' into dependabot/npm_and_yarn/supertest-7.1.3
2 parents ebad195 + 97e4bc2 commit 1da45a0

File tree

66 files changed

+453
-2182
lines changed

Some content is hidden

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

66 files changed

+453
-2182
lines changed

.github/dependabot.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,15 +174,6 @@ updates:
174174
- dependencies
175175
versioning-strategy: increase-if-necessary
176176

177-
- package-ecosystem: npm
178-
directory: "/plugins/enterpriseinfo"
179-
schedule:
180-
interval: daily
181-
open-pull-requests-limit: 10
182-
labels:
183-
- dependencies
184-
versioning-strategy: increase-if-necessary
185-
186177
- package-ecosystem: npm
187178
directory: "/plugins/errorlogs"
188179
schedule:

CHANGELOG.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
## Version 25.03.XX
22
Fixes:
3-
- [active_directory] Fix for reading azure application tenant id from config
3+
- [core] Fix user analytics widget chart
4+
- [core] Fix mongo connection url parsing
5+
- [crashes] Fix free session and free user calculation
6+
- [dashboards] Delete associated widgets and reports when a dashboard is removed
7+
8+
Enterprise Fixes:
9+
- [crash_symbolication] Remove auto symbolication setting
10+
11+
## Version 25.03.10
12+
Enterprise Fixes:
13+
- [okta] Fix body parser middleware version mismatch causing OKTA authentication break
14+
15+
## Version 25.03.9
16+
Features:
17+
- [core] Added support for prerelease and build fields in app version parsing
18+
19+
Fixes:
420
- [core] Set up default headers for common return methods
21+
- [star-rating] Fix widget close button
522

623
Enterprise Fixes:
24+
- [active_directory] Fix for reading azure application tenant id from config
25+
- [active_directory] Fix for handling azure ad callback properly when request body empty
726
- [drill] Disabling the view user profiles button on drill for queries going to the report manager
27+
- [drill] Fixed typo issue while getting segment values in drill widgets
828
- [journeys] Fixed the issue where events added in Journey couldn't have their visibility updated in Data Manager plugin
29+
- [surveys] Fix widget close button
930

1031
## Version 25.03.8
1132
Fixes:

api/utils/countly-request/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/docker/k8s/countly-api.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ spec:
5252
timeoutSeconds: 30
5353
env:
5454
- name: COUNTLY_PLUGINS
55-
value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,enterpriseinfo,logger,systemlogs,errorlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,assistant,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards"
55+
value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,errorlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,assistant,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards"
5656
# Countly Enterprise: value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,license,drill,funnels,retention_segments,flows,cohorts,surveys,remote-config,ab-testing,formulas,activity-map,concurrent_users,revenue,logger,systemlogs,errorlogs,populator,reports,crashes,push,geo,block,restrict,users,star-rating,slipping-away-users,compare,server-stats,assistant,dbviewer,crash_symbolication,groups,white-labeling,alerts,times-of-day,compliance-hub,onboarding,active_users,performance-monitoring,config-transfer,consolidate,data-manager,hooks,dashboards,heatmaps"
5757
- name: COUNTLY_CONFIG_API_FILESTORAGE
5858
value: "gridfs"

bin/docker/k8s/countly-frontend.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ spec:
5252
timeoutSeconds: 30
5353
env:
5454
- name: COUNTLY_PLUGINS
55-
value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,enterpriseinfo,logger,systemlogs,errorlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,assistant,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards"
55+
value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,errorlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,assistant,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards"
5656
# Countly Enterprise: value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,license,drill,funnels,retention_segments,flows,cohorts,surveys,remote-config,ab-testing,formulas,activity-map,concurrent_users,revenue,logger,systemlogs,errorlogs,populator,reports,crashes,push,geo,block,restrict,users,star-rating,slipping-away-users,compare,server-stats,assistant,dbviewer,crash_symbolication,groups,white-labeling,alerts,times-of-day,compliance-hub,onboarding,active_users,performance-monitoring,config-transfer,consolidate,data-manager,hooks,dashboards,heatmaps"
5757
- name: COUNTLY_CONFIG_API_FILESTORAGE
5858
value: "gridfs"

bin/docker/k8s/ingestion/countly-ingestion.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ spec:
4141
timeoutSeconds: 30
4242
env:
4343
- name: COUNTLY_PLUGINS
44-
value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,enterpriseinfo,logger,systemlogs,errorlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,assistant,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards"
44+
value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,errorlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,assistant,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards"
4545
# Countly Enterprise: value: "mobile,web,desktop,plugins,density,locale,browser,sources,views,license,drill,funnels,retention_segments,flows,cohorts,surveys,remote-config,ab-testing,formulas,activity-map,concurrent_users,revenue,logger,systemlogs,errorlogs,populator,reports,crashes,push,geo,block,restrict,users,star-rating,slipping-away-users,compare,server-stats,assistant,dbviewer,crash_symbolication,groups,white-labeling,alerts,times-of-day,compliance-hub,onboarding,active_users,performance-monitoring,config-transfer,consolidate,data-manager,hooks,dashboards,heatmaps"
4646
- name: COUNTLY_CONFIG_API_FILESTORAGE
4747
value: "gridfs"
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/**
2+
* Description : Remove orphan widget and long_tasks records left behind after a dashboard is deleted
3+
* Server : countly
4+
* Path : $(countly dir)/bin/scripts/fix-data
5+
* Command : node delete_widgets_of_deleted_dashboards.js [--dry-run]
6+
* Usage :
7+
* # Preview only
8+
* node delete_widgets_of_deleted_dashboards.js --dry-run
9+
* # Actual deletion
10+
* node delete_widgets_of_deleted_dashboards.js
11+
*/
12+
const DRY_RUN = process.argv.includes('--dry-run');
13+
14+
const BATCH_SIZE = 1000;
15+
const pluginManager = require('../../../plugins/pluginManager.js');
16+
17+
// Widget type configurations for long_tasks relationships
18+
const WIDGET_LONG_TASK_CONFIG = {
19+
'drill': {
20+
reportField: 'drill_report'
21+
},
22+
'users': {
23+
reportField: 'drill_report'
24+
},
25+
'formulas': {
26+
reportField: 'cmetrics'
27+
}
28+
};
29+
30+
/**
31+
* Deletes documents in batches to avoid oversized commands
32+
* @param {Object} db - MongoDB connection
33+
* @param {String} collection - Collection name
34+
* @param {Array} ids - List of document ids to delete
35+
*/
36+
async function deleteByChunks(db, collection, ids) {
37+
let bucket = [];
38+
39+
for (const id of ids) {
40+
bucket.push(id);
41+
42+
if (bucket.length === BATCH_SIZE) {
43+
await runDelete(bucket);
44+
bucket = [];
45+
}
46+
}
47+
48+
if (bucket.length) {
49+
await runDelete(bucket);
50+
}
51+
52+
/**
53+
* Executes the delete operation for a batch of ids
54+
* @param {Array} batch - Array of document ids to delete
55+
* @returns {Promise<void>}
56+
* */
57+
async function runDelete(batch) {
58+
if (DRY_RUN) {
59+
console.log(`[dry-run] ${collection}: would delete ${batch.length}`);
60+
}
61+
else {
62+
const res = await db.collection(collection).deleteMany({ _id: { $in: batch } });
63+
console.log(`[deleted] ${collection}: ${res.deletedCount}`);
64+
}
65+
}
66+
}
67+
68+
/**
69+
* Counts references to reports and returns only unreferenced ones
70+
* @param {Object} db - MongoDB connection
71+
* @param {Array} reportIds - Report IDs to be checked
72+
* @param {Array} excludeWidgetIds - Widget IDs to exclude from reference check
73+
* @returns {Array} Unreferenced report IDs
74+
*/
75+
async function getUnreferencedReports(db, reportIds, excludeWidgetIds) {
76+
if (!reportIds || !reportIds.length) {
77+
return [];
78+
}
79+
80+
let referencedReports = [];
81+
82+
// Check all widget types that can reference reports
83+
for (const [widgetType, config] of Object.entries(WIDGET_LONG_TASK_CONFIG)) {
84+
const query = {
85+
widget_type: widgetType,
86+
[config.reportField]: { $in: reportIds }
87+
};
88+
89+
// Exclude orphan widgets from reference check
90+
if (excludeWidgetIds.length) {
91+
query._id = { $nin: excludeWidgetIds };
92+
}
93+
94+
const widgets = await db.collection('widgets').find(query, { [config.reportField]: 1 }).toArray();
95+
96+
widgets.forEach(widget => {
97+
const reports = widget[config.reportField] || [];
98+
referencedReports.push(...reports.map(reportId => reportId.toString()));
99+
});
100+
}
101+
102+
// Return only those report IDs that are not referenced in any widget
103+
return reportIds.filter(reportId => !referencedReports.includes(reportId.toString()));
104+
}
105+
106+
/**
107+
* Collects all linked long_task IDs from a widget based on its type
108+
* @param {Object} widget - Widget document
109+
* @returns {Array} Array of long_task IDs
110+
*/
111+
function collectAllLinkedLongTasks(widget) {
112+
const config = WIDGET_LONG_TASK_CONFIG[widget.widget_type];
113+
if (!config) {
114+
return [];
115+
}
116+
117+
const reportField = config.reportField;
118+
return Array.isArray(widget[reportField]) ? widget[reportField] : [];
119+
}
120+
121+
122+
(async() => {
123+
const db = await pluginManager.dbConnection('countly');
124+
125+
try {
126+
const dashboardWidgets = [];
127+
128+
const dashCursor = db.collection('dashboards').find({widgets: {$exists: true, $not: {$size: 0}}}, {projection: {widgets: 1}});
129+
130+
while (await dashCursor.hasNext()) {
131+
const dash = await dashCursor.next();
132+
for (const w of dash.widgets) {
133+
const idStr = (w && w.$oid) ? w.$oid : (w + '');
134+
if (idStr && !dashboardWidgets.includes(idStr)) {
135+
dashboardWidgets.push(idStr);
136+
}
137+
}
138+
}
139+
140+
await dashCursor.close();
141+
142+
const orphanWidgetIds = [];
143+
const allLinkedLongTasks = [];
144+
145+
const widgetCursor = db.collection('widgets').find({});
146+
147+
while (await widgetCursor.hasNext()) {
148+
const w = await widgetCursor.next();
149+
if (!dashboardWidgets.includes(String(w._id))) {
150+
orphanWidgetIds.push(w._id);
151+
152+
// Find linked long_tasks based on widget type
153+
const linkedTasks = collectAllLinkedLongTasks(w);
154+
allLinkedLongTasks.push(...linkedTasks);
155+
}
156+
}
157+
await widgetCursor.close();
158+
159+
console.log(`Orphan widgets found: ${orphanWidgetIds.length}`);
160+
if (DRY_RUN && orphanWidgetIds.length) {
161+
console.log('Orphan widget IDs to be deleted:', orphanWidgetIds.map(id => id.toString()));
162+
}
163+
await deleteByChunks(db, 'widgets', orphanWidgetIds);
164+
165+
const unreferencedLongTasks = await getUnreferencedReports(db, allLinkedLongTasks, orphanWidgetIds);
166+
console.log(`Unreferenced long_tasks to delete: ${unreferencedLongTasks.length}`);
167+
if (DRY_RUN && unreferencedLongTasks.length) {
168+
console.log('Unreferenced long_task IDs to be deleted:', unreferencedLongTasks.map(id => id.toString()));
169+
}
170+
await deleteByChunks(db, 'long_tasks', unreferencedLongTasks);
171+
172+
console.log(DRY_RUN ? 'Dry-run finished' : 'Cleanup completed.');
173+
}
174+
catch (err) {
175+
console.error(err);
176+
}
177+
finally {
178+
db.close();
179+
}
180+
})();

bin/scripts/modify-data/changeOwner.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,32 @@ var OLD_ID = "59832c06c5e80024b5e1fa1f"; //Id for old user
1111
var pluginManager = require('./../../../plugins/pluginManager');
1212
var common = require('./../../../api/utils/common');
1313
var async = require('async');
14+
const Promise = require('bluebird');
1415

1516
pluginManager.dbConnection("countly").then((db) => {
1617
common.db = db;
18+
const NEW_OID = common.db.ObjectID(NEW_ID);
19+
const OLD_OID = common.db.ObjectID(OLD_ID);
1720

1821
function changeOwner(NEW_ID, OLD_ID, done) {
1922
changeOwnerDashboard(NEW_ID, OLD_ID, function() {
20-
common.db.collection("alerts").update({createdBy: OLD_ID}, {$set: {createdBy: NEW_ID}}, function(err, res) {
23+
common.db.collection("alerts").update({createdBy: OLD_OID}, {$set: {createdBy: NEW_OID}}, function(err, res) {
2124
console.log("alerts", err, res && res.result);
2225
common.db.collection("auth_tokens").update({owner: OLD_ID}, {$set: {owner: NEW_ID}}, function(err, res) {
2326
console.log("auth_tokens", err, res && res.result);
2427
common.db.collection("calculated_metrics").update({owner_id: OLD_ID}, {$set: {owner_id: NEW_ID}}, function(err, res) {
2528
console.log("calculated_metrics", err, res && res.result);
26-
common.db.collection("concurrent_users_alerts").update({created_by: OLD_ID}, {$set: {created_by: NEW_ID}}, function(err, res) {
29+
common.db.collection("concurrent_users_alerts").update({created_by: OLD_OID}, {$set: {created_by: NEW_OID}}, function(err, res) {
2730
console.log("concurrent_users_alerts", err, res && res.result);
28-
common.db.collection("data_migrations").update({userid: OLD_ID}, {$set: {userid: NEW_ID}}, function(err, res) {
31+
common.db.collection("data_migrations").update({userid: OLD_OID}, {$set: {userid: NEW_OID}}, function(err, res) {
2932
console.log("data_migrations", err, res && res.result);
3033
changeOwnerLongTasks(NEW_ID, OLD_ID, function(err, res) {
31-
console.log("messages", err, res && res.result);
32-
common.db.collection("messages").update({creator: OLD_ID}, {$set: {creator: NEW_ID}}, function(err, res) {
34+
console.log("messages", err, res && res.result); // "createdBy": { "$oid": "63442fa1ec94a1edb60f2453"}
35+
common.db.collection("messages").update({"info.createdBy": OLD_ID}, {$set: {"info.createdBy": NEW_ID}}, function(err, res) {
3336
console.log("messages", err, res && res.result);
3437
common.db.collection("notes").update({owner: OLD_ID}, {$set: {owner: NEW_ID}}, function(err, res) {
3538
console.log("notes", err, res && res.result);
36-
common.db.collection("reports").update({user: OLD_ID}, {$set: {user: NEW_ID}}, function(err, res) {
39+
common.db.collection("reports").update({user: OLD_OID}, {$set: {user: NEW_OID}}, function(err, res) {
3740
console.log("reports", err, res && res.result);
3841
done();
3942
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const pluginManager = require('../../../../plugins/pluginManager.js');
2+
3+
console.log('Removing auto symbolication setting');
4+
5+
pluginManager.dbConnection().then(async(db) => {
6+
try {
7+
await db.collection('plugins').updateOne({ _id: 'plugins' }, { $unset: { 'crashes.automatic_symbolication': '' } });
8+
console.log('Auto symbolication setting removed');
9+
}
10+
catch(err) {
11+
console.error('Error while removing auto symbolication setting', err);
12+
}
13+
14+
db.close();
15+
});

frontend/express/public/core/user-analytics-overview/javascripts/countly.views.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ app.route("/analytics/users/*tab", "user-analytics-tab", function(tab) {
181181
});
182182
//Analytics->User analytics - overview widget
183183
var GridComponent = countlyVue.views.create({
184-
template: CV.T('/dashboards/templates/widgets/analytics/widget.html'), //using core dashboard widget template
184+
template: CV.T('/core/user-analytics-overview/templates/widget.html'),
185185
mixins: [countlyVue.mixins.customDashboards.global, countlyVue.mixins.customDashboards.widget, countlyVue.mixins.customDashboards.apps, countlyVue.mixins.zoom, countlyVue.mixins.hasDrawers("annotation"), countlyVue.mixins.graphNotesCommand],
186186
components: {
187187
"drawer": countlyGraphNotesCommon.drawer
@@ -292,11 +292,8 @@ var GridComponent = countlyVue.views.create({
292292
stackedBarTimeSeriesOptions: function() {
293293
return this.timelineGraph.lineOptions;
294294
},
295-
stackedBarOptions: function() {
295+
barOptions: function() {
296296
var data = this.timelineGraph;
297-
for (var k = 0; k < data.lineOptions.series.length; k++) {
298-
data.lineOptions.series[k].stack = "A";
299-
}
300297
return data.lineOptions;
301298
},
302299
number: function() {

0 commit comments

Comments
 (0)