Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,6 @@ dist
# IDE specific
.idea
.vscode
.atom
.atom

.DS_Store
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ import SimpleSchema from "meteor/aldeed:simple-schema";

const mySchema = new SimpleSchema({ name: String });
const doc = { name: 123 };
const cleanDoc = mySchema.clean(doc);
const cleanDoc = await mySchema.clean(doc);
// cleanDoc is now mutated to hopefully have a better chance of passing validation
console.log(typeof cleanDoc.name); // string
```
Expand All @@ -331,7 +331,7 @@ import SimpleSchema from "meteor/aldeed:simple-schema";

const mySchema = new SimpleSchema({ name: String });
const modifier = { $set: { name: 123 } };
const cleanModifier = mySchema.clean(modifier);
const cleanModifier = await mySchema.clean(modifier);
// doc is now mutated to hopefully have a better chance of passing validation
console.log(typeof cleanModifier.$set.name); // string
```
Expand Down Expand Up @@ -835,7 +835,7 @@ Prior to SimpleSchema 2.0, objects that are instances of a custom class were con

_Used by the cleaning process but not by validation_

When you call `simpleSchemaInstance.clean()` with `trimStrings` set to `true`, all string values are trimmed of leading and trailing whitespace. If you set `trim` to `false` for certain keys in their schema definition, those keys will be skipped.
When you call `await simpleSchemaInstance.clean()` with `trimStrings` set to `true`, all string values are trimmed of leading and trailing whitespace. If you set `trim` to `false` for certain keys in their schema definition, those keys will be skipped.

### custom

