scotthovestadt / schema-object

Enforce schema on JavaScript objects, including type, transformation, and validation. Supports extends, sub-schemas, and arrays.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Errors should be pass through parent SchemaObjects.

marmol-dev opened this issue · comments

When trying to validate a object within other object the validator doesn't show any error. It corrects the types and put them as 'undefined' but no error is shown. Example:

var Post = new SchemaObject({
    _id: {
        type: Number,
        required: true,
    },
    title: {
        type: String,
        minLength: 10
    },
    content: String
});

var Location = new SchemaObject({
    town: String,
    city: String
});

var User = new SchemaObject({
    _id: Number,
    name: {
        type: String,
        minLength: 5,
    },
    posts: [Post],
    location: Location
});

var user = new User({
    _id: 5,
    name: 'ThisWillBeCorrectlyValidated',
    location: {
        town: {
            invalidTypeThatWontShowError: true
        },
        city: 'Ourense'
    },
    posts: [{
        _id: 3,
        title: 'short',
        content: 'Title wont be correctly validated'
    }]
});

user.toObject() will return:

{
    _id: 5,
    name: "ThisWillBeCorrectlyValidated",
    posts: [{
        _id: 3
        title: undefined,
        content: "Title wont be correctly validated"
    }],
    location: {
        town: undefined,
        city: "Ourense"
    }
}

And user.getErrors() returns and empty array. What should I do to get the errors of the array and the object?

I'll look into this today.

Currently, errors do not waterfall down into the parent SchemaObject. I'll work on fixing that ASAP.

I ran into this today, and as a stop-gap have added the following getAllErrors() method to my parent SchemaObject.

function getAllErrors() {
  // Array.<Object> of { <SchemaObjectInstance>, String - parent node }
  const schemas = findNestedSchemaObjects(this).concat({ so: this, parent: "" });

  return schemas
    .map(obj => obj.so.getErrors()
      // Merge the parent property into the SetterError
      .map(SetterError => Object.assign(SetterError, { parent: obj.parent }))
    )
    // Flatten .getError() results into single array
    .reduce((errors, nextError) => errors.concat(nextError)
  );
}

function findNestedSchemaObjects(schemaObject, parent = "<root>") {
  let nestedSchemas = [];

  for (const property in schemaObject) {
    if (schemaObject[property] && schemaObject[property].constructor.name === "SchemaObjectInstance"
    ) {
      nestedSchemas.push({ so: schemaObject[property], parent });

      nestedSchemas = nestedSchemas.concat(
        findNestedSchemaObjects(schemaObject[property], `${parent}.${property}`)
      );
    }
  }

  return nestedSchemas;
}

This isn't a great solution as it assumes that you have no intermediate, non-SchemaObject properties in your chain of nested schema objects.

I tried moving the recursive call outside of the SchemaObjectInstance check (i.e. to recurse on any property), and ended up with stack overflows, I'm guessing due to supporting properties with circular references. Didn't spend any time with the source trying to go deeper since my use case didn't require it.

@tyronep If you're using this actively I'll work on getting a permanent solution in the codebase this weekend. Thanks for the code sample.

@tyronep In 3.3.0, getErrors() now includes errors from parent SchemaObject(s). Let me know if this solves your use-case.

I've updated the errors fieldSchema.name to include the key from the parent object dot-notation style instead of including a reference to the actual parent object. Additionally, the SchemaObject reference itself is on key schemaObject.

Example:

[ SetterError {
    errorMessage: 'String length too short to meet minLength requirement.',
    setValue: '1234',
    originalValue: undefined,
    fieldSchema: { type: 'string', minLength: 15, name: 'string' },
    so: SchemaObjectInstance {} },
  SetterError {
    errorMessage: 'String length too short to meet minLength requirement.',
    setValue: '1234',
    originalValue: undefined,
    fieldSchema: { type: 'string', minLength: 15, name: 'subobj.string' },
    so: SchemaObjectInstance {} } ]

Apologies for the delayed response -- this looks fantastic. Your project was the most discoverable on npm for application schema management, outside of pure JSON schema-based validation, and in line with what most people coming from an ODM like mongoose are expecting.

Thanks for the quick add!