reporting error messages when using ajv.addKeyword - javascript

const ajv = require("ajv");
const add_formats = require("ajv-formats");
const add_errors = require("ajv-errors");
const ajv_inst = new ajv({ $data: true, allErrors: true });
add_errors(add_formats(ajv_inst));
ajv_inst.addKeyword({
keyword: "age",
validate: (schema, data) => schema.max_age >= data && schema.min_age <= data,
metaSchema: {
// schema to validate keyword value
type: "object",
properties: {
min_age: { type: "integer" },
max_age: { type: "integer" },
},
required: ["min_age", "max_age"],
additionalProperties: false,
},
});
const schema = {
age: {
min_age: 18,
max_age: 100,
},
};
const validate = ajv_inst.compile(schema);
console.log(validate(17));
console.log(ajv_inst.errors);
output:
false
null
So it is working properly, except that there is no error message. How do I add custom error messages for custom keywords?

Related

How to trigger mongoose schema at instantization instead of save()?

I have to schema built with mongoose. This is the first schema.
const mongoose = require('mongoose');
const {
formatToCamelCase,
formatToCapitalizeEach
} = require('../src/util/formatter');
const formAttributeElementSchema = new mongoose.Schema({
elementTitle: {
type: String,
required: [true, 'Please provide element title in formAttributeSchema.'],
set: elementTitle => formatToCapitalizeEach(elementTitle),
},
datatype: {
type: String,
required: [true, 'Please provide data type for form attribute.'],
lowercase: true,
enum: {
values: ['string', 'number'],
message: 'We still not support {VALUE} data type in form attribute'
},
default: 'string',
},
attributeName: {
type: String,
required: [true, 'Please provide attribute name in form atribute schema.'],
set: attributeName => formatToCamelCase(attributeName),
},
isRequired: {
type: Boolean,
required: true,
default: true,
},
HTMLInputType: {
type: String,
required: [true, 'Please determine HTML input type for this form attribute.'],
enum: {
values: ['text', 'radio', 'checkbox'],
message: '{VALUE} is not supported yet. Please use supported type.',
},
},
maxLength: {
type: Number,
required: false,
validate: {
validator: (maxLength) => {
if (maxLength <= 0) return false;
},
message: 'Max length value can not have 0 or negative value.',
}
},
minLength: {
type: Number,
required: false,
validate: {
validator: (minLength) => {
if (minLength < 0) return false;
},
message: 'Min length can not have negative value.'
}
}
}, { _id: false });
const FormAttributeElement = mongoose.model('FormAttributeElement', formAttributeElementSchema);
module.exports = { FormAttributeElement };
and the second schema is:
const mongoose = require('mongoose');
const { formatToCapitalizeEach } = require('../src/util/formatter');
const { emailValidator } = require('../src/util/validator');
const { FormAttributeElement } = require('./formAttributeElement');
const dynamicFormDetails = new mongoose.Schema({
formTitle: {
type: String,
required: [true, 'Please provide form title.'],
set: formTitle => formatToCapitalizeEach(formTitle),
},
issuer: {
type: String,
required: [true, 'Please provide issuer email on form detail.'],
lowercase: true,
validate: {
validator: (issuer) => emailValidator(issuer),
message: 'Please makse sure use correct email format in issuer field on form detail attribute.',
},
},
startAt: {
type: Date,
required: [true, 'please provide date on startAt attribute.'],
},
closedAt: {
type: Date,
required: [true, 'Please provide date on closedAt attribute']
},
formShape: {
type: Array,
required: [true, 'Please provide form shape in form details.'],
validate: {
validator: (formShape) => {
for (let i = 0; i < formShape.length; i += 1) {
if (!formShape[i] instanceof FormAttributeElement) return false;
}
},
message: 'Form body only can be instance of FormAttributeElement',
},
},
});
const DynamicFormDetail = mongoose.model('DynamicFormDetail', dynamicFormDetails);
module.exports = { DynamicFormDetail };
now, i want to test my schema, especially it's validation using this code:
const { mongodbAtlasConnection } = require('../db/mongodb-connection');
const { DynamicFormDetail } = require('./dynamicForm');
const { FormAttributeElement } = require('./formAttributeElement');
const main = async () => {
let formElements = [];
let element;
const body = [
{
elementTitle: 'test aku hanya ngetes',
datatype: 'anjir harusnya gabisa sih',
isRequired: true,
HTMLInputType: 'blah',
}
]
body.forEach((data) => {
element = new FormAttributeElement({
elementTitle: data.elementTitle,
datatype: data.datatype,
attributeName: data.elementTitle,
isRequired: data.isRequired,
HTMLInputType: data.HTMLInputType,
});
formElements.push(element);
});
const formDetails = new DynamicFormDetail({
formTitle: 'test 123 form title',
issuer: 'lucky#gmail.com',
startAt: '2021-10-6',
closedAt: '2021-10-7',
formShape: formElements,
});
try {
await mongodbAtlasConnection();
const result = await formDetails.save();
console.log(result);
} catch (e) {
console.log(e)
}
}
main();
Yes, in DynamicFormDetail model, the validation is work just fine. If i wasn't supplied required attribute it throws an error. But, the problem is in the instantization of FormAttributeElement (inside of forEach loop) i can insert anything even if it's againts the constraint i defined in the schema. How to trigger that validation if i don't want to use .save() function on that model?

