Skip to content

Commit

Permalink
Rc api final (#287)
Browse files Browse the repository at this point in the history
* first draft

* removed a check

* un reachable remote conf

* final form

* latest version

* warning changed to legacy api usage

* Update CHANGELOG.md

* update

* pushed

* new cond

* erased

* expilicite

* comments

* B

* example

Co-authored-by: ArtursKadikis <kadikis.arturs@gmail.com>
  • Loading branch information
turtledreams and ArtursKadikis authored Sep 27, 2022
1 parent 030fdca commit 250e07e
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 22 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
- ! Minor breaking change ! If no domain whitelist is provided for the heatmaps the SDK will fallback to your server url
- Fixed a bug where heatmap files were susceptible to DOM XSS
- Users can now input their domain whitelist for heatmaps feature during init
- Implementing new Remote Config/AB testing API:
- Added an init time flag to enable/disable new remote config API (default: disabled)
- Added a new call to opt in users to the A/B testing for the given keys
- Added an init time flag to enable/disable automatically opting in users for A/B testing while fetching remote config (with new RC API)(default: enabled)

## 22.06.1
- Added SDK calls to report Feedback widgets manually
Expand Down
14 changes: 11 additions & 3 deletions examples/example_remote_config.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
app_key: "YOUR_APP_KEY",
url: "https://try.count.ly", //your server goes here
debug: true,
rc_automatic_optin_for_ab: true, // set it to false for not opting in users for AB testing while fetching the remote config (only with latest API)
use_explicit_rc_api: false, // set it to true to use the latest API
remote_config: function (err, configs) {
//handle initial remote configs here
console.log(err, configs);
Expand All @@ -31,19 +33,25 @@ <h1>Remote Config</h1>
<center>
<img src="./images/team_countly.jpg" id="wallpaper" />
<br />
<input type="button" onclick="reloadConfig()" value="Reload Config">
<input type="button" onclick="fetchConfig()" value="Fetch Config">
<input type="button" onclick="getConfig()" value="Get Config">
<input type="button" onclick="getConfig('test')" value="Get config for key Test">
<input type="button" onclick="ab(['key1','key2'])" value="Enroll user to AB test">
<p><a href='http://count.ly/'>Count.ly</a></p>
</center>
<script type='text/javascript'>
//reload configs
function reloadConfig(ob) {
// fetches all keys
function fetchConfig() {
Countly.fetch_remote_config(function (err, config) {
alert(JSON.stringify(config));
});
}

// enroll user
function ab(keyArray) {
Countly.enrollUserToAb(keyArray);
}

//get config
function getConfig(key) {
alert(JSON.stringify(Countly.get_remote_config(key)));
Expand Down
64 changes: 45 additions & 19 deletions lib/countly.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@
this.onload = getConfig("onload", ob, []);
this.utm = getConfig("utm", ob, { source: true, medium: true, campaign: true, term: true, content: true });
this.ignore_prefetch = getConfig("ignore_prefetch", ob, true);
this.rcAutoOptinAb = getConfig("rc_automatic_optin_for_ab", ob, true);
this.useExplicitRcApi = getConfig("use_explicit_rc_api", ob, false);
this.debug = getConfig("debug", ob, false);
this.test_mode = getConfig("test_mode", ob, false);
this.metrics = getConfig("metrics", ob, {});
Expand Down Expand Up @@ -292,6 +294,11 @@
lsSupport = false;
}

if (!this.rcAutoOptinAb && !this.useExplicitRcApi) {
log(logLevelEnums.WARNING, "initialize, Auto opting is disabled, switching to explicit RC API");
this.useExplicitRcApi = true;
}

if (!Array.isArray(ignoreReferrers)) {
ignoreReferrers = [];
}
Expand Down Expand Up @@ -426,6 +433,12 @@
if (this.remote_config) {
log(logLevelEnums.DEBUG, "initialize, remote_config callback provided:[" + !!this.remote_config + "]");
}
if (typeof this.rcAutoOptinAb === "boolean") {
log(logLevelEnums.DEBUG, "initialize, automatic RC optin is enabled:[" + this.rcAutoOptinAb + "]");
}
if (!this.useExplicitRcApi) {
log(logLevelEnums.WARNING, "initialize, will use legacy RC API. Consider enabling new API during init with use_explicit_rc_api flag");
}
if (this.track_domains) {
log(logLevelEnums.DEBUG, "initialize, tracking domain info:[" + this.track_domains + "]");
}
Expand Down Expand Up @@ -693,6 +706,8 @@
self.ip_address = undefined;
self.ignore_bots = undefined;
self.force_post = undefined;
self.rcAutoOptinAb = undefined;
self.useExplicitRcApi = undefined;
self.remote_config = undefined;
self.ignore_visitor = undefined;
self.require_consent = undefined;
Expand Down Expand Up @@ -1609,8 +1624,17 @@
* @param {function=} callback - Callback to notify with first param error and second param remote config object
* */
this.fetch_remote_config = function(keys, omit_keys, callback) {
log(logLevelEnums.INFO, "fetch_remote_config, Fetching remote config");
fetch_remote_config_v2(keys, omit_keys, 1, "legacy", callback);
// use new RC API
if (this.useExplicitRcApi) {
log(logLevelEnums.INFO, "fetch_remote_config, Fetching remote config");
// opt in is true(1) or false(0)
var opt = this.rcAutoOptinAb ? 1 : 0;
fetch_remote_config_explicit(keys, omit_keys, opt, callback);
return;
}

log(logLevelEnums.WARNING, "fetch_remote_config, Fetching remote config, with legacy API");
fetch_remote_config_explicit(keys, omit_keys, "legacy", callback);
};

/**
Expand All @@ -1621,8 +1645,8 @@
* @param {string=} api - which API to use, if not provided would use default ("legacy" is for method="fetch_remote_config", default is method="rc")
* @param {function=} callback - Callback to notify with first param error and second param remote config object
* */
function fetch_remote_config_v2(keys, omit_keys, optIn, api, callback) {
log(logLevelEnums.INFO, "fetch_remote_config_v2, Fetching remote config");
function fetch_remote_config_explicit(keys, omit_keys, optIn, api, callback) {
log(logLevelEnums.INFO, "fetch_remote_config_explicit, Fetching sequence initiated");
var request = {
method: "rc"
};
Expand Down Expand Up @@ -1653,14 +1677,14 @@
provivedCall = arguments[j];
}
}
if (this.check_consent(featureEnums.SESSIONS)) {
if (self.check_consent(featureEnums.SESSIONS)) {
request.metrics = JSON.stringify(getMetrics());
}
if (this.check_consent(featureEnums.REMOTE_CONFIG)) {
if (self.check_consent(featureEnums.REMOTE_CONFIG)) {
prepareRequest(request);
sendXmlHttpRequest("fetch_remote_config_v2", this.url + readPath, request, function(err, params, responseText) {
sendXmlHttpRequest("fetch_remote_config_explicit", self.url + readPath, request, function(err, params, responseText) {
if (err) {
log(logLevelEnums.ERROR, "fetch_remote_config_v2, An error occurred: " + err);
log(logLevelEnums.ERROR, "fetch_remote_config_explicit, An error occurred: " + err);
return;
}
try {
Expand All @@ -1678,17 +1702,17 @@
setValueInStorage("cly_remote_configs", remoteConfigs);
}
catch (ex) {
log(logLevelEnums.ERROR, "fetch_remote_config_v2, Had an issue while parsing the response: " + ex);
log(logLevelEnums.ERROR, "fetch_remote_config_explicit, Had an issue while parsing the response: " + ex);
}
if (provivedCall) {
log(logLevelEnums.INFO, "fetch_remote_config_v2, Callback function is provided");
log(logLevelEnums.INFO, "fetch_remote_config_explicit, Callback function is provided");
provivedCall(err, remoteConfigs);
}
// JSON array can pass
}, true);
}
else {
log(logLevelEnums.ERROR, "fetch_remote_config_v2, Remote config requires explicit consent");
log(logLevelEnums.ERROR, "fetch_remote_config_explicit, Remote config requires explicit consent");
if (provivedCall) {
provivedCall(new Error("Remote config requires explicit consent"), remoteConfigs);
}
Expand All @@ -1699,32 +1723,32 @@
* AB testing key provider, opts the user in for the selected keys
* @param {array=} keys - Array of keys opt in FOR
* */
function optAB(keys) {
log(logLevelEnums.INFO, "optAB, Providing AB test keys to opt in for");
this.enrollUserToAb = function(keys) {
log(logLevelEnums.INFO, "enrollUserToAb, Providing AB test keys to opt in for");
if (!keys || !Array.isArray(keys) || keys.length === 0) {
log(logLevelEnums.ERROR, "optAB, No keys provided");
log(logLevelEnums.ERROR, "enrollUserToAb, No keys provided");
return;
}
var request = {
method: "ab",
keys: JSON.stringify(keys)
};
prepareRequest(request);
sendXmlHttpRequest("optAB", this.url + readPath, request, function(err, params, responseText) {
sendXmlHttpRequest("enrollUserToAb", this.url + readPath, request, function(err, params, responseText) {
if (err) {
log(logLevelEnums.ERROR, "optAB, An error occurred: " + err);
log(logLevelEnums.ERROR, "enrollUserToAb, An error occurred: " + err);
return;
}
try {
var resp = JSON.parse(responseText);
log(logLevelEnums.DEBUG, "optAB, Parsed the response's result: [" + resp.result + "]");
log(logLevelEnums.DEBUG, "enrollUserToAb, Parsed the response's result: [" + resp.result + "]");
}
catch (ex) {
log(logLevelEnums.ERROR, "optAB, Had an issue while parsing the response: " + ex);
log(logLevelEnums.ERROR, "enrollUserToAb, Had an issue while parsing the response: " + ex);
}
// JSON array can pass
}, true);
}
};

/**
* Gets remote config object (all key/value pairs) or specific value for provided key from the storage
Expand Down Expand Up @@ -4352,6 +4376,8 @@
* @param {number} [conf.max_stack_trace_line_length=200] - maximum amount of characters are allowed per stack trace line. This limits also the crash message length
* @param {array=} conf.ignore_referrers - array with referrers to ignore
* @param {boolean} [conf.ignore_prefetch=true] - ignore prefetching and pre rendering from counting as real website visits
* @param {boolean} [conf.rc_automatic_optin_for_ab=true] - opts in the user for A/B testing while fetching the remote config (if true)
* @param {boolean} [conf.use_explicit_rc_api=false] - set it to true to use the new remote config API
* @param {boolean} [conf.force_post=false] - force using post method for all requests
* @param {boolean} [conf.ignore_visitor=false] - ignore this current visitor
* @param {boolean} [conf.require_consent=false] - Pass true if you are implementing GDPR compatible consent management. It would prevent running any functionality without proper consent
Expand Down

0 comments on commit 250e07e

Please sign in to comment.