dispatch:configuration 
A Meteor package that provides a simple API for working with cascading app configurations.
A Simple Example
Let's suppose you want to allow users to configure a setting within your app. After they have changed the setting, you want to use their choice, but until then you want to use a default.
First we call Configuration.setSchema
in common (both client and server) startup code:
1Configuration.setSchema(new SimpleSchema({ 2 hideAlerts: {type: Boolean} 3}));
You can also call Configuration.setSchemaForPrefix
if you want to set the schema only for a certain nested property. This is useful if you have packages that define their own configuration.
1Configuration.setSchemaForPrefix('myPrefix', newSimpleSchema({ 2 propertyUniqueToThisPackage:{type:String} 3}));
Next we call Configuration.setDefault
in server code to define the default values for all settings.
1Configuration.setDefault({ 2 hideAlerts: false 3});
And later you can set the default for a prefix:
1Configuration.setDefaultForPrefix('myPrefix', { 2 propertyUniqueToThisPackage:'foo' 3});
Now when we want to get the settings for user "ABC", we can do this:
server code (synchronous)
1var shouldHideAlerts = Configuration.user('ABC').hideAlerts;
client code (asynchronous)
1Configuration.user('ABC', function (error, config) { 2 if (error) throw error; 3 var shouldHideAlerts = config.hideAlerts; 4});
And to change the user's settings (overriding the defaults), you only have to set the property and the database will be updated:
server code (synchronous)
1Configuration.user('ABC').hideAlerts = true;
client or server code (asynchronous)
1Configuration.user('ABC', function (error, config) { 2 if (error) throw error; 3 config.hideAlerts = true; 4});
This relies on the dispatch:bound-document
package to work. You can't set object properties or properties within arrays with this method. If you need to do so, you can call Configuration.setForEntity
to replace the whole user config with a new one:
client or server code
1Configuration.setForEntity('user', 'ABC', { 2 hideAlerts: true 3});
A Complex Example
There is a default user
entity type, but you can override it to add additional layers of inheritance. Call Configuration.addEntityType
in common code for each entity type, including user
. Here is an example where users inherit their configuration from the organization
they belong to.
common code
1Configuration.addEntityType('organization', { 2 inherit: 'default', 3 publish: function (userId) { 4 // must return id or array of ids 5 if (!userId) return; 6 7 var user = Meteor.users.findOne(userId); 8 if (!user) return; 9 10 return user.organization_id; 11 }, 12 write: function (userId, id) { 13 // must return true or false 14 if (!userId) return false; 15 16 var user = Meteor.users.findOne(userId); 17 if (!user) return false; 18 19 return user.organization_id === id; 20 } 21}); 22 23// We override the default user entity type in order to specify inheritance from org 24Configuration.addEntityType('user', { 25 inherit: function (id) { 26 // must return [entityType, entityId] or "default" 27 var user = Meteor.users.findOne(id, { 28 fields: { 29 organization_id: 1 30 } 31 }); 32 if (!user || !user.organization_id) return 'default'; 33 34 return ['organization', user.organization_id]; 35 }, 36 publish: function (userId) { 37 // must return id or array of ids 38 return userId; 39 }, 40 write: function (userId, id) { 41 // must return true or false 42 return userId === id; 43 } 44});
There are lots of possibilities. You could do role-based configuration where users inherit from role entity types like admin
or manager
, which in turn inherit from default.
Configuration.addEntityType Options
inherit
A function that returns[entityType, entityId]
to inherit from or "default". Receives the entityId as first argument and any options you pass togetForEntity
are provided as the second argument, allowing you to do complex inheritance based on calling context if necessary. For inheriting from the default configuration, you can set this to the string "default" instead of a function.write
A function that receives theuserId
andentityId
and returnstrue
orfalse
to allow or disallow updating it from the client. Only called from server code.publish
A function that receives theuserId
and returns theentityId
or array of entityIds that should be published for this type, or returnsundefined
for none. Only called from server code.cannotOverride
An array of fields in the schema that cannot be overridden (must inherit) for this entity type
Security for Editing Defaults From Client
To allow calling Configuration.setDefault
from client code for some users, define a role checking function using Configuration.canEditDefault
in server code. This is like the write
function for the default entity.
server code
1Configuration.canEditDefault(function (userId) { 2 if (!userId) return false; 3 4 var userRole = new Roles.User(userId); 5 return userRole.is(Roles.ADMIN); 6});
Audit Trail
After every successful configuration update, Configuration
will emit an "afterUpdate" event in server code. You can use this to log an audit trail.
1Configuration.on('afterUpdate', (info) => { 2 log(info); 3});
Limitations
Currently all entity IDs must be strings and must not contain the underscore character.