How to prevent unwanted request in node js express? - node.js

expected request:
{
"name": "Raju",
"email": "email#email.com"
}
Actual:
{
"name": "Raju",
"email": "email#email.com",
"xyz" : "xxxx"
}
I would like to throw an error or escape for "xyz" in a validation/router level.
Am using the fastest-validator.
is there any other validator supports this feature?
Any help appreciated

You can set $$strict: true like below example:
const schema = {
name: { type: "string" }, // required
$$strict: true // no additional properties allowed
}
v.validate({ name: "John" }, schema); // Valid
v.validate({ name: "John", age: 42 }, schema); // Fail

Related

How can I cast a string to number in an response using axios?

I am using Axios to execute a GET request to a public API. This API return a numeric value as string. I need to cast this value a numeric value. My application runs using tsc, so I expect the result of my object to be a numeric value, but it's not.
Axios Response
[
{
"name": "foo1",
"value": "8123.3000"
},
{
"name": "foo2",
"value": "5132.2003"
},
{
"name": "foo3",
"value": "622.0000"
}
]
Expected Output
[
{
"name": "foo1",
"value": 8123.3
},
{
"name": "foo2",
"value": 5132.2003
},
{
"name": "foo3",
"value": 622
}
]
My code is very simple,
interface MyObj {
myString: string;
myNumber: number;
}
(async () => {
let { data }: AxiosResponse<MyObj> = await axios.get<MyObj>("/public/data");
console.log(data);
})();
I try to use interface, class, the interface Number. Nothing worked.
I leave an example of code to try it.
How can I get the expected output without manually converting each value one by one?
Axios does not change the type of properties in the response. Please verify that the server does not send you the wrong types.
Edit
From your comment, it seems that the server sends you the vslue as string instead of as number. In this case I would suggest working with Ajv (https://github.com/ajv-validator/ajv) so you can create a schema that describes how the response looks like. Ajv cn also transform the value from string to number for you:
const Ajv = require('ajv')
const ajv = new Ajv({
// allow chaning the type of some values from type X to type Y. depends on the source and target type:
// https://ajv.js.org/guide/modifying-data.html#coercing-data-types
// X => Y rules: https://ajv.js.org/coercion.html
coerceTypes: true,
})
const schema = {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'number' },
},
required: ['name', 'value'],
additionalProperties: false,
},
}
const data = { name: 1, value: '1.1' }
console.log(typeof data.value === 'number') // false!
const valid = ajv.validate(schema, data)
if (!valid) {
console.log(ajv.errors)
process.exit(1)
}
console.log(typeof data.value === 'number') // true!
When declaring that your axios request returns a certain type; this will be checked at compile time and syntax checking. It does not however do this at runtime. If you know that the axios request is returning something different than your interface, you need to convert to that format first. You can do this using the second argument of the JSON.parse function like so:
interface Item {
name: string;
value: number;
}
let responseString = `
[
{
"name": "foo1",
"value": "8123.3000"
},
{
"name": "foo2",
"value": "5132.2003"
},
{
"name": "foo3",
"value": "622.0000"
}
]`
const items: Item[] = JSON.parse(responseString, (key, value) => {
const propertiesToCast = ["value"] // Which properties should be converted from string to number
if (propertiesToCast.includes(key)) {
return parseFloat(value)
}
return value
});
console.log(items)

Mongoose NodeJS Express - How to Push Data To a Specific Sub Document Object Array

