'use strict';
/**
* Provide the NotificationController class and two functions.
*
* @module store
* @submodule store.notifications
*/
YUI.add('juju-notification-controller', function(Y) {
var juju = Y.namespace('juju');
var _changeNotificationOpToWords = function(op) {
if (op === 'add') {
return 'created';
}
else if (op === 'remove') {
return 'removed';
}
else { // Unexpected operation.
console.log('Unexpected operation.', op);
return 'changed';
}
};
var _relationNotifications = {
model_list: 'relations',
title: function(change_type, change_op, change_data, notify_data) {
return 'Relation ' + _changeNotificationOpToWords(change_op);
},
message: function(change_type, change_op, change_data, notify_data) {
var action = _changeNotificationOpToWords(change_op);
if (change_op === 'remove') {
// We don't have endpoint data on removed.
return 'Relation removed';
}
if (!change_data.endpoints) {
return ('Relation was ' + action);
}
if (change_data.endpoints.length === 2) {
var endpoint0 = change_data.endpoints[0][0],
endpoint1 = change_data.endpoints[1][0],
relationType0 = change_data.endpoints[0][1].name,
relationType1 = change_data.endpoints[1][1].name;
return ('Relation between ' +
endpoint0 + ' (relation type "' + relationType0 + '") ' +
'and ' +
endpoint1 + ' (relation type "' + relationType1 + '") ' +
'was ' + action);
}
var endpoint = change_data.endpoints[0][0],
relationType = change_data.endpoints[0][1].name;
return ('Relation with ' +
endpoint + ' (relation type "' + relationType + '") ' +
'was ' + action);
}
// There is no relation eviction because relation errors are
// reported on units, there are no relation errors to evict.
};
/**
* This controller manages the relationship between incoming delta stream
* events and the notification models the views use for display and
* interaction.
*
* NotificationController({env: Environment, notifications: ModelList})
*
* @class NotificationController
*/
var NotificationController = Y.Base.create('NotificationController',
Y.Base, [], {
/**
* Tell `Y.Base` that it should create ad hoc attributes for
* config properties.
*
* @property _allowAdHocAttrs
* @type {Boolean}
* @default true
* @protected
* @since 3.5.0
*/
_allowAdHocAttrs: true,
/*
* Parse the delta stream looking for interesting events to translate
* into notifications.
*
* change_type maps to a set of controls
* model_list: (string)
* where do objects of this type live in the app.db
*
* Each attribute of notify is checked in the appropriate rule
* for a function that can return its value, if no rule is defined
* the function in a rule called 'default' will be used. The signature
* of all such methods will be::
* function(change_type, change_op, change_data) -> value
*/
ingest_rules: {
service: {
model_list: 'services'
},
relation: _relationNotifications,
unit: {
model_list: 'units',
level: function(change_type, change_op, change_data) {
var astate = change_data['agent-state'];
if (astate !== undefined &&
astate.search('error') > -1) {
return 'error';
}
return 'info';
},
message: function(change_type, change_op, change_data,
notify_data) {
var level = notify_data.level,
astate = change_data['agent-state'],
msg = '';
if (astate !== undefined) {
msg = 'Agent-state = ' + astate + '.';
}
return msg;
},
evict: function(old, new_data, change) {
if (new_data.level !== 'error') {
if (old.get('seen') === false) {
// mark it as seen
old.set('seen', true);
}
}
}
},
// Defaults when no special rules apply
'default': {
title: function(change_type, change_op, change_data, notify_data) {
var level = notify_data.level,
msg = 'Problem with ';
if (level === 'error') {
msg = 'Error with ';
}
return msg + change_data.id;
},
message: function(change_type, change_op, change_data) {
return 'Action required to resolve the problem.';
},
level: function() {
return 'info';
}
}
},
/**
* Process new delta stream events and see if we need new
* notifications.
*
* @method generate_notices
*/
generate_notices: function(delta_evt) {
var self = this,
rules = this.ingest_rules,
app = this.get('app'),
notifications = this.get('notifications');
delta_evt.data.result.forEach(function(change) {
var change_type = change[0],
change_op = change[1],
change_data = change[2],
notify_data = {isDelta: true},
rule = rules[change_type],
model;
/*
* Data ingestion rules
* Create notifications for incoming deltas
* Promote some notifications to the 'show me' list
* Also:
* - for each change event see if there is an notice
* relating to that object in the model list
* -- see if the current change event invalidates the need
* to show the existing notices
* -- make the new notice as 'must see' or not (
* errors, etc)
* - add a notification for the event
*/
// Dispatch ingestion rules (which may mutate either the
// current 'notifications' or models within it (notice status)
// 'level' must be called first as 'title' and 'message' may
// depend on it being set.
['level', 'title', 'message'].forEach(function(attr) {
var active_rule = rule;
if (!active_rule) {
return;
}
if (!(attr in active_rule)) {
// No special rule exists, use the default
active_rule = rules['default'];
}
// Assign the attribute by the rule
if (attr in active_rule) {
notify_data[attr] = active_rule[attr](
change_type, change_op, change_data, notify_data);
}
});
notify_data.kind = change_type;
// see if there is an object associated with this
// message
model = app.db.getModelFromChange(change);
if (model) {
// modelId setter pulls the modelId reference from a
// model automatically
notify_data.modelId = model;
notify_data.link = app.getModelURL(model);
// If there are eviction rules for old notices
// based on this model
var existing = notifications.getNotificationsForModel(
model);
if (existing && rule && rule.evict) {
existing.forEach(function(old) {
rule.evict(old, notify_data, change,
notifications);
});
}
}
// If we have a title set we have enough info
// to add _something_
if (notify_data.title) {
notifications.create(notify_data);
}
});
}
});
juju.NotificationController = NotificationController;
juju._changeNotificationOpToWords = _changeNotificationOpToWords;
juju._relationNotifications = _relationNotifications;
}, '0.1.0', {
requires: ['juju-models']
});