Skip to content

Commit

Permalink
Remote Config support
Browse files Browse the repository at this point in the history
  • Loading branch information
ar2rsawseen committed Feb 14, 2019
1 parent 09d4a5a commit 30cbcbb
Showing 1 changed file with 124 additions and 38 deletions.
162 changes: 124 additions & 38 deletions lib/countly.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ Countly.Bulk = Bulk;
sessionStarted = false,
platform,
apiPath = "/i",
readPath = "/o/sdk",
beatInterval = 500,
queueSize = 1000,
requestQueue = [],
eventQueue = [],
remoteConfigs = {},
crashLogs = [],
maxCrashLogs = 100,
timedEvents = {},
Expand Down Expand Up @@ -98,6 +100,7 @@ Countly.Bulk = Bulk;
* @param {boolean} [conf.force_post=false] - force using post method for all requests
* @param {string} [conf.storage_path="../data/"] - where SDK would store data, including id, queues, etc
* @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
* @param {boolean|function} [conf.remote_config=false] - Enable automatic remote config fetching, provide callback function to be notified when fetching done
* @param {number} [conf.max_logs=100] - maximum amount of breadcrumbs to store for crash logs
* @param {Object} conf.metrics - provide {@link Metrics} for this user/device, or else will try to collect what's possible
* @param {string} conf.metrics._os - name of platform/operating system
Expand Down Expand Up @@ -144,7 +147,8 @@ Countly.Bulk = Bulk;
Countly.force_post = ob.force_post || Countly.force_post || false;
Countly.storage_path = ob.storage_path || Countly.storage_path || "../data/";
Countly.require_consent = ob.require_consent || Countly.require_consent || false;

Countly.remote_config = ob.rremote_config || Countly.remote_config || false;

var dir = path.resolve(__dirname, Countly.storage_path);
try{
if (!fs.existsSync(dir)){
Expand All @@ -165,6 +169,7 @@ Countly.Bulk = Bulk;
storeSet("cly_id", Countly.device_id);
requestQueue = storeGet("cly_queue", []);
eventQueue = storeGet("cly_event", []);
remoteConfigs = storeGet("cly_remote_configs", {});
heartBeat();
//listen to current workers
if(cluster.workers){
Expand All @@ -176,6 +181,9 @@ Countly.Bulk = Bulk;
cluster.on("fork", function(worker) {
worker.on("message", handleWorkerMessage);
});
if (Countly.remote_config) {
Countly.fetch_remote_config(Countly.remote_config);
}
}
}
}
Expand Down Expand Up @@ -437,6 +445,13 @@ Countly.Bulk = Bulk;
//start new session for new id
Countly.begin_session(!autoExtend);
}
if (Countly.remote_config) {
remoteConfigs = {};
if (cluster.isMaster) {
storeSet("cly_remote_configs", remoteConfigs);
}
Countly.fetch_remote_config(Countly.remote_config);
}
}
}
else{
Expand Down Expand Up @@ -749,6 +764,78 @@ Countly.Bulk = Bulk;
}
};

/**
* Fetch remote config
* @param {array=} keys - Array of keys to fetch, if not provided will fetch all keys
* @param {array=} omit_keys - Array of keys to omit, if provided will fetch all keys except provided ones
* @param {function=} callback - Callback to notify with first param error and second param remote config object
**/
Countly.fetch_remote_config = function (keys, omit_keys, callback) {
if (Countly.check_any_consent()) {
var request = {
method: "fetch_remote_config"
};
if (Countly.check_consent("sessions")) {
request.metrics = JSON.stringify(getMetrics());
}
if (keys) {
if (!callback && typeof keys === "function") {
callback = keys;
keys = null;
}
else if (Array.isArray(keys) && keys.length) {
request.keys = JSON.stringify(keys);
}
}
if (omit_keys) {
if (!callback && typeof omit_keys === "function") {
callback = omit_keys;
omit_keys = null;
}
else if (Array.isArray(omit_keys) && omit_keys.length) {
request.omit_keys = JSON.stringify(omit_keys);
}
}
prepareRequest(request);
makeRequest(Countly.url, readPath, request, function(err, params, responseText){
try {
var configs = JSON.parse(responseText);
if (request.keys || request.omit_keys) {
//we merge config
for (var i in configs) {
remoteConfigs[i] = configs[i];
}
}
else {
//we replace config
remoteConfigs = configs;
}
if (cluster.isMaster) {
storeSet("cly_remote_configs", remoteConfigs);
}
}
catch(ex) {
//silent catch
}
if (typeof callback === "function") {
callback(err, remoteConfigs);
}
});
}
};