thank you in advance for any help.
My problem is essentially to add data to a specific sub document.
I have the following models in my NodeJS server:
MODELS
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const dataSchema = new Schema({
time: Date,
value: String
});
const nodeSchema = new Schema({
name: String,
description: String,
number: Number,
status: String,
lastSeen: Date,
data: [dataSchema]
});
const siteSchema = new Schema({
code: String,
name: String,
description: String,
totalNodes: Number,
nodes: [nodeSchema]
});
const Site = mongoose.model('site',siteSchema);
module.exports = Site;
Which basically looks like this:
Example
{
"_id": "5fa169473a394829bc485069",
"code": "xfx3090",
"name": "Name of this site",
"description": "Some description",
"totalNodes": 2,
"__v": 0,
"nodes": [
{
"_id": "5fa1af361e085b516066d7e2",
"name": "device name",
"description": "device description",
"number": 1,
"status": "Offline",
"lastSeen": "2020-11-03T19:27:50.062Z",
"data": [
{
"Date": "2019-01-01T00:00:00.000Z",
"value": "12"
},
{
"Date": "2019-01-01T00:00:00.000Z",
"Value": "146"
}
]
},
{
"_id": "5fa1b10f4f24051520f85a58",
"name": "device name",
"description": "device description",
"number": 2,
"status": "Offline",
"lastSeen": "2020-11-03T19:35:43.409Z",
"data": [
{
"Date": "2019-01-01T00:00:00.000Z",
"Value": "555"
}
]
}
]
}
]
As you can see I have created two dummy nodes with some random data.
My question now is, say I want to add some data to Node 1. How will this code look?
I've tried many variations and attempted many different things without any luck. I know this would be easier by using the Object Id's, but I was hoping there is a way around this.
My Best result so far was with this code, but unfortunately it doesn't add any data.
addNodeData: async (req,res,next) => {
const {siteCode} = xfx3090; //req.params
const { nodeNumber } = 1; //req. params - just to show example
const nodeData = await Site.findOneAndUpdate({'code': siteCode, 'node.number': nodeNumber}, {$push: {'data':{'time': Date.now(), 'value':1223}}});
res.status(200).json({message:'success'});
}
Thank you in advance!
You need the positional operator $.
The query you want is something like this:
db.collection.update({
"_id": "5fa169473a394829bc485069",
"nodes._id": "5fa1af361e085b516066d7e2"
},
{
"$push": {
"nodes.$.data": {
"Date": "newDate",
"value": "newValue"
}
}
})
The first part is to find the document. I'm assuming nodes._id is not unique so I match _id too.
Then, with the pointer in the document you want to add the new data, you use $push into nodes.$.data. So, in the filed data there will be a new object.
A mongo plauground example is here

Apollo Server Express + GraphQL relationships

I am using Express + Apollo Server + GraphQL + Mongoose + MongoDB to "perform" several CRUD operations on a database.
One of the operations I am trying to make is to get the sites from the database and expand its users with their information for each record like this:
query {
getSites {
id
name
owner {
name
email
}
origins
}
}
Instead, I am getting these results:
{
"data": {
"getSites": [{
"id": "5cae36182ab9b94e94ba9af5",
"name": "Test site 1",
"owner": [{
"name": null,
"email": null
}
],
"origins": [
"test1",
"test2"
]
}, {
"id": "5cae3a3798c302247c036544",
"name": "Test site 2",
"owner": [{
"name": null,
"email": null
}
],
"origins": [
"test1",
"test2"
]
}
]
}
}
This is my typeDef code for Site:
import { gql } from 'apollo-server-express';
const site = gql `
extend type Site {
id: ID!
name: String!
origins: [String]
owner: [User]
createdOn: String
updatedOn: String
}
extend type Query {
getSites: [Site]
getSite(id: ID!): Site
}
extend type Mutation {
addSite(name: String!, owner: [String!], origins: [String]): Site
}
`;
export default site;
If I console.log(sites) I see owner is an array of Strings.
Edit:
If I change addSite(name: String!, owner: [User], origins: [String]): Site then I get when compiling:
Error: The type of Mutation.addSite(owner:) must be Input Type but got: [User]
My resolver looks like this:
getSites: async () => await Site.find().exec()
What's the proper way to define relationships today? Thanks.
I just edited my resolver to this:
getSites: async () => {
let sites = await Site.find().exec();
let ownedSites = await User.populate(sites, { path: 'owner' });
return ownedSites;
}
And that solved the errors.

How to set include_type_name in postman to true