Expand All @@ -845,7 +845,7 @@ Refer to the [Custom Validation](#custom-field-validation) section.

_Used by the cleaning process but not by validation_

Set this to any value that you want to be used as the default when an object does not include this field or has this field set to `undefined`. This value will be injected into the object by a call to `mySimpleSchema.clean()` with `getAutovalues: true`.
Set this to any value that you want to be used as the default when an object does not include this field or has this field set to `undefined`. This value will be injected into the object by a call to `await mySimpleSchema.clean()` with `getAutovalues: true`.

Note the following points of confusion:

Expand All @@ -862,6 +862,8 @@ _Used by the cleaning process but not by validation_

The `autoValue` option allows you to specify a function that is called by `simpleSchemaInstance.clean()` to potentially change the value of a property in the object being cleaned. This is a powerful feature that allows you to set up either forced values or default values, potentially based on the values of other fields in the object.

The `autoValue` function can be an `async` function

An `autoValue` function `this` context provides a variety of properties and methods to help you determine what you should return:

- `this.closestSubschemaFieldName`: If your schema is used as a subschema in another schema, this will be set to the name of the key that references the schema. Otherwise it will be `null`.
Expand Down
81 changes: 41 additions & 40 deletions lib/SimpleSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,7 @@ import { forEachKeyAncestor, isEmptyObject, merge } from './utility';
import defaultMessages from './defaultMessages';

// Exported for tests
export const schemaDefinitionOptions = [
'autoValue',
'defaultValue',
'label',
'optional',
'required',
'type',
];
export const schemaDefinitionOptions = ['autoValue', 'defaultValue', 'label', 'optional', 'required', 'type'];

const oneOfProps = [
'allowedValues',
Expand Down Expand Up @@ -53,7 +46,7 @@ const propsThatCanBeFunction = [

class SimpleSchema {
constructor(schema = {}, options = {}) {
// Stash the options object
// [...Stash the options object
this._constructorOptions = {
...SimpleSchema._constructorOptionDefaults,
...options,
Expand Down Expand Up @@ -88,8 +81,8 @@ class SimpleSchema {
}

/**
/* @returns {Object} The entire raw schema definition passed in the constructor
*/
/* @returns {Object} The entire raw schema definition passed in the constructor
*/
get rawDefinition() {
return this._rawDefinition;
}
Expand All @@ -114,7 +107,7 @@ class SimpleSchema {
* @returns {Boolean} True if the given object appears to be a SimpleSchema instance
*/
static isSimpleSchema(obj) {
return (obj && (obj instanceof SimpleSchema || obj._schema));
return obj && (obj instanceof SimpleSchema || obj._schema);
}

/**
Expand Down Expand Up @@ -222,7 +215,7 @@ class SimpleSchema {
mergedSchema[key] = keySchema;

keySchema.type.definitions.forEach((typeDef) => {
if (!(SimpleSchema.isSimpleSchema(typeDef.type))) return;
if (!SimpleSchema.isSimpleSchema(typeDef.type)) return;
const childSchema = typeDef.type.mergedSchema();
Object.keys(childSchema).forEach((subKey) => {
mergedSchema[`${key}.${subKey}`] = childSchema[subKey];
Expand Down Expand Up @@ -256,7 +249,8 @@ class SimpleSchema {
...functionContext,
});
// Inflect label if undefined
if (prop === 'label' && typeof newObj[prop] !== 'string') newObj[prop] = inflectedLabel(key, this._constructorOptions.humanizeAutoLabels);
if (prop === 'label' && typeof newObj[prop] !== 'string')
newObj[prop] = inflectedLabel(key, this._constructorOptions.humanizeAutoLabels);
} else {
newObj[prop] = val;
}
Expand Down Expand Up @@ -355,18 +349,16 @@ class SimpleSchema {

this._schemaKeys.forEach((key) => {
this._schema[key].type.definitions.forEach((typeDef) => {
if (!(SimpleSchema.isSimpleSchema(typeDef.type))) return;
result = result.concat(typeDef.type.autoValueFunctions().map(({
func,
fieldName,
closestSubschemaFieldName,
}) => {
return {
func,
fieldName: `${key}.${fieldName}`,
closestSubschemaFieldName: closestSubschemaFieldName.length ? `${key}.${closestSubschemaFieldName}` : key,
};
}));
if (!SimpleSchema.isSimpleSchema(typeDef.type)) return;
result = result.concat(
typeDef.type.autoValueFunctions().map(({ func, fieldName, closestSubschemaFieldName }) => {
return {
func,
fieldName: `${key}.${fieldName}`,
closestSubschemaFieldName: closestSubschemaFieldName.length ? `${key}.${closestSubschemaFieldName}` : key,
};
}),
);
});
});

Expand All @@ -379,7 +371,7 @@ class SimpleSchema {

this._schemaKeys.forEach((key) => {
this._schema[key].type.definitions.forEach((typeDef) => {
if (!(SimpleSchema.isSimpleSchema(typeDef.type))) return;
if (!SimpleSchema.isSimpleSchema(typeDef.type)) return;
typeDef.type.blackboxKeys().forEach((blackboxKey) => {
blackboxKeys.add(`${key}.${blackboxKey}`);
});
Expand All @@ -399,7 +391,7 @@ class SimpleSchema {
const testKeySchema = this.schema(ancestor);
if (testKeySchema) {
testKeySchema.type.definitions.forEach((typeDef) => {
if (!(SimpleSchema.isSimpleSchema(typeDef.type))) return;
if (!SimpleSchema.isSimpleSchema(typeDef.type)) return;
if (typeDef.type.keyIsInBlackBox(remainder)) isInBlackBox = true;
});
}
Expand Down Expand Up @@ -437,7 +429,7 @@ class SimpleSchema {
let allowed = false;
const subKey = key.slice(loopKey.length + 1);
fieldSchema.type.definitions.forEach((typeDef) => {
if (!(SimpleSchema.isSimpleSchema(typeDef.type))) return;
if (!SimpleSchema.isSimpleSchema(typeDef.type)) return;
if (typeDef.type.allowsKey(subKey)) allowed = true;
});
return allowed;
Expand Down Expand Up @@ -568,7 +560,8 @@ class SimpleSchema {
// Make sure parent has a definition in the schema. No implied objects!
if (fieldName.indexOf('.') > -1) {
const parentFieldName = fieldName.slice(0, fieldName.lastIndexOf('.'));
if (!Object.prototype.hasOwnProperty.call(this._schema, parentFieldName)) throw new Error(`"${fieldName}" is in the schema but "${parentFieldName}" is not`);
if (!Object.prototype.hasOwnProperty.call(this._schema, parentFieldName))
throw new Error(`"${fieldName}" is in the schema but "${parentFieldName}" is not`);
}

const definition = this._schema[fieldName];
Expand Down Expand Up @@ -657,7 +650,11 @@ class SimpleSchema {
const check = options.check || this._constructorOptions.check;
if (typeof check === 'function') {
// Call check but ignore the error
try { check(obj); } catch (e) { /* ignore error */ }
try {
check(obj);
} catch (e) {
/* ignore error */
}
}

// obj can be an array, in which case we validate each object in it and
Expand Down Expand Up @@ -717,12 +714,12 @@ class SimpleSchema {
}

validator(options = {}) {
return (obj) => {
return async (obj) => {
const optionsClone = { ...options };
if (options.clean === true) {
// Do this here and pass into both functions for better performance
optionsClone.mongoObject = new MongoObject(obj, this.blackboxKeys());
this.clean(obj, optionsClone);
await this.clean(obj, optionsClone);
}
if (options.returnErrorsPromise) {
return this.validateAndReturnErrorsPromise(obj, optionsClone);
Expand All @@ -735,8 +732,8 @@ class SimpleSchema {
return this.validator({ ...options, returnErrorsPromise: true });
}

clean(...args) {
return clean(this, ...args);
async clean(...args) {
return await clean(this, ...args);
}

/**
Expand Down Expand Up @@ -855,7 +852,7 @@ class SimpleSchema {

static validate(obj, schema, options) {
// Allow passing just the schema object
if (!(SimpleSchema.isSimpleSchema(schema))) {
if (!SimpleSchema.isSimpleSchema(schema)) {
schema = new SimpleSchema(schema);
}

Expand Down Expand Up @@ -957,12 +954,14 @@ function checkSchemaOverlap(schema) {
const val = schema[key];
if (!val.type) throw new Error(`${key} key is missing "type"`);
val.type.definitions.forEach((def) => {
if (!(SimpleSchema.isSimpleSchema(def.type))) return;
if (!SimpleSchema.isSimpleSchema(def.type)) return;

Object.keys(def.type._schema).forEach((subKey) => {
const newKey = `${key}.${subKey}`;
if (Object.prototype.hasOwnProperty.call(schema, newKey)) {
throw new Error(`The type for "${key}" is set to a SimpleSchema instance that defines "${key}.${subKey}", but the parent SimpleSchema instance also tries to define "${key}.${subKey}"`);
throw new Error(
`The type for "${key}" is set to a SimpleSchema instance that defines "${key}.${subKey}", but the parent SimpleSchema instance also tries to define "${key}.${subKey}"`,
);
}
});
});
Expand Down Expand Up @@ -1069,7 +1068,9 @@ function checkAndScrubDefinition(fieldName, definition, options, allKeys) {
Object.keys(type._schema).forEach((subKey) => {
const newKey = `${fieldName}.${subKey}`;
if (allKeys.has(newKey)) {
throw new Error(`The type for "${fieldName}" is set to a SimpleSchema instance that defines "${newKey}", but the parent SimpleSchema instance also tries to define "${newKey}"`);
throw new Error(
`The type for "${fieldName}" is set to a SimpleSchema instance that defines "${newKey}", but the parent SimpleSchema instance also tries to define "${newKey}"`,
);
}
});
}
Expand Down Expand Up @@ -1113,7 +1114,7 @@ function checkAndScrubDefinition(fieldName, definition, options, allKeys) {
definition.optional = !definition.required;
}
} else {
definition.optional = (options.requiredByDefault === false);
definition.optional = options.requiredByDefault === false;
}
}

Expand Down
Loading