Skip to content

Commit 88095f7

Browse files
Merge pull request #5455 from Countly/remocon-master
Remote config cycle9
2 parents 9a55c9e + a5fb5e0 commit 88095f7

File tree

21 files changed

+1869
-80
lines changed

21 files changed

+1869
-80
lines changed

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/remote-config/api/api.js

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,37 @@ plugins.setConfigs("remote-config", {
1818
ob.features.push(FEATURE_NAME);
1919
});
2020

21+
/**
22+
* @api {get} /o/sdk?method=rc Get remote configs in sdk
23+
* @apiName GetRemoteConfigInSdk
24+
* @apiGroup Remote Config
25+
* @apiPermission user
26+
* @apiDescription Fetch all remote config in sdk
27+
*
28+
* @apiQuery {String} app_key APP_KEY of an app for which to fetch remote config
29+
* @apiQuery {String} device_id Your generated or device specific unique device ID to identify user
30+
* @apiQuery {String} [timestamp] 10 digit UTC timestamp for recording past data
31+
* @apiQuery {String} [city] Name of the user's city
32+
* @apiQuery {String} [country_code] ISO Country code for the user's country
33+
* @apiQuery {String} [location] Users lat, lng
34+
* @apiQuery {String} [tz] Users timezone
35+
* @apiQuery {String} [ip_address] IP address of user to determine user location, if not provided, countly will try to establish ip address based on connection data
36+
* @apiQuery {String[]} [keys] Only the values mentioned in the array will be fetched
37+
* @apiQuery {String[]} [omit_keys] Only the values mentioned in the array will not be fetched
38+
* @apiQuery {Object} [metrics] JSON object with key value pairs
39+
* @apiQuery {Number} [oi] To indicate that user will be enrolled in the returned keys if eligible
40+
*
41+
* @apiSuccessExample {json} Success-Response:
42+
* {
43+
"default_colors": {
44+
"button": "#f77a22",
45+
"buttonColor": "#ffffff",
46+
"titleColor": "#2eb52b"
47+
},
48+
"display_onboarding": true,
49+
"image_alt": "The image cannot be loaded"
50+
}
51+
*/
2152
plugins.register("/o/sdk", function(ob) {
2253
var params = ob.params;
2354
if (params.qstring.method !== "rc") {
@@ -27,6 +58,30 @@ plugins.setConfigs("remote-config", {
2758

2859
});
2960

61+
/**
62+
* @api {get} /o/sdk?method=ab Enrolls in ab tests for mentioned keys if user is eligible
63+
* @apiName EnrollUserInABTests
64+
* @apiGroup Remote Config
65+
* @apiPermission user
66+
* @apiDescription Enrolls in ab tests for mentioned keys if user is eligible
67+
*
68+
* @apiQuery {String} app_key APP_KEY of an app for which to fetch remote config
69+
* @apiQuery {String} device_id Your generated or device specific unique device ID to identify user
70+
* @apiQuery {String} [timestamp] 10 digit UTC timestamp for recording past data
71+
* @apiQuery {String} [city] Name of the user's city
72+
* @apiQuery {String} [country_code] ISO Country code for the user's country
73+
* @apiQuery {String} [location] Users lat, lng
74+
* @apiQuery {String} [tz] Users timezone
75+
* @apiQuery {String} [ip_address] IP address of user to determine user location, if not provided, countly will try to establish ip address based on connection data
76+
* @apiQuery {String[]} [keys] Only the values mentioned in the array will be fetched
77+
* @apiQuery {Object} [metrics] JSON object with key value pairs
78+
*
79+
* @apiSuccessExample {body} Success-Response:
80+
* HTTP/1.1 200 OK
81+
* {
82+
* "Successfully enrolled in ab tests"
83+
* }
84+
*/
3085
plugins.register("/o/sdk", function(ob) {
3186
var params = ob.params;
3287
if (params.qstring.method !== "ab") {
@@ -295,6 +350,27 @@ plugins.setConfigs("remote-config", {
295350
* @apiQuery {String} app_id Application id
296351
* @apiQuery {Object} parameter Parameter information
297352
*
353+
* @apiQueryExample {json} Request-Example:
354+
* {
355+
* "app_id": "5da8c68cb1ce0e2f34c4f3e6",
356+
* "parameter": "{\"parameter_key\":\"new_feature_enabled\",\"default_value\":false,\"conditions\":[]}"
357+
* }
358+
*
359+
* @apiSuccess {Number} result Result code (200 for success)
360+
*
361+
* @apiSuccessExample {json} Success-Response:
362+
* HTTP/1.1 200 OK
363+
* {}
364+
*
365+
* @apiError {Number} result Result code (400 or 500 for error)
366+
* @apiError {String} message Error message
367+
*
368+
* @apiErrorExample {json} Error-Response:
369+
* HTTP/1.1 400 Bad Request
370+
* {
371+
* "result": 400,
372+
* "message": "Invalid parameter: parameter_key"
373+
* }
298374
*/
299375
/**
300376
* Function to add a parameter
@@ -311,6 +387,9 @@ plugins.setConfigs("remote-config", {
311387
catch (SyntaxError) {
312388
console.log('Parse parameter failed: ', params.qstring.parameter);
313389
}
390+
if (!parameter.status) {
391+
parameter.status = "Running";
392+
}
314393

315394
var parameterKey = parameter.parameter_key;
316395
var defaultValue = parameter.default_value;
@@ -352,7 +431,7 @@ plugins.setConfigs("remote-config", {
352431

353432
async.series(asyncTasks, function(err) {
354433
if (err) {
355-
var message = 'Failed to add parameter';
434+
var message = err?.message || 'Failed to add parameter';
356435
if (err.exists) {
357436
message = 'The parameter already exists';
358437
}
@@ -368,6 +447,39 @@ plugins.setConfigs("remote-config", {
368447
});
369448
}
370449

450+
/**
451+
* @api {post} /i/remote-config/add_complete_config
452+
* @apiName AddCompleteRemoteConfig
453+
* @apiGroup Remote Config
454+
* @apiPermission user
455+
* @apiDescription Add a complete remote configuration including parameters and conditions, it is used to publish the experiment results,
456+
* In summmary replace the default value of a parameter in remote config with the winning variant from AB Test
457+
*
458+
* @apiParam {String} app_id Application ID
459+
* @apiParam {String} config JSON string representing the complete config object
460+
*
461+
* @apiParamExample {json} Request-Example:
462+
* {
463+
* "app_id": "5da8c68cb1ce0e2f34c4f3e6",
464+
* "config": "{\"parameters\":[{\"parameter_key\":\"feature_enabled\",\"exp_value\":true,\"description\":\"Enable new feature\"}],\"condition\":{\"condition_name\":\"New Users\",\"condition\":{\"user_properties.is_new\":true}}}"
465+
* }
466+
*
467+
* @apiSuccess {Number} result Result code (200 for success)
468+
*
469+
* @apiSuccessExample {json} Success-Response:
470+
* HTTP/1.1 200 OK
471+
* {}
472+
*
473+
* @apiError {Number} result Result code (400 or 500 for error)
474+
* @apiError {String} message Error message
475+
*
476+
* @apiErrorExample {json} Error-Response:
477+
* HTTP/1.1 400 Bad Request
478+
* {
479+
* "result": 400,
480+
* "message": "Invalid config"
481+
* }
482+
*/
371483
/**
372484
* Function to add the complete config including parameter and condition
373485
* @param {Object} params - params object
@@ -716,7 +828,7 @@ plugins.setConfigs("remote-config", {
716828
var deviceId = params.qstring.device_id || "";
717829
user.random_percentile = remoteConfig.randomPercentile(seed, deviceId);
718830

719-
var conditionStatus = remoteConfig.processFilter(params, user, conditionObj.condition);
831+
var conditionStatus = remoteConfig.processFilter(user, conditionObj.condition);
720832

721833
if (conditionStatus) {
722834
parameterValue = conditionObj.value;
@@ -768,7 +880,29 @@ plugins.setConfigs("remote-config", {
768880
* @apiQuery {String} app_id Application id
769881
* @apiQuery {Object} parameter Parameter information
770882
* @apiQuery {String} parameter_id Id of the parameter which is to be updated
883+
*
884+
* @apiQueryExample {json} Request-Example:
885+
* {
886+
* "app_id": "5da8c68cb1ce0e2f34c4f3e6",
887+
* "parameter_id": "60a7c1234b1ce0e2f34c4f3e7",
888+
* "parameter": "{\"parameter_key\":\"feature_enabled\",\"default_value\":true,\"conditions\":[]}"
889+
* }
890+
*
891+
* @apiSuccess {Number} result Result code (200 for success)
892+
*
893+
* @apiSuccessExample {json} Success-Response:
894+
* HTTP/1.1 200 OK
895+
* {}
896+
*
897+
* @apiError {Number} result Result code (400 or 500 for error)
898+
* @apiError {String} message Error message
771899
*
900+
* @apiErrorExample {json} Error-Response:
901+
* HTTP/1.1 400 Bad Request
902+
* {
903+
* "result": 400,
904+
* "message": "Invalid parameter: parameter_key"
905+
* }
772906
*/
773907
/**
774908
* Function to update parameter
@@ -785,18 +919,25 @@ plugins.setConfigs("remote-config", {
785919
catch (SyntaxError) {
786920
console.log('Parse parameter failed: ', params.qstring.parameter);
787921
}
922+
if (!parameter.status) {
923+
parameter.status = "Running";
924+
}
788925

789926
var parameterId = params.qstring.parameter_id;
790927
var parameterKey = parameter.parameter_key;
791928
var defaultValue = parameter.default_value;
792-
929+
//var conditionName = parameter.conditions;
793930
var pattern = new RegExp(/^[a-zA-Z_][a-zA-Z0-9_]*$/);
794931
if (!pattern.test(parameterKey)) {
795932
common.returnMessage(params, 400, 'Invalid parameter: parameter_key');
796933
return true;
797934
}
798-
799-
if (!defaultValue && defaultValue !== false) {
935+
// var conditionPattern = new RegExp(/^[a-zA-Z0-9 ]+$/);
936+
// if (!conditionName || !conditionPattern.test(conditionName.trim())) {
937+
// common.returnMessage(params, 400, 'Invalid parameter: condition_name');
938+
// return true;
939+
// }
940+
if (defaultValue === undefined) {
800941
common.returnMessage(params, 400, 'Invalid parameter: default_value');
801942
return true;
802943
}
@@ -808,9 +949,12 @@ plugins.setConfigs("remote-config", {
808949

809950
var collectionName = "remoteconfig_parameters" + appId;
810951

952+
//TODO:validations
953+
811954
var asyncTasks = [
812955
checkMaximumConditionsLimit.bind(null, parameter.conditions, maximumConditionsAllowed),
813956
checkIfParameterExists.bind(null, appId, parameterKey, parameterId),
957+
//checkIfConditionExists.bind(null, appId, conditionName, null),
814958
updateParameterInDb.bind(null, params, collectionName, parameterId, parameter)
815959
];
816960

@@ -1001,6 +1145,14 @@ plugins.setConfigs("remote-config", {
10011145
return true;
10021146
}
10031147

1148+
if (!condition.condition) {
1149+
if (params.internal) {
1150+
return 'Invalid parameter: condition';
1151+
}
1152+
common.returnMessage(params, 400, 'Invalid parameter: condition');
1153+
return true;
1154+
}
1155+
10041156
if (typeof condition.condition !== typeof '') {
10051157
condition.condition = JSON.stringify(condition.condition);
10061158
}
@@ -1177,6 +1329,14 @@ plugins.setConfigs("remote-config", {
11771329
return true;
11781330
}
11791331

1332+
if (!condition.condition) {
1333+
if (params.internal) {
1334+
return 'Invalid parameter: condition';
1335+
}
1336+
common.returnMessage(params, 400, 'Invalid parameter: condition');
1337+
return true;
1338+
}
1339+
11801340
condition.condition = JSON.stringify(condition.condition);
11811341

11821342
var asyncTasks = [

plugins/remote-config/api/parts/rc.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@ var globalSeed = "Countly_is_awesome";
99
var remoteConfig = {};
1010

1111
/**
12-
* Function to process condition filter
13-
* @param {Object} params - params object
12+
* Function to check if the given query would match the given user
1413
* @param {Object} user - user
1514
* @param {Object} query - query
16-
* @returns {Boolean} query status
15+
* @returns {Boolean} true if the query matches the user
1716
*/
18-
remoteConfig.processFilter = function(params, user, query) {
17+
remoteConfig.processFilter = function(user, query) {
1918
var queryStatus = false, isCohort = false, hasValue = false;
2019

2120
if (Object.keys(query).length) {

0 commit comments

Comments
 (0)