I am creating a mapping index in postman and am getting the error below.
I am trying to create an index that would communicate with my elastic search firebase server using a function I have created index.js. What could be the problem?
Here is my code
"mappings":{
"properties":{
"city":{
"type": "text"
},
"contact_email":{
"type": "text"
},
"country":{
"type": "text"
},
"description":{
"type": "text"
},
"image":{
"type": "text"
},
"post_id":{
"type": "text"
},
"state_province":{
"type": "text"
},
"title":{
"type": "text"
}
}
}
}
Here is the code to index.js function
const functions = require('firebase-functions');
const request = require('request-promise')
exports.indexPostsToElastic = functions.database.ref('/posts/{post_id}')
.onWrite((change,context) =>{
let postData = change.after.val();
let post_id = context.params.post_id;
console.log('Indexing post',postData);
let elasticSearchConfig = functions.config().elasticsearch;
let elasticSearchUrl = elasticSearchConfig.url + 'posts/' + post_id;
let elasticSearchMethod = postData ? 'POST' : 'DELETE';
let elasticSearchRequest = {
method:elasticSearchMethod,
url: elasticSearchUrl,
auth:{
username : elasticSearchConfig.username,
password : elasticSearchConfig.password,
},
body: postData,
json : true
};
return request(elasticSearchRequest).then(response => {
console.log("ElasticSearch response", response);
return response;
});
});
I am getting the following error in firebase
StatusCodeError: 400 - {"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"Rejecting mapping update to [posts] as the final mapping would have more than 1 type: [_doc, -LcajBAxUn3jrq0EHzYa]"}],
Here is a sample of my database in firebase
-LcajBAxUn3jrq0EHzYa
city:
contact_email:
country:
description:
image:
post_id:
price:
state_province:
title:
Please help me identify where my error could be.
include_type_name is an elasticsearch flag introduced in last few releases. See background here. Its goal is to address the long confusion of elasticsearch type != tableName. To include include_type_name in your requests, see this.
This is more of a Elasticsearch API problem other than firebase / postman.
You firstly need to check with the elasticSearch version you are using.
If you are using version less than 7.0 then you have to enter your properties tag inside "_doc" field. The "_doc" field is later superseded in the elasticSearch versions higher than 7.0 . Below is the reformed mapping.
"mappings":{
"_doc" :{
"properties":{
"city":{
"type": "text"
},
"contact_email":{
"type": "text"
},
"country":{
"type": "text"
},
"description":{
"type": "text"
},
"image":{
"type": "text"
},
"post_id":{
"type": "text"
},
"state_province":{
"type": "text"
},
"title":{
"type": "text"
}
}
}
}
}

Mongoose Mixed type field not getting populated

I have a json doc that has embedded document which is variable (but in json format). So I use Mixed Schema type.
When I save, everything works fine except the mixed type object doesn't get populated and won't save.
What am I doing wrong here ?
Updating --> What I mean is - everything works as expected except the data node (which is suppose to be of mixed type)
My Document Example:
{
"data": {
"user_name": "username",
"cart_items": [
{
"sku": "ABCD",
"msrp": 1250.25,
"discount": 10,
"final_price": 112.22
},
{
"sku": "PQRSDF",
"msrp": 12.25,
"discount": 10,
"final_price": 1.2
}
]
},
"template_id": "1",
"from": "x#gmail.com",
"send_status": 0,
"priority": 99,
"app_id": "app3",
"_id": "532a54aa1c76fba0874c48ea",
"bcc": [],
"cc": [],
"to": [
{
"name": "acv",
"email": "x#outlook.com"
},
{
"name": "pem",
"email": "y#gmail.com"
}
],
"call_details": {
"data_id": "01234",
"event_id": 25
}
}
code to insert:
Schema definition:
app_id : { type: String, trim: true },
priority: { type: Number},
send_status: { type: Number},
call_details : {
event_id : { type: Number},
data_id : { type: String, trim: true },
id : false
},
from : { type: String, trim: true },
to : [addressSchema],
cc : [addressSchema],
bcc : [addressSchema],
template_id : { type: String, trim: true },
data: { any: {} }
Code:
r.app_id = req.body.app_id;
r.priority= req.body.priority;
r.send_status= req.body.send_status;
r.call_details.event_id= req.body.call_details.event_id;
r.call_details.data_id= req.body.call_details.data_id;
r.from= req.body.from;
r.to = populate_address(req.body.to);
r.cc = populate_address(req.body.cc);
r.bcc = populate_address(req.body.bcc);
r.template_id= req.body.template_id;
r.data =req.body.data);
r.markModified('data');
r.save(function (err){
console.log("add");
res.send ("added");
});
As you currently define your schema, it will only save the any field within data.
Remove the any embedded field from the definition for data in your schema.
So instead of:
data: { any: {} }
Use:
data: {}
As mongoose does not handle the embedded document save automatically. You need to save the embedded document first and assign the object ID to the parent schema as reference.

Resources