How can I create Json with data from a form?

I recently started working on a project in which the administrator can create tours online by filling out a form. By completing the form the information to be introduced intro a Mongoose Schema and create JSON.
In createcontent.js file takes the data from the form, I used new FormData(); because to upload images i use a module call multer.
const createTour_form = document.querySelector('.form-create__tour');
if (createTour_form) {
createTour_form.addEventListener('submit', (cr) => {
cr.preventDefault();
const create = new FormData();
// Name
create.append('name', document.getElementById('tour_name').value);
//Tour Duration
create.append('duration', document.getElementById('tour_duration').value);
//Number Participoants
create.append('maxGroupSize', document.getElementById('tour_participants').value);
//Tour Difficulty
create.append('difficulty', document.getElementById('tour_difficulty').value);
//Tour Price
create.append('price', document.getElementById('tour_prices').value);
//Short Description
create.append('summary', document.getElementById('tour_short_des').value);
//Description
create.append('description', document.getElementById('tour_long_des').value);
createTour(create);
})
}
I use a module slugify to convert the tour name to a slug that I use in the url.
This is my mongoose schema:
const tourchema = new mongoose.Schema({
name: {
type: String,
require: [true, 'A tour mush have a name'],
unique: true,
trim: true,
maxlength: [50, 'A tour name must have less or equal then 50 characters'],
minlength: [10, 'A tour name must have more or equal then 10 characters'],
//validate:[validator.isAlpha, 'Tour name must only contain characters']
},
slug: {
formType: String
},
duration: {
type: Number,
require: [true, 'A tour must have a duration']
},
maxGroupSize: {
type: Number,
require: [true, 'A tour must have a group size']
},
difficulty: {
type: String,
require: [true, 'A tour must have a difficulty'],
enum: {
values: ['easy', 'medium', 'difficult'],
message: 'Difficulty is either: easy, medium, difficult'
}
},
ratingsAverage: {
type: Number,
default: 4.5,
min: [1, 'Raiting must be above 1.0'],
max: [5, 'Raiting must be below 5.0'],
set: val => Math.round(val * 10) / 10
},
ratingsQuantity: {
type: Number,
default: 0
},
price: {
type: Number,
require: [true, 'A tour must have a price']
},
priceDiscount: {
type: Number,
validate: {
validator: function (val) {
//his only points to create doc on new document creation
return val < this.price;
},
message: 'Discount price ({VALUE}) shoud be below the regular price'
}
},
summary: {
type: String,
trim: true,
require: [true, 'A tour must have a description']
},
description: {
type: String,
trim: true
},
imageCover: {
type: String,
require: [true, 'A tour must have a cover image']
},
images: [String],
createdAt: {
type: Date,
default: Date.now()
},
startDates: [Date],
secretTour: {
type: Boolean,
default: false
},
startLocation: {
//GeoJSON
type: {
formType: String,
default: 'Point',
enum: ['Point']
},
coordinates: [Number],
adress: String,
description: String
},
locations: [
{
type: {
type: String,
default: 'Point',
enum: ['Point']
},
coordinates: [Number],
adress: String,
description: String,
day: Number
}
],
// guides:Array
guides: [
{
type: mongoose.Schema.ObjectId,
ref: 'User'
}
]
}
tourchema.index({ slug: 1 });
tourchema.pre('save', function (next) {
this.slug = slugify(this.name, { lower: true });
next();
});
When i upload data from form unsign axios winth asynchronus function:
import axios from 'axios';
import { showAlert } from './alert';
export const createTour = async(data)=>{
try{
const create_tour = await axios({
method:'POST',
url:'http://127.0.0.1:3000/api/v1/tours',
data:data
});
}
catch(err){
console.log(err);
showAlert('error',err.response.data.message);
}
}
when the referral action takes place an error occurs slugify: string argument expected
I did not find anything on the internet about this error.
I tried to build my own function to replace the module but it didn't work there is no solution to solve this error??
You can convert the "FormData" to an Object and then parse it.
function formDataToObj(formData) {
let obj = {};
for (let key of formData.keys()) {
obj[key] = formData.get(key)
}
return obj;
}
Example:
function formDataToObj(formData) {
let obj = {};
for (let key of formData.keys()) {
obj[key] = formData.get(key)
}
return obj;
}
const fd = new FormData()
fd.append('a', 1)
fd.append('b', 2)
const obj = formDataToObj(fd)
console.log( obj )
const json = JSON.stringify( obj )
console.log( json )
UPDATE: A better approach could be by using the native way like this:
Object.fromEntries(formData.entries())
Example:
const fd = new FormData()
fd.append('a', 1)
fd.append('b', 2)
const obj = Object.fromEntries(fd.entries())
console.log( obj )
const json = JSON.stringify( obj )
console.log( json )

My ObjectType is returning an empty object [duplicate]

This question already has answers here:
How to deal with cyclic dependencies in Node.js
(16 answers)
Closed 3 years ago.
So i have two objecttypes and i'm trying to include them to make relationships, one of them works and one of them just returns an empty object and i can't figure out why.
this one works, it console logs the ranktype and works fine
const Rank = require('../model/RankModel')
const { RankType } = require('./rank')
console.log(RankType)
/**
* Defines Branch Type
*/
const BranchType = new GraphQLObjectType({
name: "Branch",
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
color: { type: GraphQLString },
ranks: {
type: GraphQLList(RankType),
resolve: async (branch) => {
return await Rank.find({branch: branch.id})
}
}
}
})
module.exports.BranchType = BranchType
this is the one thats breaking
const Rank = require('../model/RankModel')
const Branch = require('../model/BranchModel')
const { BranchType } = require('./branch')
console.log(BranchType)
/**
* Defines Rank Type
*/
const RankType = new GraphQLObjectType({
name: "Rank",
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
shortHand: { type: GraphQLString },
branch: {
type: BranchType,
resolve: async (rank) => {
return await Branch.findById(rank.branch)
}
}
}
})
module.exports.RankType = RankType
this givs me an error of "message": "The type of Rank.branch must be Output Type but got: undefined."
Models/Relations:
BranchModel:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let branchSchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
color: {
type: String,
required: true,
unique: true
},
ranks: [{
type: Schema.Types.ObjectId,
ref: 'Rank'
}]
});
module.exports = mongoose.model('Branch', branchSchema)
RankModel
const mongoose = require('mongoose')
const Schema = mongoose.Schema
let rankSchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
shortHand: {
type: String,
required: true,
unique: true
},
branch: {
type: Schema.Types.ObjectId,
ref: 'Branch'
}
});
module.exports = mongoose.model('Rank', rankSchema);
Answer!!!!!!
/**
* Defines Rank Type
*/
const RankType = new GraphQLObjectType({
name: "Rank",
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
shortHand: { type: GraphQLString },
branch: {
type: require('./branch').BranchType,
resolve: async (rank) => {
console.log(rank.branch)
return await Branch.findById(rank.branch)
}
}
})
})
module.exports.RankType = RankType
Looks to me like you need to destructure BranchType like you did when requiring RankType, based what I can see from your module.exports
change
const BranchType = require('./branch')
to
const { BranchType } = require('./branch')

