[SOLVED] This SimpleSchema schema is terrible - how to improve it?

This is for a validated method:

  validate: new SimpleSchema({
    geoframes: Array,
    'geoframes.$': Object,
    'geoframes.$.end': String,
    'geoframes.$.name': String,
    'geoframes.$.start': String,
    'geoframes.$.polygons': Array,
    'geoframes.$.polygons.$': Array,
    'geoframes.$.polygons.$.$': Array,
    'geoframes.$.polygons.$.$.$': Number, // isn't there a better way?!
  }).validator(),

Is there a way to be non-specific about the contents of an array or object? I would’ve preferred to only get as specific as geoframes.$.polygons being an array, and put whatever I want in it without SimpleSchema telling me certain properties or structures are not valid.

Ahh, I missed this little option here: https://github.com/aldeed/node-simple-schema#blackbox

So I assume that would look like this:

  validate: new SimpleSchema({
    geoframes: Array,
    'geoframes.$': Object,
    'geoframes.$.end': String,
    'geoframes.$.name': String,
    'geoframes.$.start': String,
    'geoframes.$.polygons': { type: Array, blackbox: true },
  }).validator(),

You can assign a blackbox property to geoframes.$

"geoframes.$": {type: String, blackbox:true}

not sure if this will help, but this is how I do it:

import { Mongo } from 'meteor/mongo';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';

export const Extensions = new Mongo.Collection('Extensions');

Extensions.allow({
  insert: () => false,
  update: () => false,
  remove: () => false,
});

Extensions.deny({
  insert: () => true,
  update: () => true,
  remove: () => true,
});

export const ExtensionUpdate = new SimpleSchema({
  extensionId: { type: String },
  extensionAction: { type: String },
  extensionInformation: { type: Object, blackbox: true },
});

export const ExtensionSchema = new SimpleSchema({
  extensionGeneralSettings: { type: Object },
  'extensionGeneralSettings.name': { type: String },
  'extensionGeneralSettings.code': { type: String },
  'extensionGeneralSettings.key': { type: String },
  'extensionGeneralSettings.createDate': { type: Date },
});

Extensions.attachSchema(ExtensionSchema);

So what this means is that I can import 3 things into my methods file, like so:

import { Extensions, ExtensionSchema, ExtensionUpdate } from './extensions.js';

But then I don’t need the schema validation here, as just use validate: ExtensionUpdate.validator(), as an example. All updates then come in with three bits of info like so:

export const updateEGS = new ValidatedMethod({
  name: 'extensionGeneralSettings.update',
  validate: ExtensionUpdate.validator(),
  run({ extensionId, extensionAction, extensionInformation }) {
    if (Meteor.isServer) {
      switch (extensionAction) {
        case ('name'): {
          let setModifier = { $set: {} };

          setModifier.$set[
            'extensionGeneralSettings.name'
          ] = extensionInformation.name;

          Extensions.update(extensionId, setModifier);
          break;
        }
        case ('code'): {
          // some method
        }
        case ('key'): {
          // some method
        }
        default: {
          throw new Meteor.Error(
            'extensionGeneralSettings.update.failure',
            `Method ${extensionAction} was not recognized...`
          );
        }
      }
    }
  },
});

This simple pattern makes metod calls and testing method calls really simple. The major downside is that much more of your business logic is going to end up in client side functions. How to test these now? SO updside and downside, based around your use case. Just my 2c.

Tat

Notes: I have wrapper functions, which do a lot of the security heavy lifting, on client side, like:

/**
 * @function getExtensionGeneralSettings
 * @param { String } extensionId
 * @returns { Object } extensionGeneralSettings
 * @throws { Meteor.Error }
 */
function getExtensionGeneralSettings(extensionId) {
  checkExtension(extensionId);
  return getExtension(extensionId).extensionGeneralSettings;
}

/**
 * @function setExtensionGeneralSettings
 * @param { String } extensionId
 * @param { String } extensionAction
 * @param { Object } extensionInformation
 * @throws { Meteor.Error }
 */
function setExtensionGeneralSettings(extensionId, extensionAction, extensionInformation) {
  checkExtension(extensionId); // special checking functions
  // more checks. Depending on complexity, can also do deep checks
  // of object structure for the extensionInformation object. 
  check(extensionAction, Match.OneOf(
    'name', 'code', 'key', 'createDate',
  ));
  check(extensionInformation, Object);

  const updateModifier = {
    extensionId: extensionId,
    extensionAction: extensionAction,
    extensionInformation: extensionInformation,
  };

  updateEGS.call(updateModifier, (err) => {
    if (err) {
      throw new Meteor.Error(
        'setExtensionGeneralSettings.failure',
        `${extensionAction} cannot be completed...`
      );
    }
  });
}

/**
 * @function getEGSname
 * @param { String } extensionId
 * @returns { String } name
 * @throws { Meteor.Error }
 */
export function getEGSname(extensionId) {
  checkExtension(extensionId);
  return getExtensionGeneralSettings(extensionId).name;
}

/**
 * @function setLGSname
 * @param { String } extensionId
 * @param { String } name
 * @throws { Meteor.Error }
 */
export function setEGSname(extensionId, name) {
  checkExtension(extensionId);
  check(name, String);

  const extensionInformation = {
    name: name,
  };

  setExtensionGeneralSettings(extensionId, 'name', extensionInformation);
}