Skip to content

Commit 72c1c2b

Browse files
committed
Merge branch 'master' into next
# Conflicts: # CHANGELOG.md
2 parents 8cb56aa + e241810 commit 72c1c2b

File tree

51 files changed

+2071
-1292
lines changed

Some content is hidden

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

51 files changed

+2071
-1292
lines changed

.github/workflows/main.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,29 @@ jobs:
2121
# This workflow contains a single job called "build"
2222
install:
2323
# The type of runner that the job will run on
24-
runs-on: ubuntu-20.04
24+
runs-on: ubuntu-22.04
2525

2626
# Steps represent a sequence of tasks that will be executed as part of the job
2727
steps:
2828
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
2929
- uses: actions/checkout@v3
3030

31+
- name: Copy code
32+
shell: bash
33+
run: |
34+
sudo mkdir -p /opt/countly
35+
touch log/countly-dashboard.log
36+
touch log/countly-api.log
37+
cp -rf ./* /opt/countly
38+
3139
- name: Github Actions Azure connection fix
3240
run: |
3341
# Workaround for https://github.com/actions/runner-images/issues/675#issuecomment-1381389712
3442
sudo sed -i 's/azure/us/g' /etc/apt/sources.list
35-
43+
3644
- name: Installing Countly
3745
shell: bash
46+
working-directory: /opt/countly
3847
run: sudo bash ./bin/countly.install.sh
3948

4049
- name: NodeJS version
@@ -50,10 +59,12 @@ jobs:
5059
run: mongosh --version
5160

5261
- name: Output API Logs
62+
working-directory: /opt/countly
5363
if: ${{ always() }}
5464
run: cat log/countly-api.log
5565

5666
- name: Output Dashboard Logs
67+
working-directory: /opt/countly
5768
if: ${{ always() }}
5869
run: cat log/countly-dashboard.log
5970

CHANGELOG.md

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,41 @@
11
## Version 25.03.x
2+
Fixes:
3+
- [feedback] Uniformize drawer internal name input texts
4+
- [star-rating] Added missing columns to Rating Widgets table edit
5+
- [star-rating] Fix rating score and responses table sorting
6+
- [ui] Fix alignment of drawers title and close icon
7+
8+
Dependencies:
9+
- Bump countly-sdk-web from 25.1.0 to 25.4.0
10+
- Bump mongodb from 6.14.2 to 6.16.0
11+
- Bump nodemailer from 6.10.0 to 6.10.1
12+
- Bump puppeteer from 23.10.4 to 24.7.0
13+
- Bump sass from 1.86.0 to 1.87.0
14+
- Bump typescript from 5.8.2 to 5.8.3
15+
16+
## Version 25.03.4
217
Features:
318
- Add ability to allow multiple CORS per app for web apps
419
- Add app id and name as view segment for self-tracking
5-
- [alerts] alerts table default order should be by creation time newest at the top
6-
- [core] allow tracking Countly dashboard usage with Countly
7-
- [sdk] Improved and added new Server Config options
20+
- [dashboards] Added the option to set a refresh rate for dashboards, allowing data to update more frequently for selected dashboards
821

922
Enterprise Features:
10-
- [journey_engine] Editing/Deleting/Duplication of blocks and version management
11-
- [cohorts] [funnels] [ai-assistants] Add cohorts and funnels assistant
23+
- [license] Update locking conditions for expired license and over limit usage
24+
- [license] Enable force locking with remote config
25+
- [license] Update dashboard lock with redirection to home page for non admin user
26+
27+
Fixes:
28+
- [core] Allow downloading data also from other databases in dbviewer
29+
- [crashes] Fix unescaped SDK logs
30+
- [crash_symbolication] Symbolication server api end point test fix
31+
- [star-rating] Added missing columns to Rating Widgets table edit
32+
- [ui] Fix alignment of drawers title and close icon
33+
- [push] Fixed push notifications title and content text and variables combination
34+
- [reports] Correctly match event for email report if event key contains '.'
35+
36+
Enterprise Fixes:
37+
- [cohorts] Fixed issue with combining multiple cohorts
38+
- [drill] Do not recheck old collections on app_user data deletion if querying from old collections is disabled
1239

1340
Dependencies:
1441
- Bump body-parser from 1.20.3 to 2.2.0

Gruntfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ module.exports = function(grunt) {
483483
name = name.replace('.properties', '');
484484
if (name.indexOf('_') !== -1) {
485485
lang = name.split('_').pop();
486-
if (lang.length > 3 || lang === "en") {
486+
if (lang.length > 2 || lang === "en") {
487487
lang = '';
488488
}
489489
}

api/jobs/topEvents.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,19 @@ class TopEventsJob extends job.Job {
138138
* The errors of all functions will be caught here.
139139
*/
140140
async getAllApps() {
141+
log.d("Fetching all apps");
141142
try {
142143
const getAllApps = await new Promise((res, rej) => common.db.collection("apps").find({}, { _id: 1, timezone: 1 }).toArray((err, apps) => err ? rej(err) : res(apps)));
143-
await Promise.all(getAllApps.map((app) => this.getAppEvents(app)));
144+
for (var z = 0; z < getAllApps.length; z++) {
145+
//Calculating for each app serially.
146+
await this.getAppEvents(getAllApps[z]);
147+
}
144148
}
145149
catch (error) {
146150
log.e("TopEvents Job has a error: ", error);
147151
throw error;
148152
}
153+
log.d("Finished processing");
149154
}
150155

