Mongoose Validation falling for embedded document

Hi,

I have this Issues schema:

// Require Mongoose
import mongoose from "mongoose";
import { rolesSchema } from "./roles.js";
import { storyPointSchema } from "./storyPoint.js";


// Define a schema
const Schema = mongoose.Schema;
/**
 * Issues Report Schema
 */
export const issuesSchema = new Schema({
    team: {
        required: true, 
        type: String,
    },
    teamRoles: {
        type: [rolesSchema],
        required: true
    },
    ticketId: {
        type: String,
        required: true
    },
    issueName: {
        type: String,
        required: true,
    },
    description: {
        type: String,
        required: true
    },
    issueType: {
        type: String,
        enum: ['story', 'bug', 'task', 'sub-task', 'epic'],
        default: 'story',
        required: true
    },
    storyPoints: {
        accepted: storyPointSchema,
        committed: storyPointSchema,
        completed: storyPointSchema,
        estimated: storyPointSchema, 
        actual: storyPointSchema
    }
}, {_id: false})

/**
 * Calculate Full Name
 * Virtual for ticket assignee's full name
 */
issuesSchema.virtual('fullname').get(function () {
    let fullName = ""
    if (this.firstName && this.lastName) {
        fullName = `${this.firstName}, ${this.lastName}`
    }
    return fullName
})

export const Issues =  mongoose.model('Issues', issuesSchema)

and the two embedded document schemas:

export const rolesSchema = new Schema({
    firstName: {
        type: String,
        required: true
    },
    lastName:{
        type: String,
        required: true
    },
    role: {
        type:String,
        required: true
    }
})

export const Roles =  mongoose.model('Roles', rolesSchema)

// Require Mongoose
import mongoose from "mongoose";

// Define a schema
const Schema = mongoose.Schema;
/**
 * Story Point Report Schema
 */
export const storyPointSchema = new Schema([{
    storyPoint: {
        type: Number,
        required: true,
        enum: [0,1, 2, 3, 5, 8, 13]
    }
}])

In unit testing I can get all errors that the validators throw when required is missing or wrong format data

Here is the validations errors:

