NodeJS Validation with JOI [duplicate] - node.js

This question already has answers here:
How to validate array of objects using Joi?
(6 answers)
Closed last year.
I am using bellow code:
const coll = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jemmy' },
{ id: 3, name: 'Jenny' }
];
const schema = Joi.object().keys({
name: Joi.string().min(3).required()
});
return schema.validate(coll);
when my coll array is valid, then also when checking the schema, it shows the following, always going to the error section.
schema validator
{
value: [
{ id: 1, name: 'Action' },
{ id: 2, name: 'Horror' },
{ id: 3, name: 'Comedy' }
],
error: [Error [ValidationError]: "value" must be of type object] {
_original: [ [Object], [Object], [Object] ],
details: [ [Object] ]
}
}

If you want to validate an array containing objects, you could use
const schema = Joi.array().items(Joi.object().keys({
name: Joi.string().min(3).required()
}))

Related

Convert DB response from object to array

I have the following schema:
const mySchema = new mongoose.Schema({
name: String,
subscribers: [ { name: String } ]
})
const myModel = mongoose.model('User', mySchema, 'users')
and I have this code in one of my controllers:
const allOfHisSubscribers = await myModel.findById(req.params.id).select('subscribers')
I want the database response of the await myModel.findByid call to be:
[
{ name: 'x' },
{ name: 'y' },
{ name: 'z' },
]
However, the code above is returning:
{
subscribers: [
{ name: 'x' },
{ name: 'y' },
{ name: 'z' },
]
}
I don't want the JavaScript way of doing it, I know you can use something like Array.prototype.map to get the result, but I am using a middleware, so I can only use the mongoose or MongoDB way of doing it (if possible).
Got it :D, if not, let me know in the comments 🌹

Difficulty using csvtojson to create nested JSON structure from csv