151156
/**
@@ -178,6 +183,7 @@ class TopEventsJob extends job.Job {
178183
* @param {Object} app - saveAppEvents object
179184
*/
180185
async getAppEvents(app) {
186+
log.d(app._id + ": Fetching app events");
181187
const getEvents = await new Promise((res, rej) => common.db.collection("events").findOne({ _id: app._id }, (errorEvents, result) => errorEvents ? rej(errorEvents) : res(result)));
182188
if (getEvents && 'list' in getEvents) {
183189
const eventMap = this.eventsFilter(getEvents.list);
@@ -199,6 +205,7 @@ class TopEventsJob extends job.Job {
199205
let totalDuration = 0;
200206
let prevTotalDuration = 0;
201207
for (const event of eventMap) {
208+
log.d(" getting event data for event: " + event + " (" + period + ")");
202209
const collectionNameEvents = this.eventsCollentions({ event, id: app._id });
203210
await this.getEventsCount({ collectionNameEvents, ob, data, event });
204211
totalCount += data[event].data.count.total;
@@ -208,11 +215,16 @@ class TopEventsJob extends job.Job {
208215
totalDuration += data[event].data.duration.total;
209216
prevTotalDuration += data[event].data.duration["prev-total"];
210217
}
218+
log.d(" getting session count (" + period + ")");
211219
await this.getSessionCount({ ob, sessionData, usersData, usersCollectionName });
220+
log.d(" saving data (" + period + ")");
212221
await this.saveAppEvents({ app, data, sessionData, usersData, period, totalCount, prevTotalCount, totalSum, prevTotalSum, totalDuration, prevTotalDuration });
213222
}
214223
}
215224
}
225+
else {
226+
log.d(" No events found for app");
227+
}
216228

217229
}
218230

api/lib/countly.common.js

Lines changed: 25 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
*/
55

66
/**
7-
* @typedef {import('moment-timezone')} MomentTimezone
7+
* @typedef {import('moment-timezone').Moment} MomentTimezone
88
*/
99

1010
/** @lends module:api/lib/countly.common */
1111
var countlyCommon = {},
1212
/**
1313
* Reference to momentjs
14-
* @type {MomentTimezone} moment
1514
*/
1615
moment = require('moment-timezone'),
1716
underscore = require('underscore');
@@ -24,57 +23,11 @@ var _period = "hour",
2423
// Private Methods
2524