{
  "errors": {
    "description": {
      "name": "ValidatorError",
      "message": "Path `description` is required.",
      "properties": {
        "message": "Path `description` is required.",
        "type": "required",
        "path": "description"
      },
      "kind": "required",
      "path": "description"
    },
    "issueName": {
      "name": "ValidatorError",
      "message": "Path `issueName` is required.",
      "properties": {
        "message": "Path `issueName` is required.",
        "type": "required",
        "path": "issueName"
      },
      "kind": "required",
      "path": "issueName"
    },
    "ticketId": {
      "name": "ValidatorError",
      "message": "Path `ticketId` is required.",
      "properties": {
        "message": "Path `ticketId` is required.",
        "type": "required",
        "path": "ticketId"
      },
      "kind": "required",
      "path": "ticketId"
    },
    "team": {
      "name": "ValidatorError",
      "message": "Path `team` is required.",
      "properties": {
        "message": "Path `team` is required.",
        "type": "required",
        "path": "team"
      },
      "kind": "required",
      "path": "team"
    },
    "issueType": {
      "name": "ValidatorError",
      "message": "`foo` is not a valid enum value for path `issueType`.",
      "properties": {
        "message": "`foo` is not a valid enum value for path `issueType`.",
        "type": "enum",
        "enumValues": [
          "story",
          "bug",
          "task",
          "sub-task",
          "epic"
        ],
        "path": "issueType",
        "value": "foo"
      },
      "kind": "enum",
      "path": "issueType",
      "value": "foo"
    }
  },
  "_message": "Issues validation failed",
  "name": "ValidationError",
  "message": "Issues validation failed: description: Path `description` is required., issueName: Path `issueName` is required., ticketId: Path `ticketId` is required., team: Path `team` is required., issueType: `foo` is not a valid enum value for path `issueType`."

Can you see any issues with these schemas to see why both embedded documents fail to register any errors relevant to roles or story points?

So I solved this validated by using this function in the issueSchema code:

issuesSchema.path('teamRoles').validate(function(teamRoles){
    if(!teamRoles){return false}
    else if(teamRoles.length === 0){return false}
    return true;
}, 'There must be at least one role added to each issue');

Now you can see the error is included:

{
  "errors": {
    "description": {
      "name": "ValidatorError",
      "message": "Path `description` is required.",
      "properties": {
        "message": "Path `description` is required.",
        "type": "required",
        "path": "description"
      },
      "kind": "required",
      "path": "description"
    },
    "issueName": {
      "name": "ValidatorError",
      "message": "Path `issueName` is required.",
      "properties": {
        "message": "Path `issueName` is required.",
        "type": "required",
        "path": "issueName"
      },
      "kind": "required",
      "path": "issueName"
    },
    "ticketId": {
      "name": "ValidatorError",
      "message": "Path `ticketId` is required.",
      "properties": {
        "message": "Path `ticketId` is required.",
        "type": "required",
        "path": "ticketId"
      },
      "kind": "required",
      "path": "ticketId"
    },
    "team": {
      "name": "ValidatorError",
      "message": "Path `team` is required.",
      "properties": {
        "message": "Path `team` is required.",
        "type": "required",
        "path": "team"
      },
      "kind": "required",
      "path": "team"
    },
    "teamRoles": {
      "name": "ValidatorError",
      "message": "There must be at least one role added to each issue",
      "properties": {
        "message": "There must be at least one role added to each issue",
        "type": "user defined",
        "path": "teamRoles",
        "value": []
      },
      "kind": "user defined",
      "path": "teamRoles",
      "value": []
    }
  },
  "_message": "Issues validation failed",
  "name": "ValidationError",
  "message": "Issues validation failed: description: Path `description` is required., issueName: Path `issueName` is required., ticketId: Path `ticketId` is required., team: Path `team` is required., teamRoles: There must be at least one role added to each issue"
}

Please close this ticket.

0

To get default or any validation working for the Story Points I had to flatten this schema ( As per Mongoose’s documentation where it says embedded sub documents can be tricky as they don’t actually belong to document so validation won’t work.

Here is the updated storyPointSchema:

// Require Mongoose
import mongoose from "mongoose";
import { ActoValidator } from "../services/validators/ActoValidator.js";
const Validator = new ActoValidator();
   

// Define a schema
const Schema = mongoose.Schema;
/**
 * Story Point Report Schema
 */
export const storyPointSchema = new Schema({
    accepted:{
        type: Number,
        enum: [0,1, 2, 3, 5, 8, 13],
    },
    committed: {
        type: Number,
        enum: [0,1, 2, 3, 5, 8, 13],
    },
    completed: {
        type: Number,
        enum: [0,1, 2, 3, 5, 8, 13],
    },
    estimated: {
        type: Number,
        enum: [0,1, 2, 3, 5, 8, 13],

    },
    actual: {
        type: Number,
        enum: [0,1, 2, 3, 5, 8, 13],
    },  
})

And then the issue schema is:

// Require Mongoose
import mongoose from "mongoose";
import { rolesSchema } from "./roles.js";
import { storyPointSchema } from "./storyPoint.js";

// Define a schema
const Schema = mongoose.Schema;
/**
 * Issues Report Schema
 */
export const issuesSchema = new Schema({
    team: {
        required: true, 
        type: String
    },
    teamRoles: {
        type: [rolesSchema],
        required: true
    },
    ticketId: {
        type: String,
        required: true
    },
    issueName: {
        type: String,
        required: true,
    },
    description: {
        type: String,
        required: true
    },
    issueType: {
        type: String,
        enum: ['story', 'bug', 'task', 'sub-task', 'epic'],
        default: 'story',
        required: true
    },
    storyPoints: {
        type: storyPointSchema,
        required: [true,  "`the estimated story point field` is required, either 0, 1, 2, 3, 5, 8, or 13"],
    }
}, {_id: false})

This now has the appropriate errors:

{
  "errors": {
    "storyPoints": {
      "name": "ValidatorError",
      "message": "`the estimated story point field` is required, either 0, 1, 2, 3, 5, 8, or 13",
      "properties": {
        "message": "`the estimated story point field` is required, either 0, 1, 2, 3, 5, 8, or 13",
        "type": "required",
        "path": "storyPoints"
      },
      "kind": "required",
      "path": "storyPoints"
    },
    "description": {
      "name": "ValidatorError",
      "message": "Path `description` is required.",
      "properties": {
        "message": "Path `description` is required.",
        "type": "required",
        "path": "description"
      },
      "kind": "required",
      "path": "description"
    },
    "issueName": {
      "name": "ValidatorError",
      "message": "Path `issueName` is required.",
      "properties": {
        "message": "Path `issueName` is required.",
        "type": "required",
        "path": "issueName"
      },
      "kind": "required",
      "path": "issueName"
    },
    "ticketId": {
      "name": "ValidatorError",
      "message": "Path `ticketId` is required.",
      "properties": {
        "message": "Path `ticketId` is required.",
        "type": "required",
        "path": "ticketId"
      },
      "kind": "required",
      "path": "ticketId"
    },
    "team": {
      "name": "ValidatorError",
      "message": "Path `team` is required.",
      "properties": {
        "message": "Path `team` is required.",
        "type": "required",
        "path": "team"
      },
      "kind": "required",
      "path": "team"
    },
    "teamRoles": {
      "name": "ValidatorError",
      "message": "`issueRole` is required. There must be at least one role added to each issue",
      "properties": {
        "message": "`issueRole` is required. There must be at least one role added to each issue",
        "type": "user defined",
        "path": "teamRoles",
        "value": []
      },
      "kind": "user defined",
      "path": "teamRoles",
      "value": []
    }
  },
  "_message": "Issues validation failed",
  "name": "ValidationError",
  "message": "Issues validation failed: storyPoints: `the estimated story point field` is required, either 0, 1, 2, 3, 5, 8, or 13, description: Path `description` is required., issueName: Path `issueName` is required., ticketId: Path `ticketId` is required., team: Path `team` is required., teamRoles: `issueRole` is required. There must be at least one role added to each issue"

I hope this helps someone else :shrug"