Skip to content

Commit

Permalink
Merge pull request #5455 from Countly/remocon-master
Browse files Browse the repository at this point in the history
Remote config cycle9
  • Loading branch information
ArtursKadikis authored Sep 23, 2024
2 parents 9a55c9e + a5fb5e0 commit 88095f7
Show file tree
Hide file tree
Showing 21 changed files with 1,869 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,9 @@
},
methods: {
loadValue: function(value) {
if (!value) {
return;
}
var changes = this.valueToInputState(value),
self = this;
changes.label = getRangeLabel(changes, this.type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@
template: '<cly-dropdown class="cly-vue-more-options" ref="dropdown" :placement="placement" :disabled="disabled" v-on="$listeners">\
<template v-slot:trigger>\
<slot name="trigger">\
<el-button :data-test-id="testId + \'-more-option-button\'" :size="size" :icon="icon" :type="type">\
<el-button :data-test-id="testId + \'-more-option-button\'" :size="size" :icon="icon" :type="type" :disabled="disabledButton">\
<span :data-test-id="testId + \'-more-option-text\'" v-if="text">{{text}}</span>\
</el-button>\
</slot>\
Expand Down Expand Up @@ -560,6 +560,10 @@
type: Boolean,
default: false
},
disabledButton: {
type: Boolean,
default: false,
},
placement: {
type: String,
default: 'bottom-end'
Expand Down
170 changes: 165 additions & 5 deletions plugins/remote-config/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,37 @@ plugins.setConfigs("remote-config", {
ob.features.push(FEATURE_NAME);
});

/**
* @api {get} /o/sdk?method=rc Get remote configs in sdk
* @apiName GetRemoteConfigInSdk
* @apiGroup Remote Config
* @apiPermission user
* @apiDescription Fetch all remote config in sdk
*
* @apiQuery {String} app_key APP_KEY of an app for which to fetch remote config
* @apiQuery {String} device_id Your generated or device specific unique device ID to identify user
* @apiQuery {String} [timestamp] 10 digit UTC timestamp for recording past data
* @apiQuery {String} [city] Name of the user's city
* @apiQuery {String} [country_code] ISO Country code for the user's country
* @apiQuery {String} [location] Users lat, lng
* @apiQuery {String} [tz] Users timezone
* @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
* @apiQuery {String[]} [keys] Only the values mentioned in the array will be fetched
* @apiQuery {String[]} [omit_keys] Only the values mentioned in the array will not be fetched
* @apiQuery {Object} [metrics] JSON object with key value pairs
* @apiQuery {Number} [oi] To indicate that user will be enrolled in the returned keys if eligible
*
* @apiSuccessExample {json} Success-Response:
* {
"default_colors": {
"button": "#f77a22",
"buttonColor": "#ffffff",
"titleColor": "#2eb52b"
},
"display_onboarding": true,
"image_alt": "The image cannot be loaded"
}
*/
plugins.register("/o/sdk", function(ob) {
var params = ob.params;
if (params.qstring.method !== "rc") {
Expand All @@ -27,6 +58,30 @@ plugins.setConfigs("remote-config", {

});

/**
* @api {get} /o/sdk?method=ab Enrolls in ab tests for mentioned keys if user is eligible
* @apiName EnrollUserInABTests
* @apiGroup Remote Config
* @apiPermission user
* @apiDescription Enrolls in ab tests for mentioned keys if user is eligible
*
* @apiQuery {String} app_key APP_KEY of an app for which to fetch remote config
* @apiQuery {String} device_id Your generated or device specific unique device ID to identify user
* @apiQuery {String} [timestamp] 10 digit UTC timestamp for recording past data
* @apiQuery {String} [city] Name of the user's city
* @apiQuery {String} [country_code] ISO Country code for the user's country
* @apiQuery {String} [location] Users lat, lng
* @apiQuery {String} [tz] Users timezone
* @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
* @apiQuery {String[]} [keys] Only the values mentioned in the array will be fetched
* @apiQuery {Object} [metrics] JSON object with key value pairs
*
* @apiSuccessExample {body} Success-Response:
* HTTP/1.1 200 OK
* {
* "Successfully enrolled in ab tests"
* }
*/
plugins.register("/o/sdk", function(ob) {
var params = ob.params;
if (params.qstring.method !== "ab") {
Expand Down Expand Up @@ -295,6 +350,27 @@ plugins.setConfigs("remote-config", {
* @apiQuery {String} app_id Application id
* @apiQuery {Object} parameter Parameter information
*
* @apiQueryExample {json} Request-Example:
* {
* "app_id": "5da8c68cb1ce0e2f34c4f3e6",
* "parameter": "{\"parameter_key\":\"new_feature_enabled\",\"default_value\":false,\"conditions\":[]}"
* }
*
* @apiSuccess {Number} result Result code (200 for success)
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {}
*
* @apiError {Number} result Result code (400 or 500 for error)
* @apiError {String} message Error message
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 400 Bad Request
* {
* "result": 400,
* "message": "Invalid parameter: parameter_key"
* }
*/
/**
* Function to add a parameter
Expand All @@ -311,6 +387,9 @@ plugins.setConfigs("remote-config", {
catch (SyntaxError) {
console.log('Parse parameter failed: ', params.qstring.parameter);
}
if (!parameter.status) {
parameter.status = "Running";
}

var parameterKey = parameter.parameter_key;
var defaultValue = parameter.default_value;
Expand Down Expand Up @@ -352,7 +431,7 @@ plugins.setConfigs("remote-config", {

async.series(asyncTasks, function(err) {
if (err) {
var message = 'Failed to add parameter';
var message = err?.message || 'Failed to add parameter';
if (err.exists) {
message = 'The parameter already exists';
}
Expand All @@ -368,6 +447,39 @@ plugins.setConfigs("remote-config", {
});
}

/**
* @api {post} /i/remote-config/add_complete_config
* @apiName AddCompleteRemoteConfig
* @apiGroup Remote Config
* @apiPermission user
* @apiDescription Add a complete remote configuration including parameters and conditions, it is used to publish the experiment results,
* In summmary replace the default value of a parameter in remote config with the winning variant from AB Test
*
* @apiParam {String} app_id Application ID
* @apiParam {String} config JSON string representing the complete config object
*
* @apiParamExample {json} Request-Example:
* {
* "app_id": "5da8c68cb1ce0e2f34c4f3e6",
* "config": "{\"parameters\":[{\"parameter_key\":\"feature_enabled\",\"exp_value\":true,\"description\":\"Enable new feature\"}],\"condition\":{\"condition_name\":\"New Users\",\"condition\":{\"user_properties.is_new\":true}}}"
* }
*
* @apiSuccess {Number} result Result code (200 for success)
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {}
*
* @apiError {Number} result Result code (400 or 500 for error)
* @apiError {String} message Error message
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 400 Bad Request
* {
* "result": 400,
* "message": "Invalid config"
* }
*/
/**
* Function to add the complete config including parameter and condition
* @param {Object} params - params object
Expand Down Expand Up @@ -716,7 +828,7 @@ plugins.setConfigs("remote-config", {
var deviceId = params.qstring.device_id || "";
user.random_percentile = remoteConfig.randomPercentile(seed, deviceId);

var conditionStatus = remoteConfig.processFilter(params, user, conditionObj.condition);
var conditionStatus = remoteConfig.processFilter(user, conditionObj.condition);

if (conditionStatus) {
parameterValue = conditionObj.value;
Expand Down Expand Up @@ -768,7 +880,29 @@ plugins.setConfigs("remote-config", {
* @apiQuery {String} app_id Application id
* @apiQuery {Object} parameter Parameter information
* @apiQuery {String} parameter_id Id of the parameter which is to be updated
*
* @apiQueryExample {json} Request-Example:
* {
* "app_id": "5da8c68cb1ce0e2f34c4f3e6",
* "parameter_id": "60a7c1234b1ce0e2f34c4f3e7",
* "parameter": "{\"parameter_key\":\"feature_enabled\",\"default_value\":true,\"conditions\":[]}"
* }
*
* @apiSuccess {Number} result Result code (200 for success)
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {}
*
* @apiError {Number} result Result code (400 or 500 for error)
* @apiError {String} message Error message
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 400 Bad Request
* {
* "result": 400,
* "message": "Invalid parameter: parameter_key"
* }
*/
/**
* Function to update parameter
Expand All @@ -785,18 +919,25 @@ plugins.setConfigs("remote-config", {
catch (SyntaxError) {
console.log('Parse parameter failed: ', params.qstring.parameter);
}
if (!parameter.status) {
parameter.status = "Running";
}

var parameterId = params.qstring.parameter_id;
var parameterKey = parameter.parameter_key;
var defaultValue = parameter.default_value;

//var conditionName = parameter.conditions;
var pattern = new RegExp(/^[a-zA-Z_][a-zA-Z0-9_]*$/);
if (!pattern.test(parameterKey)) {
common.returnMessage(params, 400, 'Invalid parameter: parameter_key');
return true;
}

if (!defaultValue && defaultValue !== false) {
// var conditionPattern = new RegExp(/^[a-zA-Z0-9 ]+$/);
// if (!conditionName || !conditionPattern.test(conditionName.trim())) {
// common.returnMessage(params, 400, 'Invalid parameter: condition_name');
// return true;
// }
if (defaultValue === undefined) {
common.returnMessage(params, 400, 'Invalid parameter: default_value');
return true;
}
Expand All @@ -808,9 +949,12 @@ plugins.setConfigs("remote-config", {

var collectionName = "remoteconfig_parameters" + appId;

//TODO:validations

var asyncTasks = [
checkMaximumConditionsLimit.bind(null, parameter.conditions, maximumConditionsAllowed),
checkIfParameterExists.bind(null, appId, parameterKey, parameterId),
//checkIfConditionExists.bind(null, appId, conditionName, null),
updateParameterInDb.bind(null, params, collectionName, parameterId, parameter)
];

Expand Down Expand Up @@ -1001,6 +1145,14 @@ plugins.setConfigs("remote-config", {
return true;
}

if (!condition.condition) {
if (params.internal) {
return 'Invalid parameter: condition';
}
common.returnMessage(params, 400, 'Invalid parameter: condition');
return true;
}

if (typeof condition.condition !== typeof '') {
condition.condition = JSON.stringify(condition.condition);
}
Expand Down Expand Up @@ -1177,6 +1329,14 @@ plugins.setConfigs("remote-config", {
return true;
}

if (!condition.condition) {
if (params.internal) {
return 'Invalid parameter: condition';
}
common.returnMessage(params, 400, 'Invalid parameter: condition');
return true;
}

condition.condition = JSON.stringify(condition.condition);

var asyncTasks = [
Expand Down
7 changes: 3 additions & 4 deletions plugins/remote-config/api/parts/rc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ var globalSeed = "Countly_is_awesome";
var remoteConfig = {};

/**
* Function to process condition filter
* @param {Object} params - params object
* Function to check if the given query would match the given user
* @param {Object} user - user
* @param {Object} query - query
* @returns {Boolean} query status
* @returns {Boolean} true if the query matches the user
*/
remoteConfig.processFilter = function(params, user, query) {
remoteConfig.processFilter = function(user, query) {
var queryStatus = false, isCohort = false, hasValue = false;

if (Object.keys(query).length) {
Expand Down
Loading

0 comments on commit 88095f7

Please sign in to comment.