Commit 6984f332 authored by Sean Roberts's avatar Sean Roberts

Enable feature flagging in the annotation layer

parent 46ea0979
'use strict';
const events = require('../shared/bridge-events');
let _features = {};
const _set = (features) => {
_features = features || {};
};
module.exports = {
init: function(crossframe) {
crossframe.on(events.FEATURE_FLAGS_UPDATED, _set);
},
reset: function() {
_set({});
},
flagEnabled: function(flag) {
if (!(flag in _features)) {
console.warn('looked up unknown feature', flag);
return false;
}
return _features[flag];
},
};
...@@ -6,6 +6,7 @@ Host = require('./host') ...@@ -6,6 +6,7 @@ Host = require('./host')
annotationCounts = require('./annotation-counts') annotationCounts = require('./annotation-counts')
sidebarTrigger = require('./sidebar-trigger') sidebarTrigger = require('./sidebar-trigger')
events = require('../shared/bridge-events'); events = require('../shared/bridge-events');
features = require('./features');
# Minimum width to which the frame can be resized. # Minimum width to which the frame can be resized.
MIN_RESIZE = 280 MIN_RESIZE = 280
...@@ -89,7 +90,6 @@ module.exports = class Sidebar extends Host ...@@ -89,7 +90,6 @@ module.exports = class Sidebar extends Host
if @onHelpRequest if @onHelpRequest
@onHelpRequest() @onHelpRequest()
); );
# Return this for chaining # Return this for chaining
this this
......
'use strict';
var events = require('../../shared/bridge-events');
var features = require('../features');
describe('features - annotation layer', function () {
var featureFlagsUpdateHandler;
var initialFeatures = {
feature_on: true,
feature_off: false,
};
var setFeatures = function(features){
featureFlagsUpdateHandler(features || initialFeatures);
};
beforeEach(function () {
sinon.stub(console, 'warn');
features.init({
on: function(topic, handler){
if(topic === events.FEATURE_FLAGS_UPDATED){
featureFlagsUpdateHandler = handler;
}
},
});
// set default features
setFeatures();
});
afterEach(function () {
console.warn.restore();
features.reset();
});
describe('flagEnabled', function () {
it('should retrieve features data', function () {
assert.equal(features.flagEnabled('feature_on'), true);
assert.equal(features.flagEnabled('feature_off'), false);
});
it('should return false if features have not been loaded', function () {
// simulate feature data not having been loaded yet
features.reset();
assert.equal(features.flagEnabled('feature_on'), false);
});
it('should return false for unknown flags', function () {
assert.isFalse(features.flagEnabled('unknown_feature'));
});
it('should warn when accessing unknown flags', function () {
assert.notCalled(console.warn);
assert.isFalse(features.flagEnabled('unknown_feature'));
assert.calledOnce(console.warn);
assert.calledWith(console.warn, 'looked up unknown feature');
});
});
});
...@@ -8,8 +8,15 @@ module.exports = { ...@@ -8,8 +8,15 @@ module.exports = {
// Events that the sidebar sends to the annotator // Events that the sidebar sends to the annotator
// ---------------------------------------------- // ----------------------------------------------
/** The set of annotations was updated. */ /**
PUBLIC_ANNOTATION_COUNT_CHANGED: 'publicAnnotationCountChanged', * The updated feature flags for the user
*/
FEATURE_FLAGS_UPDATED: 'featureFlagsUpdated',
/**
* The sidebar is asking the annotator to open the partner site help page.
*/
HELP_REQUESTED: 'helpRequested',
/** The sidebar is asking the annotator to do a partner site log in /** The sidebar is asking the annotator to do a partner site log in
* (for example, pop up a log in window). This is used when the client is * (for example, pop up a log in window). This is used when the client is
...@@ -24,19 +31,20 @@ module.exports = { ...@@ -24,19 +31,20 @@ module.exports = {
LOGOUT_REQUESTED: 'logoutRequested', LOGOUT_REQUESTED: 'logoutRequested',
/** /**
* The sidebar is asking the annotator to do a partner site sign-up. * The sidebar is asking the annotator to open the partner site profile page.
*/ */
SIGNUP_REQUESTED: 'signupRequested', PROFILE_REQUESTED: 'profileRequested',
/** /**
* The sidebar is asking the annotator to open the partner site profile page. * The set of annotations was updated.
*/ */
PROFILE_REQUESTED: 'profileRequested', PUBLIC_ANNOTATION_COUNT_CHANGED: 'publicAnnotationCountChanged',
/** /**
* The sidebar is asking the annotator to open the partner site help page. * The sidebar is asking the annotator to do a partner site sign-up.
*/ */
HELP_REQUESTED: 'helpRequested', SIGNUP_REQUESTED: 'signupRequested',
// Events that the annotator sends to the sidebar // Events that the annotator sends to the sidebar
// ---------------------------------------------- // ----------------------------------------------
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
* on $rootScope * on $rootScope
*/ */
module.exports = { module.exports = {
// Internal state changes
FRAME_CONNECTED: 'frameConnected',
// Session state changes // Session state changes
/** The list of groups changed */ /** The list of groups changed */
......
...@@ -11,8 +11,27 @@ ...@@ -11,8 +11,27 @@
*/ */
'use strict'; 'use strict';
var events = require('./events');
var bridgeEvents = require('../shared/bridge-events');
// @ngInject // @ngInject
function features($log, session) { function features($log, $rootScope, bridge, session) {
var _sendFeatureFlags = function(){
var userFeatures = session.state.features;
bridge.call(bridgeEvents.FEATURE_FLAGS_UPDATED, userFeatures || {});
};
// user changed is currently called when we initially load
// the sidebar and when the user actually logs out/in.
$rootScope.$on(events.USER_CHANGED, _sendFeatureFlags);
// send on frame connected as well because the user_changed event
// alone might run before the frames ever connected. This will
// provide us the follow up to make sure that the frames get the flags
$rootScope.$on(events.FRAME_CONNECTED, _sendFeatureFlags);
/** /**
* Returns true if the flag with the given name is enabled for the current * Returns true if the flag with the given name is enabled for the current
* user. * user.
......
...@@ -163,6 +163,8 @@ function FrameSync($rootScope, $window, Discovery, annotationUI, bridge) { ...@@ -163,6 +163,8 @@ function FrameSync($rootScope, $window, Discovery, annotationUI, bridge) {
return; return;
} }
$rootScope.$broadcast(events.FRAME_CONNECTED);
annotationUI.connectFrame({ annotationUI.connectFrame({
metadata: info.metadata, metadata: info.metadata,
uri: info.uri, uri: info.uri,
......
'use strict'; 'use strict';
var features = require('../features'); var features = require('../features');
var events = require('../events');
var bridgeEvents = require('../../shared/bridge-events');
describe('h:features', function () { describe('h:features - sidebar layer', function () {
var fakeBridge;
var fakeLog; var fakeLog;
var fakeRootScope;
var fakeSession; var fakeSession;
var sandbox;
beforeEach(function () { beforeEach(function () {
sandbox = sinon.sandbox.create();
fakeBridge = {
call: sinon.stub(),
};
fakeLog = { fakeLog = {
warn: sinon.stub(), warn: sinon.stub(),
}; };
fakeRootScope = {
eventCallbacks: {},
$broadcast: sandbox.stub(),
$on: function(event, callback) {
this.eventCallbacks[event] = callback;
},
};
fakeSession = { fakeSession = {
load: sinon.stub(), load: sinon.stub(),
state: { state: {
...@@ -22,29 +44,58 @@ describe('h:features', function () { ...@@ -22,29 +44,58 @@ describe('h:features', function () {
}; };
}); });
afterEach(function(){
sandbox.restore();
});
describe('flagEnabled', function () { describe('flagEnabled', function () {
it('should retrieve features data', function () { it('should retrieve features data', function () {
var features_ = features(fakeLog, fakeSession); var features_ = features(fakeLog, fakeRootScope, fakeBridge, fakeSession);
assert.equal(features_.flagEnabled('feature_on'), true); assert.equal(features_.flagEnabled('feature_on'), true);
assert.equal(features_.flagEnabled('feature_off'), false); assert.equal(features_.flagEnabled('feature_off'), false);
}); });
it('should return false if features have not been loaded', function () { it('should return false if features have not been loaded', function () {
var features_ = features(fakeLog, fakeSession); var features_ = features(fakeLog, fakeRootScope, fakeBridge, fakeSession);
// simulate feature data not having been loaded yet // simulate feature data not having been loaded yet
fakeSession.state = {}; fakeSession.state = {};
assert.equal(features_.flagEnabled('feature_on'), false); assert.equal(features_.flagEnabled('feature_on'), false);
}); });
it('should trigger a refresh of session data', function () { it('should trigger a refresh of session data', function () {
var features_ = features(fakeLog, fakeSession); var features_ = features(fakeLog, fakeRootScope, fakeBridge, fakeSession);
features_.flagEnabled('feature_on'); features_.flagEnabled('feature_on');
assert.calledOnce(fakeSession.load); assert.calledOnce(fakeSession.load);
}); });
it('should return false for unknown flags', function () { it('should return false for unknown flags', function () {
var features_ = features(fakeLog, fakeSession); var features_ = features(fakeLog, fakeRootScope, fakeBridge, fakeSession);
assert.isFalse(features_.flagEnabled('unknown_feature')); assert.isFalse(features_.flagEnabled('unknown_feature'));
}); });
}); });
it('should broadcast feature flags to annotation layer based on load/user changes', function(){
assert.notProperty(fakeRootScope.eventCallbacks, events.USER_CHANGED);
assert.notProperty(fakeRootScope.eventCallbacks, events.FRAME_CONNECTED);
features(fakeLog, fakeRootScope, fakeBridge, fakeSession);
assert.property(fakeRootScope.eventCallbacks, events.USER_CHANGED);
assert.property(fakeRootScope.eventCallbacks, events.FRAME_CONNECTED);
// respond to user changing by broadcasting the feature flags
assert.notCalled(fakeBridge.call);
fakeRootScope.eventCallbacks[events.USER_CHANGED]();
assert.calledOnce(fakeBridge.call);
assert.calledWith(fakeBridge.call, bridgeEvents.FEATURE_FLAGS_UPDATED, fakeSession.state.features);
// respond to frame connections by broadcasting the feature flags
fakeRootScope.eventCallbacks[events.FRAME_CONNECTED]();
assert.calledTwice(fakeBridge.call);
assert.calledWith(fakeBridge.call, bridgeEvents.FEATURE_FLAGS_UPDATED, fakeSession.state.features);
});
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment