Skip to content

Commit 8f65e39

Browse files
authored
Merge pull request #6483 from Countly/ar2rsawseen/next
Add tests
2 parents 1943815 + 0c08f26 commit 8f65e39

Some content is hidden

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

42 files changed

+24536
-228
lines changed

api/parts/mgmt/event_groups.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ const create = (params) => {
3939
'type': 'Boolean'
4040
}
4141
};
42+
if (!params.qstring.args) {
43+
common.returnMessage(params, 400, 'Error: args not found');
44+
return false;
45+
}
4246
params.qstring.args = JSON.parse(params.qstring.args);
4347
const {obj, errors} = common.validateArgs(params.qstring.args, argProps, true);
4448
if (!obj) {
@@ -59,7 +63,14 @@ const create = (params) => {
5963

6064
/**
6165
* Event Groups CRUD - The function updating which created `Event Groups` data by `_id`
62-
* @param {Object} params -
66+
* @param {Object} params - params object containing the query string and other parameters
67+
* @returns {Boolean}
68+
* This function updates the event groups based on the provided parameters.
69+
* It handles different update scenarios:
70+
* 1. If `args` is provided, it updates the event group with the specified `_id`.
71+
* 2. If `event_order` is provided, it updates the order of the events in the group.
72+
* 3. If `update_status` is provided, it updates the status of the specified event groups.
73+
* 4. If none of these parameters are found, it returns a 400 error indicating that the required arguments are not found.
6374
*/
6475
const update = (params) => {
6576
if (params.qstring.args) {
@@ -72,7 +83,7 @@ const update = (params) => {
7283
common.returnMessage(params, 200, 'Success');
7384
});
7485
}
75-
if (params.qstring.event_order) {
86+
else if (params.qstring.event_order) {
7687
params.qstring.event_order = JSON.parse(params.qstring.event_order);
7788
var bulkArray = [];
7889
params.qstring.event_order.forEach(function(id, index) {
@@ -91,7 +102,7 @@ const update = (params) => {
91102
common.returnMessage(params, 200, 'Success');
92103
});
93104
}
94-
if (params.qstring.update_status) {
105+
else if (params.qstring.update_status) {
95106
params.qstring.update_status = JSON.parse(params.qstring.update_status);
96107
params.qstring.status = JSON.parse(params.qstring.status);
97108
var idss = params.qstring.update_status;
@@ -145,13 +156,21 @@ const update = (params) => {
145156
}
146157
);
147158
}
159+
else {
160+
common.returnMessage(params, 400, 'Error: args not found');
161+
return false;
162+
}
148163
};
149164

150165
/**
151166
* Event Groups CRUD - The function deleting which created `Event Groups` data by `_id`
152167
* @param {Object} params -
153168
*/
154169
const remove = async(params) => {
170+
if (!params.qstring.args) {
171+
common.returnMessage(params, 400, 'Error: args not found');
172+
return false;
173+
}
155174
params.qstring.args = JSON.parse(params.qstring.args);
156175
var idss = params.qstring.args;
157176
common.db.collection(COLLECTION_NAME).remove({_id: { $in: params.qstring.args }}, (error) =>{

bin/scripts/adjust_stats.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* Script to check statistics for Adjust data in Countly.
3+
*
4+
* This script checks:
5+
* 1. Number of documents in the adjust collection for the specified app_id
6+
* 2. Number of users in app_users{APP_ID} collection with custom.adjust_id
7+
* 3. Number of adjust_install events in the drill collection
8+
*
9+
* Location:
10+
* Place this script in the `bin/scripts` directory of your Countly installation.
11+
*
12+
* Usage:
13+
* 1. Replace the `APP_ID` variable with the desired app's ID.
14+
* 2. Run the script using Node.js:
15+
* ```
16+
* node /var/countly/bin/scripts/adjust_stats.js
17+
* ```
18+
*/
19+
20+
// Define the APP_ID variable
21+
const APP_ID = '5ab0c3ef92938d0e61cf77f4';
22+
23+
const plugins = require('../../plugins/pluginManager.js');
24+
25+
(async() => {
26+
console.log(`Checking Adjust statistics for APP_ID: ${APP_ID}`);
27+
28+
try {
29+
// Connect to countly database
30+
const db = await plugins.dbConnection("countly");
31+
32+
// Connect to countly_drill database
33+
const drillDb = await plugins.dbConnection("countly_drill");
34+
35+
console.log('Connected to databases successfully.');
36+
37+
// 1. Check how many documents are in adjust collection for this app_id
38+
console.log('\n--- Checking adjust collection ---');
39+
40+
// Define date range for filtering (July 17-22, 2025)
41+
const startDate = new Date('2025-07-17T00:00:00.000Z');
42+
const endDate = new Date('2025-07-22T23:59:59.999Z');
43+
console.log(`Date range filter: ${startDate.toISOString()} to ${endDate.toISOString()}`);
44+
45+
const adjustQuery = {
46+
app_id: APP_ID,
47+
cd: {
48+
$gte: startDate,
49+
$lte: endDate
50+
}
51+
};
52+
53+
const adjustCount = await db.collection('adjust').countDocuments(adjustQuery);
54+
console.log(`Documents in adjust collection for app_id ${APP_ID} (${startDate.toDateString()} - ${endDate.toDateString()}): ${adjustCount}`);
55+
56+
// 1a. Check unique amount of adjust_id values in adjust collection
57+
const uniqueAdjustIds = await db.collection('adjust').distinct('adjust_id', adjustQuery);
58+
console.log(`Unique adjust_id values in adjust collection for app_id ${APP_ID} (${startDate.toDateString()} - ${endDate.toDateString()}): ${uniqueAdjustIds.length}`);
59+
60+
// 1b. Breakdown by event property in adjust collection
61+
console.log('\n--- Event breakdown in adjust collection ---');
62+
const eventBreakdown = await db.collection('adjust').aggregate([
63+
{ $match: adjustQuery },
64+
{ $group: { _id: "$event", count: { $sum: 1 } } },
65+
{ $sort: { count: -1 } }
66+
]).toArray();
67+
68+
console.log('Event breakdown:');
69+
eventBreakdown.forEach(item => {
70+
console.log(` ${item._id}: ${item.count}`);
71+
});
72+
73+
// 2. Check how many users in app_users{APP_ID} collection have custom.adjust_id value
74+
console.log('\n--- Checking app_users collection ---');
75+
const appUsersCollection = 'app_users' + APP_ID;
76+
77+
// Use the same date range but convert to seconds for fac field
78+
const appUsersQuery = {
79+
'custom.adjust_id': { $exists: true },
80+
fac: {
81+
$gte: Math.floor(startDate.getTime() / 1000),
82+
$lte: Math.floor(endDate.getTime() / 1000)
83+
}
84+
};
85+
86+
const usersWithAdjustId = await db.collection(appUsersCollection).countDocuments(appUsersQuery);
87+
console.log(`Users with custom.adjust_id in ${appUsersCollection} (${startDate.toDateString()} - ${endDate.toDateString()}): ${usersWithAdjustId}`);
88+
89+
// 2a. Check unique custom.adjust_id values in app_users collection
90+
const uniqueUserAdjustIds = await db.collection(appUsersCollection).distinct('custom.adjust_id', appUsersQuery);
91+
console.log(`Unique custom.adjust_id values in ${appUsersCollection} (${startDate.toDateString()} - ${endDate.toDateString()}): ${uniqueUserAdjustIds.length}`);
92+
93+
// 3. Check how many adjust_install events are in drill collection
94+
console.log('\n--- Checking drill collection for adjust_install events ---');
95+
const drillCollectionName = 'drill_events';
96+
console.log(`Drill collection name: ${drillCollectionName}`);
97+
98+
// Use the same date range but convert to milliseconds for ts field
99+
const drillQuery = {
100+
"a": APP_ID,
101+
"e": "adjust_install",
102+
ts: {
103+
$gte: startDate.getTime(),
104+
$lte: endDate.getTime()
105+
}
106+
};
107+
108+
const adjustInstallEvents = await drillDb.collection(drillCollectionName).countDocuments(drillQuery);
109+
console.log(`adjust_install events in drill collection (${startDate.toDateString()} - ${endDate.toDateString()}): ${adjustInstallEvents}`);
110+
111+
// 3a. Check unique custom.adjust_id values in drill collection
112+
const uniqueDrillAdjustIds = await drillDb.collection(drillCollectionName).distinct('custom.adjust_id', drillQuery);
113+
console.log(`Unique custom.adjust_id values in drill collection (${startDate.toDateString()} - ${endDate.toDateString()}): ${uniqueDrillAdjustIds.length}`);
114+
115+
// Summary
116+
console.log('\n--- SUMMARY ---');
117+
console.log(`APP_ID: ${APP_ID}`);
118+
console.log(`Date range: ${startDate.toDateString()} - ${endDate.toDateString()}`);
119+
console.log(`Adjust collection documents: ${adjustCount}`);
120+
console.log(`Unique adjust_id values: ${uniqueAdjustIds.length}`);
121+
console.log(`Users with adjust_id: ${usersWithAdjustId}`);
122+
console.log(`Unique custom.adjust_id values in app_users: ${uniqueUserAdjustIds.length}`);
123+
console.log(`adjust_install events in drill collection: ${adjustInstallEvents}`);
124+
console.log(`Unique custom.adjust_id values in drill collection: ${uniqueDrillAdjustIds.length}`);
125+
126+
console.log('\nStatistics check completed.');
127+
128+
}
129+
catch (error) {
130+
console.error('Error during statistics check:', error);
131+
}
132+
finally {
133+
console.log('Terminating the process...');
134+
process.exit(0);
135+
}
136+
})();

bin/scripts/generate-api-docs.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* This script runs the API documentation generation process:
5+
* 1. Merge all OpenAPI specs into one file
6+
* 2. Generate Swagger UI HTML documentation
7+
*/
8+
9+
const { execFileSync } = require('child_process');
10+
const path = require('path');
11+
12+
console.log('🚀 Starting API documentation generation process...');
13+
14+
// Paths to the scripts
15+
const scriptsDir = __dirname;
16+
const mergeScript = path.join(scriptsDir, 'merge-openapi.js');
17+
const swaggerScript = path.join(scriptsDir, 'generate-swagger-ui.js');
18+
19+
try {
20+
// Step 1: Merge OpenAPI specs
21+
console.log('\n📑 Step 1: Merging OpenAPI specifications...');
22+
execFileSync('node', [mergeScript], { stdio: 'inherit' });
23+
24+
// Step 2: Generate Swagger UI documentation
25+
console.log('\n📙 Step 2: Generating Swagger UI documentation...');
26+
execFileSync('node', [swaggerScript], { stdio: 'inherit' });
27+
28+
console.log('\n✅ API documentation generation completed successfully!');
29+
console.log('📊 Documentation is available in the doc/api directory:');
30+
console.log(' - Swagger UI: doc/api/swagger-ui-api.html');
31+
32+
}
33+
catch (error) {
34+
console.error('\n❌ Error during API documentation generation:', error.message);
35+
process.exit(1);
36+
}

bin/scripts/generate-swagger-ui.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* This script generates HTML documentation using Swagger UI from the merged OpenAPI specification.
5+
*/
6+
7+
const fs = require('fs');
8+
const path = require('path');
9+
10+
// Configuration
11+
const outputDir = path.join(__dirname, '../../doc/api');
12+
const mergedSpecPath = path.join(outputDir, 'openapi-merged.json');
13+
const outputHtmlPath = path.join(outputDir, 'index.html');
14+
15+
// Ensure the merged spec exists
16+
if (!fs.existsSync(mergedSpecPath)) {
17+
console.error(`Merged OpenAPI spec not found at ${mergedSpecPath}`);
18+
console.error('Please run merge-openapi.js first');
19+
process.exit(1);
20+
}
21+
22+
console.log('Generating Swagger UI HTML documentation...');
23+
24+
// Create a simple HTML file with Swagger UI
25+
const swaggerUiHtml = `
26+
<!DOCTYPE html>
27+
<html lang="en">
28+
<head>
29+
<meta charset="UTF-8">
30+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
31+
<title>Countly API Documentation</title>
32+
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
33+
<link rel="icon" type="image/png" href="https://countly.com/images/favicon.png" sizes="32x32" />
34+
<style>
35+
body {
36+
margin: 0;
37+
padding: 0;
38+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
39+
}
40+
#swagger-ui {
41+
max-width: 1460px;
42+
margin: 0 auto;
43+
}
44+
.topbar {
45+
background-color: #0166D6 !important;
46+
padding: 10px 0;
47+
text-align: center;
48+
}
49+
.topbar-wrapper img {
50+
content: url('https://countly.com/images/logo.svg');
51+
height: 40px;
52+
}
53+
.swagger-ui .info .title {
54+
color: #0166D6;
55+
}
56+
.swagger-ui .opblock.opblock-get .opblock-summary {
57+
border-color: #0166D6;
58+
}
59+
.swagger-ui .opblock.opblock-get .opblock-summary-method {
60+
background: #0166D6;
61+
}
62+
.swagger-ui .btn.execute {
63+
background-color: #0166D6;
64+
color: #fff;
65+
border-color: #0166D6;
66+
}
67+
.swagger-ui .btn.authorize {
68+
color: #0166D6;
69+
border-color: #0166D6;
70+
}
71+
.swagger-ui .opblock.opblock-post {
72+
background: rgba(1, 102, 214, 0.05);
73+
border-color: #0166D6;
74+
}
75+
</style>
76+
</head>
77+
<body>
78+
<div id="swagger-ui"></div>
79+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
80+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script>
81+
<script>
82+
window.onload = function() {
83+
const ui = SwaggerUIBundle({
84+
url: "./openapi-merged.json",
85+
dom_id: '#swagger-ui',
86+
deepLinking: true,
87+
presets: [
88+
SwaggerUIBundle.presets.apis,
89+
SwaggerUIStandalonePreset
90+
],
91+
plugins: [
92+
SwaggerUIBundle.plugins.DownloadUrl
93+
],
94+
layout: "StandaloneLayout",
95+
tagsSorter: 'alpha',
96+
docExpansion: 'list',
97+
defaultModelsExpandDepth: 1,
98+
defaultModelExpandDepth: 1,
99+
filter: true,
100+
supportedSubmitMethods: ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'],
101+
validatorUrl: null
102+
});
103+
window.ui = ui;
104+
};
105+
</script>
106+
</body>
107+
</html>
108+
`;
109+
110+
try {
111+
fs.writeFileSync(outputHtmlPath, swaggerUiHtml);
112+
113+
// Also create a copy with the swagger-ui-api.html name for backward compatibility
114+
fs.writeFileSync(path.join(outputDir, 'swagger-ui-api.html'), swaggerUiHtml);
115+
116+
console.log(`Successfully generated Swagger UI documentation at ${outputHtmlPath}`);
117+
console.log(`Also created a copy at ${path.join(outputDir, 'swagger-ui-api.html')} for compatibility`);
118+
}
119+
catch (error) {
120+
console.error('Failed to generate Swagger UI documentation:', error.message);
121+
process.exit(1);
122+
}
123+
124+
console.log('Swagger UI documentation generation completed successfully!');

0 commit comments

Comments
 (0)