how to make fixed size arrays inside array mongoose schema

I have example input
[[1,2],[3,2],[1,3],...,[4,5]]
How to write model schema in mongoose?
This is my Schema
const SubproductSchema = new Schema({
...
positions: [{
type: [Number],
validate: {
validator: function(value){
return value.length == 2;
},
message: 'Positions should be 2'
}
}]
}, {
timestamps: true
});
And this does not work.
Input should be array with fixedSize with 2 length in array like this [[1,2],[3,2],[1,3],...,[4,5]]
If input is [[1,2,4],[3,2],[1,3],...,[4,5]], it should validate with 'Position should be 2'
UPDATED
I also tried this code (logically correct I guess):
const SubproductSchema = new Schema({
...
positions: {
type: [{
type: [Number],
validate: {
validator: function(value){
return value.length == 2;
},
message: 'Positions should be 2'
}
}],
}
}, {
timestamps: true
});
And my post is
{
...
"positions": [[2,3], [1,4], [4, 5]]
}
And it output error:
Subproduct validation failed: positions: Cast to Array failed for
value \"[ [ 2, 3 ], [ 1, 4 ], [ 4, 5 ] ]\" at path \"positions\""
model should look like
This is what you were looking...
const SubproductSchema = new Schema({
...
positions: [{
type: [Number],
validate: [limit, 'Positions should be 2']
}]
}, { timestamps: true });
const limit = (val) => {
let subElementsValidated = true;
val.forEach(el => {
if (el.length != 2){
subElementsValidated = false;
return;
}
});
return subElementsValidated;
}
You can change the validate options like this
const SubproductSchema = new Schema({
...
positions: [{
type: [Number],
validate: [limit, 'Positions should be 2']
}]
}, {
timestamps: true
});
const limit = (val) => {
return val.length == 2;
}