2625
/**
27-
* Returns array with unique ticks for period
28-
* @param {MomentTimezone} startTimestamp - start of period
29-
* @param {MomentTimezone} endTimestamp - end of period
30-
* @returns {array} unique array ticks for period
31-
**/
32-
/*function getTicksBetween(startTimestamp, endTimestamp) {
33-
var dayIt = startTimestamp.clone(),
34-
ticks = [];
35-
36-
while (dayIt < endTimestamp) {
37-
let daysLeft = Math.round(moment.duration(endTimestamp - dayIt).asDays());
38-
if (daysLeft >= dayIt.daysInMonth() && dayIt.date() === 1) {
39-
ticks.push(dayIt.format("YYYY.M"));
40-
dayIt.add(1 + dayIt.daysInMonth() - dayIt.date(), "days");
41-
}
42-
else if (daysLeft >= (7 - dayIt.day()) && dayIt.day() === 1) {
43-
ticks.push(dayIt.format("YYYY.[w]W"));
44-
dayIt.add(8 - dayIt.day(), "days");
45-
}
46-
else {
47-
ticks.push(dayIt.format("YYYY.M.D"));
48-
dayIt.add(1, "day");
49-
}
50-
}
51-
52-
return ticks;
53-
}*/
54-
55-
/**
56-
* Returns array with more generalized unique ticks for period
57-
* @param {MomentTimezone} startTimestamp - start of period
58-
* @param {MomentTimezone} endTimestamp - end of period
59-
* @returns {array} unique array ticks for period
60-
**/
61-
/*function getTicksCheckBetween(startTimestamp, endTimestamp) {
62-
var dayIt = startTimestamp.clone(),
63-
ticks = [];
64-
while (dayIt < endTimestamp) {
65-
let daysLeft = Math.round(moment.duration(endTimestamp - dayIt).asDays());
66-
if (daysLeft >= (dayIt.daysInMonth() * 0.5 - dayIt.date())) {
67-
ticks.push(dayIt.format("YYYY.M"));
68-
dayIt.add(1 + dayIt.daysInMonth() - dayIt.date(), "days");
69-
}
70-
else {
71-
ticks.push(dayIt.format("YYYY.[w]W"));
72-
dayIt.add(8 - dayIt.day(), "days");
73-
}
74-
}
75-
return ticks;
76-
}*/
77-
26+
* Calculates unique values from a hierarchical map structure
27+
* @param {Object} dbObj - Database object containing hierarchical data (years, months, weeks, days)
28+
* @param {Object} uniqueMap - Map with hierarchical structure (years, months, weeks, days) used to calculate unique values
29+
* @returns {number} - Count of unique items
30+
*/
7831
countlyCommon.calculateUniqueFromMap = function(dbObj, uniqueMap) {
7932
var u = 0;
8033
for (var year in uniqueMap) {
@@ -113,13 +66,13 @@ countlyCommon.calculateUniqueFromMap = function(dbObj, uniqueMap) {
11366
};
11467

11568
/** returns unique period check array
116-
* @param {array} weeksArray_pd - weeks array
117-
* @param {array} weekCounts_pd - week counts
118-
* @param {array} monthsArray_pd - months array
119-
* @param {array} monthCounts_pd - months counts
120-
* @param {array} periodArr_pd - period array
121-
* @returns {array} periods
122-
*/
69+
* @param {Array<string>} weeksArray_pd - weeks array
70+
* @param {Array<string>} weekCounts_pd - week counts
71+
* @param {Array<string>} monthsArray_pd - months array
72+
* @param {Array<string>} monthCounts_pd - months counts
73+
* @param {Array<string>} periodArr_pd - period array
74+
* @returns {Array<string>} periods
75+
*/
12376
function getUniqArray(weeksArray_pd, weekCounts_pd, monthsArray_pd, monthCounts_pd, periodArr_pd) {
12477

12578
if (_period === "month" || _period === "day" || _period === "yesterday" || _period === "hour") {
@@ -238,12 +191,12 @@ function getUniqArray(weeksArray_pd, weekCounts_pd, monthsArray_pd, monthCounts_
238191
return uniquePeriods;
239192
}
240193
/** returns unique period check array
241-
* @param {array} weeksArray_pd - weeks array
242-
* @param {array} weekCounts_pd - week counts
243-
* @param {array} monthsArray_pd - months array
244-
* @param {array} monthCounts_pd - months counts
245-
* @returns {array} periods
246-
*/
194+
* @param {Array<string>} weeksArray_pd - weeks array
195+
* @param {Array<string>} weekCounts_pd - week counts
196+
* @param {Array<string>} monthsArray_pd - months array
197+
* @param {Array<string>} monthCounts_pd - months counts
198+
* @returns {Array<string>} periods
199+
*/
247200
function getUniqCheckArray(weeksArray_pd, weekCounts_pd, monthsArray_pd, monthCounts_pd) {
248201

249202
if (_period === "month" || _period === "day" || _period === "yesterday" || _period === "hour") {
@@ -314,9 +267,9 @@ function getUniqCheckArray(weeksArray_pd, weekCounts_pd, monthsArray_pd, monthCo
314267
}
315268

316269
/** Function to clone object
317-
* @param {object} obj - object to clone
318-
* @returns {object} cloned object
319-
*/
270+
* @param {object} obj - object to clone
271+
* @returns {object|undefined|string|Array<string>|Date} cloned object
272+
*/
320273
function clone(obj) {
321274
if (null === obj || "object" !== typeof obj) {
322275
return obj;
@@ -350,8 +303,8 @@ function clone(obj) {
350303

351304
/**
352305
* Returns number for timestamp making sure it is 13 digits
353-
* @param {integer}ts - number we need to se as timestamp
354-
* @returns {integer} timestamp in ms
306+
* @param {number} ts - number we need to se as timestamp
307+
* @returns {number} timestamp in ms
355308
**/
356309
function fixTimestampToMilliseconds(ts) {
357310
if ((ts + "").length > 13) {
@@ -363,7 +316,7 @@ function fixTimestampToMilliseconds(ts) {
363316

364317
/**
365318
* Returns a period object used by all time related data calculation functions
366-
* @param {MomentTimezone} prmPeriod period to be calculated (optional)
319+
* @param {string|any} prmPeriod period to be calculated (optional) todo:figure this type out
367320
* @param {string} bucket - daily or monthly. If bucket is set, period will be modified to fit full months or days
368321
* @returns {timeObject} time object
369322
**/

api/parts/data/batcher.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ class ReadBatcher {
422422
* @param {string} id - id of cache
423423
* @param {string} query - query for the document
424424
* @param {string} projection - which fields to return
425-
* @param {bool} multi - true if multiple documents
425+
* @param {boolean} multi - true if multiple documents
426426
* @returns {Promise} promise
427427
*/
428428
getData(collection, id, query, projection, multi) {
@@ -517,7 +517,7 @@ class ReadBatcher {
517517
* @param {string} collection - name of the collection where to update data
518518
* @param {object} query - query for the document
519519
* @param {object} projection - which fields to return
520-
* @param {bool} multi - true if multiple documents
520+
* @param {boolean} multi - true if multiple documents
521521
*/
522522
invalidate(collection, query, projection, multi) {
523523
if (!this.onMaster || cluster.isMaster) {
@@ -539,7 +539,7 @@ class ReadBatcher {
539539
* @param {string} collection - name of the collection where to update data
540540
* @param {object} query - query for the document
541541
* @param {object} projection - which fields to return
542-
* @param {bool} multi - true if multiple documents
542+
* @param {boolean} multi - true if multiple documents
543543
* @returns {Promise} promise
544544
*/
545545
get(collection, query, projection, multi) {
@@ -651,7 +651,7 @@ class ReadBatcher {
651651
* @param {string} query - query for the document
652652
* @param {string} projection - which fields to return
653653
* @param {object} data - data from database
654-
* @param {bool} multi - true if multiple documents
654+
* @param {boolean} multi - true if multiple documents
655655
*/
656656
cache(collection, id, query, projection, data, multi) {
657657
if (this.process) {

api/parts/data/usage.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ usage.returnAllProcessedMetrics = function(params) {
398398
continue;
399399
}
400400

401-
if (recvMetricValue) {
401+
if (recvMetricValue !== undefined && recvMetricValue !== null && recvMetricValue !== "") {
402402
var escapedMetricVal = (recvMetricValue + "").replace(/^\$/, "").replace(/\./g, ":");
403403
processedMetrics[tmpMetric.short_code] = escapedMetricVal;
404404
}
@@ -705,7 +705,7 @@ function processMetrics(user, uniqueLevelsZero, uniqueLevelsMonth, params, done)
705705
continue;
706706
}
707707

708-
if (recvMetricValue) {
708+
if (recvMetricValue !== undefined && recvMetricValue !== null && recvMetricValue !== "") {
709709
recvMetricValue = (recvMetricValue + "").replace(/^\$/, "").replace(/\./g, ":");
710710
postfix = common.crypto.createHash("md5").update(recvMetricValue).digest('base64')[0];
711711
metaToFetch[predefinedMetrics[i].db + params.app_id + "_" + dateIds.zero + "_" + postfix] = {
@@ -767,7 +767,7 @@ function processMetrics(user, uniqueLevelsZero, uniqueLevelsMonth, params, done)
767767
continue;
768768
}
769769

770-
if (recvMetricValue) {
770+
if (recvMetricValue !== undefined && recvMetricValue !== null && recvMetricValue !== "") {
771771
escapedMetricVal = (recvMetricValue + "").replace(/^\$/, "").replace(/\./g, ":");
772772
postfix = common.crypto.createHash("md5").update(escapedMetricVal).digest('base64')[0];
773773

@@ -1110,6 +1110,15 @@ plugins.register("/sdk/user_properties", async function(ob) {
11101110
userProps[key] = up[key];
11111111
}
11121112
}
1113+
1114+
if (params.qstring.metrics._app_version) {
1115+
const versionComponents = common.parseAppVersion(params.qstring.metrics._app_version);
1116+
if (versionComponents.success) {
1117+
userProps.av_major = versionComponents.major;
1118+
userProps.av_minor = versionComponents.minor;
1119+
userProps.av_patch = versionComponents.patch;
1120+
}
1121+
}
11131122
}
11141123

11151124
if (params.qstring.session_duration) {

0 commit comments

Comments
 (0)