/**
* Get Remote config object or specific value for provided key
* @param {string=} key - if provided, will return value for key, or return whole object
**/
Countly.get_remote_config = function (key) {
if (typeof key !== "undefined") {
return remoteConfigs[key];
}
return remoteConfigs;
};


/**
* Stop tracking duration time for this user/device
**/
Expand Down Expand Up @@ -852,36 +939,41 @@ Countly.Bulk = Bulk;
}
}

//prepare request by adding basic info to it
function prepareRequest(request) {
request.app_key = Countly.app_key;
request.device_id = Countly.device_id;
request.sdk_name = SDK_NAME;
request.sdk_version = SDK_VERSION;
if(Countly.check_consent("location")){
if(Countly.country_code){
request.country_code = Countly.country_code;
}
if(Countly.city){
request.city = Countly.city;
}
if(Countly.ip_address !== null){
request.ip_address = Countly.ip_address;
}
}
else{
request.location = "";
}

request.timestamp = getMsTimestamp();
var date = new Date();
request.hour = date.getHours();
request.dow = date.getDay();
}

//insert request to queue
function toRequestQueue(request){
if(cluster.isMaster){
if(!Countly.app_key || !Countly.device_id){
log("app_key or device_id is missing");
return;
}
request.app_key = Countly.app_key;
request.device_id = Countly.device_id;
request.sdk_name = SDK_NAME;
request.sdk_version = SDK_VERSION;
if(Countly.check_consent("location")){
if(Countly.country_code){
request.country_code = Countly.country_code;
}
if(Countly.city){
request.city = Countly.city;
}
if(Countly.ip_address !== null){
request.ip_address = Countly.ip_address;
}
}
else{
request.location = "";
}

request.timestamp = getMsTimestamp();
var date = new Date();
request.hour = date.getHours();
request.dow = date.getDay();
prepareRequest(request);

if(requestQueue.length > queueSize){
requestQueue.shift();
Expand Down Expand Up @@ -934,7 +1026,7 @@ Countly.Bulk = Bulk;
syncConsents = {};
}
log("Processing request", params);
makeRequest(params, function(err, params){
makeRequest(Countly.url, apiPath, params, function(err, params){
log("Request Finished", params, err);
if(err){
requestQueue.unshift(params);
Expand Down Expand Up @@ -1056,16 +1148,16 @@ Countly.Bulk = Bulk;
}

//sending HTTP request
function makeRequest(params, callback) {
function makeRequest(url, path, params, callback) {
try {
log("Sending HTTP request");
var serverOptions = parseUrl(Countly.url);
var serverOptions = parseUrl(url);
var data = prepareParams(params);
var method = "GET";
var options = {
host: serverOptions.host,
port: serverOptions.port,
path: apiPath+"?"+data,
path: path+"?"+data,
method: "GET"
};

Expand All @@ -1078,14 +1170,14 @@ Countly.Bulk = Bulk;

if(method === "POST"){
options.method = "POST";
options.path = apiPath;
options.path = path;
options.headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(data)
};
}
var protocol = http;
if(Countly.url.indexOf("https") === 0){
if(url.indexOf("https") === 0){
protocol = https;
}
var req = protocol.request(options, function(res) {
Expand All @@ -1095,14 +1187,8 @@ Countly.Bulk = Bulk;
});

res.on("end", function () {
try{
str = JSON.parse(str);
}
catch(ex){
str = {};
}
if(res.statusCode >= 200 && res.statusCode < 300 && str.result === "Success"){
callback(false, params);
if(res.statusCode >= 200 && res.statusCode < 300){
callback(false, params, str);
}
else{
callback(true, params);
Expand Down

0 comments on commit 30cbcbb

Please sign in to comment.