Search code examples
meteorsimple-schema

Can SimpleSchema express "object with custom keys and specific schema for values"?


I want to make a SimpleSchema for documents with the the following format:

{
  ...,
  permissions: {
    foo: {allow: ["user1", "user2"]},
    bar: {allow: ["admin"]},
  }
}

If foo and bar were well-known strings in the schema, I would just do this:

const PermissionsSchema = new SimpleSchema({
  allow: {type: [String]},
});

new SimpleSchema({
  ...,
  'permissions.foo': {
    type: PermissionSchema,
  },
  'permissions.bar': {
    type: PermissionSchema,
  },
})

However, in this case, there can be arbitrary string keys, not just foo and bar. The values must always match PermissionsSchema. Is there a way to express this?


Solution

  • Custom validators to the rescue!

    import { ValidationError } from 'mdg:validation-error';
    
    function permissionsValidator(keyRegEx) {
      if (!(keyRegEx instanceof RegExp)) {
        throw new Error('must pass a regular expression');
      }
      return function() {
        // https://github.com/aldeed/meteor-simple-schema#custom-validation
        const value = this.value;
        for (let key in value) {
          if (value.hasOwnProperty(key)) {
            if (!keyRegEx.test(key)) {
              return 'regEx';
            }
            try {
              PermissionSchema.validate(value[key]);
            } catch (ex) {
              if (ex instanceof ValidationError) {
                return ex.error;
              }
            }
          }
        }
      };
    }
    
    new SimpleSchema({
      ...,
      permissions: {
        type: Object,
        custom: permissionsValidator(/^.*$/),
        blackbox: true,
        optional: true,
        defaultValue: {},
      },
    });
    

    The error messages that come out are rubbish, though. Improvements or better strategies still welcome.