Skip to content

Commit

Permalink
feat: improved oneOf support
Browse files Browse the repository at this point in the history
Thanks to @znewsham for initial work on this

fixes #428
fixes #410
fixes #112
fixes #68
  • Loading branch information
aldeed committed Dec 11, 2022
1 parent e356fcd commit e8f91dc
Show file tree
Hide file tree
Showing 7 changed files with 1,096 additions and 415 deletions.
73 changes: 68 additions & 5 deletions src/SimpleSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,33 @@ class SimpleSchema {
return keySchema
}

/**
* @param key One specific or generic key for which to get all possible schemas.
* @returns An potentially empty array of possible definitions for one key
*
* Note that this returns the raw, unevaluated definition object. Use `getDefinition`
* if you want the evaluated definition, where any properties that are functions
* have been run to produce a result.
*/
schemas (key: string): StandardSchemaKeyDefinition[] {
const schemas: StandardSchemaKeyDefinition[] = []

const genericKey = MongoObject.makeKeyGeneric(key)
const keySchema = genericKey == null ? null : this._schema[genericKey]
if (keySchema != null) schemas.push(keySchema)

// See if it's defined in any subschema
this.forEachAncestorSimpleSchema(
key,
(simpleSchema, ancestor, subSchemaKey) => {
const keyDef = simpleSchema.schema(subSchemaKey)
if (keyDef != null) schemas.push(keyDef)
}
)

return schemas
}

/**
* @returns {Object} The entire schema object with subschemas merged. This is the
* equivalent of what schema() returned in SimpleSchema < 2.0
Expand Down Expand Up @@ -329,9 +356,45 @@ class SimpleSchema {
propList?: string[] | null,
functionContext: Record<string, unknown> = {}
): StandardSchemaKeyDefinitionWithSimpleTypes | undefined {
const defs = this.schema(key)
if (defs == null) return
const schemaKeyDefinition = this.schema(key)
if (schemaKeyDefinition == null) return
return this.resolveDefinitionForSchema(key, schemaKeyDefinition, propList, functionContext)
}

/**
* Returns the evaluated definition for one key in the schema
*
* @param key Generic or specific schema key
* @param [propList] Array of schema properties you need; performance optimization
* @param [functionContext] The context to use when evaluating schema options that are functions
* @returns The schema definition for the requested key
*/
getDefinitions (
key: string,
propList?: string[] | null,
functionContext: Record<string, unknown> = {}
): StandardSchemaKeyDefinitionWithSimpleTypes[] {
const schemaKeyDefinitions = this.schemas(key)
return schemaKeyDefinitions.map((def) => {
return this.resolveDefinitionForSchema(key, def, propList, functionContext)
})
}

/**
* Resolves the definition for one key in the schema
*
* @param key Generic or specific schema key
* @param schemaKeyDefinition Unresolved definition as returned from simpleSchema.schema()
* @param [propList] Array of schema properties you need; performance optimization
* @param [functionContext] The context to use when evaluating schema options that are functions
* @returns The schema definition for the requested key
*/
resolveDefinitionForSchema (
key: string,
schemaKeyDefinition: StandardSchemaKeyDefinition,
propList?: string[] | null,
functionContext: Record<string, unknown> = {}
): StandardSchemaKeyDefinitionWithSimpleTypes {
const getPropIterator = (obj: Record<string, any>, newObj: Record<string, any>) => {
return (prop: string): void => {
if (Array.isArray(propList) && !propList.includes(prop)) return
Expand Down Expand Up @@ -362,11 +425,11 @@ class SimpleSchema {
type: []
}

Object.keys(defs).forEach(getPropIterator(defs, result))
Object.keys(schemaKeyDefinition).forEach(getPropIterator(schemaKeyDefinition, result))

// Resolve all the types and convert to a normal array to make it easier to use.
if (Array.isArray(defs.type?.definitions)) {
result.type = defs.type.definitions.map((typeDef) => {
if (Array.isArray(schemaKeyDefinition.type?.definitions)) {
result.type = schemaKeyDefinition.type.definitions.map((typeDef) => {
const newTypeDef: SchemaKeyDefinitionWithOneType = {
type: String // will be overwritten
}
Expand Down
12 changes: 12 additions & 0 deletions src/ValidationContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import MongoObject from 'mongo-object'
import doValidation from './doValidation.js'
import { SimpleSchema } from './SimpleSchema.js'
import { CleanOptions, ObjectToValidate, ValidationError, ValidationOptions } from './types.js'
import { looksLikeModifier } from './utility/index.js'

export default class ValidationContext {
public name?: string
Expand Down Expand Up @@ -88,6 +89,17 @@ export default class ValidationContext {
upsert: isUpsert = false
}: ValidationOptions = {}
): boolean {
// First do some basic checks of the object, and throw errors if necessary
if (obj == null || (typeof obj !== 'object' && typeof obj !== 'function')) {
throw new Error('The first argument of validate() must be an object')
}

if (!isModifier && looksLikeModifier(obj)) {
throw new Error(
'When the validation object contains mongo operators, you must set the modifier option to true'
)
}

const validationErrors = doValidation({
extendedCustomContext,
ignoreTypes,
Expand Down
Loading

0 comments on commit e8f91dc

Please sign in to comment.