There's a feature in the csvtojson package where you can create a nested JSON object from a CSV file permitting you configure the headers correctly. However, I'm having difficulty applying the schema shown in the above link for my CSV file.
My current CSV structure is this:
And the JSON structure I'm trying to create should look something like this:
{
Name: 'Bedminster Twp East',
Contests: [
{
Name: 'JUSTICE OF THE SUPREME COURT',
Candidates: [
{
Name: 'Maria McLaughlin',
Votes: 511
},
{
Name: 'Kevin Brobson',
Votes: 857
},
{
Name: 'Write-In',
Votes: 1
},
]
},
{
Name: 'JUDGE OF THE SUPERIOR COURT',
Candidates: [
{
Name: 'Timika Lane',
Votes: 494
},
{
Name: 'Megan Sullivan',
Votes: 870
},
{
Name: 'Write-In',
Votes: 1
},
]
}
...
],
Turnout: '45.77%'
},
...
This is the existing code I have:
const csv = require('csvtojson');
const axios = require('axios');
axios({
method: 'get',
url: 'URL'
}).then(function(response) {
let x = response.data.split('\n');
x.splice(0, 2);
let newX = x.join('\n');
csv({
noheader: false,
headers: ['Precinct.Name', 'Precinct.Contests.Name', 'Precinct.Contests.Name.Candidates.Name', 'Precinct.Contests.Name.Candidates.Votes', 'Precinct.Turnout']
}).fromString(newX).then((csvRow) => {
console.log(csvRow);
});
});
Which is unfortunately creating the following JSON structure:
[
{
Precinct: {
Name: 'Bedminster Twp East',
Contests: [Object],
Turnout: '47.89%'
}
},
{
Precinct: {
Name: 'Bedminster Twp East',
Contests: [Object],
Turnout: '47.89%'
}
},
{
Precinct: {
Name: 'Bedminster Twp East',
Contests: [Object],
Turnout: '47.89%'
}
},
{
Precinct: {
Name: 'Bedminster Twp East',
Contests: [Object],
Turnout: '47.89%'
}
},
... 19841 more items
I don't know exactly what is in the "Contests" object, but considering that the number of objects is equal to the number of rows in the existing CSV file, I believe there is something wrong with my header grouping. I can't tell if I'm severely misreading the documentation linked earlier or if it's not clear enough. Thanks for any assistance.
EDIT: I'm splitting and joining because the first two rows are metadata that says "UNOFFICIAL RESULTS" and "2022", which I don't want being read in.
try this headers configuration:
headers: ['Name', 'Contests[0].Name', 'Contests[0].Candidates[0].Name', 'Contests[0].Candidates[0].Votes', 'Turnout']

Why I could still assign ObjectId type value to an Array of String in Typescript?

I'm using node with typescript, and mongoose for database.
I have this code that takes user model from database (using mongoose) by user id, then takes the user roles attribute. The roles attribute contains array of object. Inside each object (roles) contains _id
// Check User Roles
const user = await this.userModel.findById(req.identitas.user._id)
const userRoles: Array<string> = []
for (let index in user.roles) {
userRoles.push((user.roles[index]._id))
}
When i try to check the type of what's pushed to userRoles
console.log(typeof(userRoles[0]))
It says that it has object type variable.
I then decided to try
userRoles.push({ dummy: "dummy" })
and it wouldn't let me do that. So I know for sure that the type declaration in
const userRoles: Array<sring> = []
is working properly. But, at the same time, it isn't. When I try to push _id to userRoles, it still somehow allows me to do that.
This is a problem because I'm using this userRoles array of string to compare it with another string value, using
userRoles.includes("some-string-here")
Why and how did this thing could happen?
*This is what user variable contains:
{
roles: [
{
_id: 5f93fdc**********36,
name: 'Karyawan Cabang Neutron',
...
__v: 2,
policies: [],
organization: 5f8fe9a62*****000082e3fef
}
],
units: [
{
_id: 5fc1f9******00081b2897,
name: 'Neutron Yogyakarta 2',
code: '102',
organization: 5f8fe****42a2000kj13fef,
createdAt: 2020-11-28T07:18:09.628Z,
updatedAt: 2020-11-28T07:18:09.628Z,
__v: 0
}
],
organizations: [
{
_id: 5f8c6b*****b75jg008d4a156,
...
__v: 34,
code: 'NEO',
logo: [Object],
content: [Array],
object: [Object],
config: [Array]
}
],
_id: 5fdad1a25e9f78****c42,
email: 'email#example.com',
username: 'EmiliaBestGirl',
...
isEmailVerified: true,
identities: [],
createdAt: 2020-12-17T03:33:54.484Z,
updatedAt: 2020-12-22T08:12:24.019Z,
__v: 3,
...
}

How can I set the list style to force prompts to be buttons?

I much prefer the look of buttons to a numbered list, but the default handling of prompt in waterfall dialog is to automatically change from buttons (default) to a numbered list after a certain length of content.
I'm currently implementing the prompt like this:
return await step.prompt(FOCUS_AREA_PROMPT, {
prompt: 'Got it. Can you confirm the focus area this is for?',
choices: ChoiceFactory.toChoices(FOCUS_AREAS)
});
I've tried adding a style attribute within this prompt, and also tried adding to the addDialogs line this.dialogs.add(new ChoicePrompt(FOCUS_AREA_PROMPT)); but nothing I have tried has modified the behavior of the options.
I've reviewed the ListStyle enum in the MS docs, but any method I've tried to add these in with has not made any difference. Is there any way to force buttons regardless of content length?
You can set up a choice prompt in the following manner to achieve the buttons look you are seeking. For reference, you can read more about forChannel here.
Alter to match your needs.
Hope of help!
[edit]
Updated below to represent the two ways a Choice Prompt can be assembled and how the value is output (via imBack). When using toChoices, the associated button value is returned in activity.text and in stepContext.result.value (as type Object). When using forChannel, the associated button value is returned in activity.text and in stepContext.result (as type String).
As discussed in the comments, the button title length has a character limit however this is channel specific. When testing in Web Chat, the limit is 20 characters. Adjusting the FOCUS_AREAS "AI & Machine Learning" value (21 chars) to "AI/Machine Learning" (19 chars) results in the choices displaying as buttons and not a list.
Option 1: using toChoices
async choiceStep ( stepContext ) {
const stepResult = stepContext.context.activity.text;
const FOCUS_AREAS = [ 'Chatbots', 'RPA', 'Blockchain', 'AR/VR', 'AI/Machine Learning' ]
if ( stepResult ) {
return await stepContext.prompt( CHOICE_PROMPT, {
prompt: 'Got it. Can you confirm the focus area this is for?',
choices: ChoiceFactory.toChoices( FOCUS_AREAS )
} );
}
}
activity:
{ type: 'message',
id: 'A50eelAPrFIHKv9XeCRm24-o|0000021',
timestamp: 2019-09-25T20:34:30.562Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: [Object],
conversation: [Object],
recipient: [Object],
textFormat: 'plain',
locale: 'en-US',
text: 'Chatbots',
channelData: [Object] },
info:
{ index: 1,
options: {},
reason: 'endCalled',
result:
{ value: 'Chatbots', index: 0, score: 1, synonym: 'Chatbots' },
values: { instanceId: 'c10ed437-77eb-4502-cd24-e89d4c5e45cf' },
onNext: [AsyncFunction: onNext] }
Option 2: using forChannel
async choiceStep ( stepContext ) {
const stepResult = stepContext.context.activity.text;
if ( stepResult ) {
const message = ChoiceFactory.forChannel(
stepContext.context, [
{ value: 'Chatbots', action: { type: 'imBack', title: 'Chatbots', value: 'Chatbots' } },
{ value: 'RPA', action: { type: 'imBack', title: 'RPA', value: 'RPA' } },
{ value: 'Blockchain', action: { type: 'imBack', title: 'Blockchain', value: 'Blockchain' } },
{ value: 'AR/VR', action: { type: 'imBack', title: 'AR/VR', value: 'AR/VR' } },
{ value: 'AI/Machine Learning', action: { type: 'imBack', title: '', value: 'AI/Machine Learning' }, text: 'AI/Machine Learning' },
], `Which do you choose?`
);
await stepContext.context.sendActivity( message );
}
return { status: DialogTurnStatus.waiting };
}
activity:
{ type: 'message',
id: 'Cw5xvHTv6RCDWf3kkyS3Ir-o|0000205',
timestamp: 2019-09-25T20:21:30.320Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: [Object],
conversation: [Object],
recipient: [Object],
textFormat: 'plain',
locale: 'en-US',
text: 'Chatbots',
channelData: [Object] },
info:
{ index: 1,
options: {},
reason: 'continueCalled',
result: 'Chatbots',
values: { instanceId: '4becefed-88d2-773e-6184-91456609a26a' },
onNext: [AsyncFunction: onNext] }

How to save an array of objects into a mongoose db

I am trying to save a leader board made of objects nested in an array. I want to save it in my database, but I have not been able to create the right schema and I don't think I that is the best way to go.
When I run the code I get the error of:
"LeaderBoardSchema is not a constructor".
What is the appropriate way of creating a schema that I need.
I have tried many variations looking online, but I keep getting the " LeaderBoardSchema is not a constructor".
The examples from other questions on S.O have not been able to help me much.
// leaderboard object that i want to save
leaderBoard = [
leaderOne= {
name: 'Ultimate Beast',
score: 2
},
leaderTwo= {
name: 'Masked Titan',
score: 9
},
leaderThree= {
name: 'Oranolio',
score: 7
},
leaderFour= {
name: 'Popularkiya',
score:1
},
leaderFive= {
name: 'Bootecia',
score: 11
},
];
// Database schema
const Schema = mongoose.Schema()
const LeaderBoardSchema = new mongoose.Schema({
leaderBoard:{
leaderOne : {
name: String,
score: Number
},
leaderTwo : {
name: String,
score: Number
},
leaderThree : {
name: String,
score: Number
},
leaderFour : {
name: String,
score:Number
},
leaderFive : {
name: String,
score: Number
}
}
}, {collection: 'leaderboard-data'});
const PlayerData = mongoose.model('LeaderBoard Data', LeaderBoardSchema);
// My attempt
const leaderBoardToSave = new LeaderBoardSchema({
leaderBoard:{
leaderOne : {
name: 'asdf',
score: 12
},
leaderTwo : {
name: 'sfgh',
score: 12
},
leaderThree : {
name: 'weh',
score: 12
},
leaderFour : {
name: 'asdf',
score:12
},
leaderFive : {
name: 'asdf',
score: 12
}
}
})
Currently your leaderBoard field is an object. To model an array of objects do the following with your schema:
const LeaderBoardSchema = new mongoose.Schema({
leaderBoard: [
{
name: String,
score: Number
}
]
}, {collection: 'leaderboard-data'});
as for the issue with the schema constructor. You're creating the mongoose model as follows const PlayerData = mongoose.model('LeaderBoard Data', LeaderBoardSchema);. But then you do new LeaderBoardSchema({...}). You need to use the mongoose model PlayerData instead. so to create a new leaderboard:
const leaderBoardToSave = new PlayerData({
leaderBoard: [
{
name: 'asdf',
score: 12
},
{
name: 'gds',
score: 12
},
{
name: 'adad',
score: 12
},
]
})

Resources