dispatch:configuration

v0.2.2Published 9 years ago

This package has not had recent updates. Please investigate it's current state before committing to using it in your project.

dispatch:configuration Build Status

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 to getForEntity 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 the userId and entityId and returns true or false to allow or disallow updating it from the client. Only called from server code.
  • publish A function that receives the userId and returns the entityId or array of entityIds that should be published for this type, or returns undefined 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.