mongoose - how to get subdocument to comply with a schema

So I have the following schemas:
var Item_Detail = new Schema(
{
content: {
index: true,
type: Array
},
is_private: {
default: false,
index: true,
type: Boolean
},
order: {
index: true,
required: true,
type: Number
},
table: {
default: {},
type: Object
},
title: {
required: true,
index: true,
type: String,
},
type: {
default: "text",
enum: ["text", "table"],
index: true,
type: String
},
},
{
strict: false
}
)
const Item = new Schema(
{
details: {
default: [],
index: true,
type: [Item_Detail],
},
display_name: {
default: "",
index: true,
type: String,
},
image: {
default: "http://via.placeholder.com/700x500/ffffff/000000/?text=No%20Image&",
type: String
},
is_private: {
default: false,
index: true,
type: Boolean
},
tags: {
index: true,
type: [Tag]
}
},
{
strict: false
}
)
Now, Item_Detail is to be a subdocument of Item, but I'm not quite sure how I should enforce the defaults and type restriction. I also don't want Item_Detail to be a collection in its own right, so using create or save probably doesn't fit.
I think you can use embedded documents for this so in your item schema you can embed an item_detail:
const Item = new Schema({
...
item_detail: item_detail
})
Then on the server when you want to add an item_detail you can do the following
myItem = new Item({//enter item data here})
//assign item detail here
myItem.item_detail = item_detail ;
then proceed to save it
myItem.save()
Enforcing the type is easy the default value is the tricky one but mongoose allows you to specify a function instead of boolean for default (just like it allows you the same for required).
So you could do something like this (I shortened the schemas for bravity):
const itemDetails = new Schema({
info: {
type: String,
required: true
}
})
const item = new Schema({
details: {
default: function() {
return [new ItemDetails({ info: 'N/A' })]
},
type: [itemDetails],
}
})
This would allow you to do something like this:
var itm = new Item();
And the saved result would be:
{
"_id": ObjectId("5b72795d4aa17339b0815b8b"),
"details": [{
"_id": ObjectId("5b72795d4aa17339b0815b8c"),
"info": "N/A"
}]
}
So this gives you two things:
You cannot put any type in details other than itemDetails since you enforced the type with itemDetails.
You initialize your ItemDetails object with defaults you want in your default custom function.
Hope this helps.

Categories