Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Commit

Permalink
Merge pull request #7 from launchdarkly/ag/ch4534/emit-errors-if-safe
Browse files Browse the repository at this point in the history
[ch4534] Emit errors if there is an error listener, log otherwise
  • Loading branch information
apucacao authored Aug 1, 2017
2 parents b501fbc + 67f6d3f commit fba5346
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 34 deletions.
16 changes: 16 additions & 0 deletions errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function createCustomError(name) {
function CustomError(message) {
Error.captureStackTrace && Error.captureStackTrace(this, this.constructor);
this.message = message;
}

CustomError.prototype = new Error();
CustomError.prototype.name = name;
CustomError.prototype.constructor = CustomError;

return CustomError;
}

exports.LDPollingError = createCustomError('LaunchDarklyPollingError');
exports.LDStreamingError = createCustomError('LaunchDarklyStreamingError');
exports.LDClientError = createCustomError('LaunchDarklyClientError');
6 changes: 6 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
declare module "ldclient-node" {
import { EventEmitter } from 'events';

namespace errors {
export const LDPollingError: ErrorConstructor;
export const LDStreamingError: ErrorConstructor;
export const LDClientError: ErrorConstructor;
}

/**
* The LaunchDarkly static global.
*/
Expand Down
48 changes: 34 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,27 @@ var tunnel = require('tunnel');
var winston = require('winston');
var crypto = require('crypto');
var async = require('async');
var errors = require('./errors');
var package_json = require('./package.json');

var noop = function(){};

global.setImmediate = global.setImmediate || process.nextTick.bind(process);

function createErrorReporter(emitter, logger) {
return function(error) {
if (!error) {
return;
}

if (emitter.listenerCount('error')) {
emitter.emit('error', error);
} else {
logger.error('[LaunchDarkly]', error);
}
};
}

var new_client = function(sdk_key, config) {
var client = new EventEmitter(),
init_complete = false,
Expand Down Expand Up @@ -48,6 +63,8 @@ var new_client = function(sdk_key, config) {
);
config.feature_store = config.feature_store || InMemoryFeatureStore();

var maybeReportError = createErrorReporter(client, config.logger);

if (!sdk_key && !config.offline) {
throw new Error("You must configure the client with an SDK key");
}
Expand All @@ -67,15 +84,12 @@ var new_client = function(sdk_key, config) {
var error;
if ((err.status && err.status === 401) || (err.code && err.code === 401)) {
error = new Error("Authentication failed. Double check your SDK key.");
} else if (err.message) {
error = "Error: " + err.message;
} else {
error = new Error("Unexpected error:", err);
error = err;
}

config.logger.error("[LaunchDarkly]", error);
}
else if (!init_complete) {
maybeReportError(error);
} else if (!init_complete) {
init_complete = true;
client.emit('ready');
}
Expand All @@ -94,6 +108,7 @@ var new_client = function(sdk_key, config) {
client.variation = function(key, user, default_val, fn) {
sanitize_user(user);
var cb = fn || noop;
var variationErr;

if (this.is_offline()) {
config.logger.info("[LaunchDarkly] variation called in offline mode. Returning default value.");
Expand All @@ -102,16 +117,18 @@ var new_client = function(sdk_key, config) {
}

else if (!key) {
config.logger.error("[LaunchDarkly] No feature flag key specified. Returning default value.");
variationErr = new errors.LDClientError('No feature flag key specified. Returning default value.');
maybeReportError(variationError);
send_flag_event(key, user, default_val, default_val);
cb(new Error("[LaunchDarkly] No flag key specified in variation call"), default_val);
cb(variationErr, default_val);
return;
}

else if (!user) {
config.logger.error("[LaunchDarkly] No user specified. Returning default value.");
variationErr = new errors.LDClientError('No user specified. Returning default value.');
maybeReportError(variationErr);
send_flag_event(key, user, default_val, default_val);
cb(new Error("[LaunchDarkly] No user specified in variation call"), default_val);
cb(variationErr, default_val);
return;
}

Expand All @@ -120,18 +137,20 @@ var new_client = function(sdk_key, config) {
}

if (!init_complete) {
config.logger.error("[LaunchDarkly] client has not finished initializing. Returning default value.");
variationErr = new errors.LDClientError("Variation called before LaunchDarkly client initialization completed (did you wait for the 'ready' event?)");
maybeReportError(variationErr);
send_flag_event(key, user, default_val, default_val);
cb(new Error("[LaunchDarkly] variation called before LaunchDarkly client initialization completed (did you wait for the 'ready' event?)"), default_val);
cb(variationErr, default_val);
return;
}

config.feature_store.get(key, function(flag) {
evaluate.evaluate(flag, user, config.feature_store, function(err, result, events) {
var i;
var version = flag ? flag.version : null;

if (err) {
config.logger.error("[LaunchDarkly] Encountered error evaluating feature flag", err)
maybeReportError(new errors.LDClientError('Encountered error evaluating feature flag:' + (err.message ? (': ' + err.message) : err)));
}

// Send off any events associated with evaluating prerequisites. The events
Expand Down Expand Up @@ -284,7 +303,8 @@ var new_client = function(sdk_key, config) {

module.exports = {
init: new_client,
RedisFeatureStore: RedisFeatureStore
RedisFeatureStore: RedisFeatureStore,
errors: errors
};


Expand Down
11 changes: 5 additions & 6 deletions polling.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
var noop = function(){};
var errors = require('./errors');

function PollingProcessor(config, requestor) {
var processor = {},
Expand All @@ -8,7 +8,7 @@ function PollingProcessor(config, requestor) {
function poll(cb) {
var start_time, delta;

cb = cb || noop;
cb = cb || function(){};

if (stopped) {
return;
Expand All @@ -21,15 +21,14 @@ function PollingProcessor(config, requestor) {
sleepFor = Math.max(config.poll_interval * 1000 - elapsed, 0);
config.logger.debug("Elapsed: %d ms, sleeping for %d ms", elapsed, sleepFor);
if (err) {
config.logger.error("[LaunchDarkly] Error polling for all feature flags", err);
cb(err);
cb(new errors.LDPollingError('Failed to fetch all feature flags: ' + err.message));
// Recursively call poll after the appropriate delay
setTimeout(poll, sleepFor);
setTimeout(function() { poll(cb); }, sleepFor);
} else {
store.init(flags, function() {
cb();
// Recursively call poll after the appropriate delay
setTimeout(poll, sleepFor);
setTimeout(function() { poll(cb); }, sleepFor);
});
}
});
Expand Down
28 changes: 14 additions & 14 deletions streaming.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
var EventSource = require('./eventsource');
var errors = require('./errors');

var noop = function(){};
var EventSource = require('./eventsource');

function StreamProcessor(sdk_key, config, requestor) {
var processor = {},
store = config.feature_store,
es;

processor.start = function(fn) {
var cb = fn || noop;
var cb = fn || function(){};
es = new EventSource(config.stream_uri + "/flags",
{
agent: config.proxy_agent,
headers: {'Authorization': sdk_key,'User-Agent': config.user_agent}
});

es.onerror = function(err) {
cb(err);
cb(new errors.LDStreamingError(err.message));
};

es.addEventListener('put', function(e) {
config.logger.debug("[LaunchDarkly] Received put event")
config.logger.debug('[LaunchDarkly] Received put event');
if (e && e.data) {
var flags = JSON.parse(e.data);
store.init(flags, function() {
cb();
})
} else {
cb(new Error("[LaunchDarkly] Unexpected payload from event stream"));
cb(new errors.LDStreamingError('[LaunchDarkly] Unexpected payload from event stream'));
}
});

es.addEventListener('patch', function(e) {
config.logger.debug("[LaunchDarkly] Received patch event")
config.logger.debug('[LaunchDarkly] Received patch event');
if (e && e.data) {
var patch = JSON.parse(e.data);
store.upsert(patch.data.key, patch.data);
} else {
config.logger.error("[LaunchDarkly] Unexpected payload from event stream")
cb(new errors.LDStreamingError('[LaunchDarkly] Unexpected payload from event stream'));
}
});

es.addEventListener('delete', function(e) {
config.logger.debug("[LaunchDarkly] Received delete event")
config.logger.debug('[LaunchDarkly] Received delete event');
if (e && e.data) {
var data = JSON.parse(e.data),
key = data.path.charAt(0) === '/' ? data.path.substring(1) : data.path, // trim leading '/'
version = data.version;

store.delete(key, version);
} else {
config.logger.error("[LaunchDarkly] Unexpected payload from event stream");
cb(new errors.LDStreamingError('[LaunchDarkly] Unexpected payload from event stream'));
}
});

es.addEventListener('indirect/put', function(e) {
config.logger.debug("[LaunchDarkly] Received indirect put event")
config.logger.debug('[LaunchDarkly] Received indirect put event')
requestor.request_all_flags(function (err, flags) {
if (err) {
cb(err);
Expand All @@ -68,18 +68,18 @@ function StreamProcessor(sdk_key, config, requestor) {
});

es.addEventListener('indirect/patch', function(e) {
config.logger.debug("[LaunchDarkly] Received indirect patch event")
config.logger.debug('[LaunchDarkly] Received indirect patch event')
if (e && e.data) {
var key = e.data.charAt(0) === '/' ? e.data.substring(1) : e.data;
requestor.request_flag(key, function(err, flag) {
if (err) {
config.logger.error("[LaunchDarkly] Unexpected error requesting feature flag");
cb(new errors.LDStreamingError('[LaunchDarkly] Unexpected error requesting feature flag'));
} else {
store.upsert(key, flag);
}
})
} else {
config.logger.error("[LaunchDarkly] Unexpected payload from event stream");
cb(new errors.LDStreamingError('[LaunchDarkly] Unexpected payload from event stream'));
}
});
}
Expand Down

0 comments on commit fba5346

Please sign in to comment.