Pass value to Json schema Definition - javascript

I'm using AJV Schema validator on NodeJS and wish to add userID from sessionObject to every incoming payload so that I can store userID for each transaction.
I wish to know if that can be done in json schemas.
sample Incoming Client payload -
Client: {
Client_Id: 12,
ClientName: 'jon',
Address: [{
Address_Id: 22,
Street: 'Sesimi Street',
City: 'Mumbai'
}
],
Contact: [{
Contact_Id: 23,
Phone: 11111111,
Email: "jon#doe.com"}]
Desired object post schema Validation -
Client: {
Client_Id: 12,
ClientName: 'jon',
UserId: 12121,
Address: [{
Address_Id: 22,
Street: 'Sesimi Street',
City: 'Mumbai',
UserId: 12121
}
],
Contact: [{
Contact_Id: 23,
Phone: 11111111,
Email: "jon#doe.com",
UserId: 12121
}]
Since incoming payloads are huge, I believe it would be best to do assignments at schema validation point instead of doing recursive operations inside of my application. I'm open to suggestions.
Thank you

You can try useDefaults feature of AJV.
var ajv = new Ajv({ useDefaults: true });
var schema = {
"type": "object",
"properties": {
"ClientName": { "type": "string" },
"UserId": { "type": "integer", "default": 12121 }
}
};
var data = { "ClientName": "jon" };
var validate = ajv.compile(schema);
console.log(validate(data)); // true
console.log(data); // { "ClientName": "jon", "UserId": 12121 }
To have flexibility on default value you can use dynamicDefaults with callback function. Example:
var uuid = require('uuid');
function getUuid(args) {
var version = 'v' + (arvs && args.v || 4);
return function() {
return uuid[version]();
};
}
var definition = require('ajv-keywords').get('dynamicDefaults').definition;
definition.DEFAULTS.uuid = getUuid;
var schema = {
dynamicDefaults: {
id1: 'uuid', // v4
id2: { func: 'uuid', v: 4 }, // v4
id3: { func: 'uuid', v: 1 } // v1
}
};

Thankyou #vearutop for your answer helped me solve my problem.
All I did was use a custom function that returns UserId from sessionObject
var Ajv = require("ajv");
var ajv = new Ajv({useDefaults: true,});
require("ajv-keywords")(ajv);
var definition = require('ajv-keywords/keywords/dynamicDefaults').definition;
definition.DEFAULTS.getuser = getuser;
function getuser() {
return sessionObject.UserId;
}
var schema = {
dynamicDefaults: {
user: 'getuser'
},
properties: {
user: {}
}
};

Related

Typescript/NestJS - extract object from db query response

I'm using external API to make an SQL query for a user. As a result i get matching Entity but as a set of fields, lookin like this:
[
{ IsNull: false, Name: 'Key', Value: '897', Values: null },
{ IsNull: false, Name: 'FirstName', Value: 'User', Values: null },
{ IsNull: false, Name: 'LastName', Value: 'Portal', Values: null },
{
IsNull: false,
Name: 'Email',
Value: 'some#email.com',
Values: null
},
{ IsNull: true, Name: 'Salutation', Value: null, Values: null },
{ IsNull: false, Name: 'Type', Value: '2', Values: null },
{
IsNull: false,
Name: 'LastLoggedDate',
Value: '2022-12-01 15:24:03',
Values: null
}
]
How to transform this response to end with simple object { email: 'some#email', firstName: 'User' lastName: 'Portal' } ??
I ended up with solution like this (below) but i believe there's some easiest way to do that, especially with more fields
let userRawEntity = queryResult.data.Entities[0].Fields;
const userEmail = userRawEntity.filter((obj) => { return obj.Name === 'Email' });
const userFirstName = userRawEntity.filter((obj) => { return obj.Name === 'FirstName' });
const userLastName = userRawEntity.filter((obj) => { return obj.Name === 'LastName' });
return { email: userEmail[0].Value, firstName: userFirstName[0].Value, lastName: userLastName[0].Value };
Edit:
final solution that works and looks nicer. thanks for help :)
if (queryResult.data.TotalEntityCount > 0) {
let user: {[key: string]: string | null } = {}
let userRawEntity = queryResult.data.Entities[0].Fields;
userRawEntity.forEach(data => user[data.Name] = data.Value);
return { email: user.Email, currency: user.Currency } as JwtPayload;
}
As a starting point, I would transform that entire array into an object as follows:
let dataTransformed: {[key: string]: string | null} = {}
data.forEach(d => {
dataTransformed[d.Name] = d.Value
})
Which will give you a nicer looking object as follows:
{
"Key": "897",
"FirstName": "User",
"LastName": "Portal",
"Email": "some#email.com",
"Salutation": null,
"Type": "2",
"LastLoggedDate": "2022-12-01 15:24:03"
}
You now have a familiar object with which to work with. From here, you can strip out the entries you don't want. Note, you may want to do further work in that array transformation such as checking for null values, changing keys to camel case, etc...
Another approach using lodash
_.mapValues(_.keyBy(data, "Name"), o => o.Value || o.Values);

i have objects in array in mongo db i want to delete all the objects but always left one object with{ id:_oldxxxxxxxx}

I have some code in javascript with moongooes that I used in mongo DB to store a data
Sometimes I need to delete all the objects in array
and get a clean array
this is my schema
const orderSchema =new Schema({
date: {
type: Date,
},
OrderNumber: {
type: String,
required: true
},
City: {
type: String,
required: true
},
Address: {
type: String,
required: true
},
Phone: {
type: String
},
Country: {
type: String
},
Name: {
type: String,
required: true
},
Trackingnumber: {
type: String
},
ZipCode: {
type: Number
},
Province: {
type: String,
},
fulfillmentOrders:{
type: String,
},
Quantity: {
},
});
Holde:[
orderSchema
],
module.exports = mongoose.model('User', userSchema);
and my data on mongo looks like this
"Holde": [
{
"OrderNumber": "gid://shopify/Order/4958122475753",
"City": "xxxx",
"Address": "xxxx",
"Phone": "",
"Country": "xxx",
"Name": "xxx",
"Trackingnumber": "0",
"ZipCode": xxxx,
"fulfillmentOrders": "gid://shopify/FulfillmentOrder/6034089509097",
"Quantity": [
{
"quantity": 1,
"product": {
"id": "gid://shopify/Product/7909915590889"
},
"variant": {
"sku": "11111"
}
}
],
"_id": {
"$oid": "6389b12faaade0788141bf4f"
}
I try to delete all the objects in my array
whit this code
const User = require('../model/User');
const foundUse= await User.findOne({ "user":req.body.user}).exec();
await foundUse.updateOne({
Holde :{
$pull: {'_id':6389b12faaade0788141bf4f},
}
},
)
and expect to get "hold":[]
but actually
I get
"Holde": [
{
"_id": {
"$oid": "6389d882afbc458cc1c1af23"
}
}
],
It's pretty normal because you are updating your User with theses data.
With mongoose, the way to propperly delete item is .deleteMany(), but in your case it will only delete the User (and you seems to want to delete only the order).
You can then filter user's orders and re-assign it without the found order, using FindOneAndUpdate, like:
const User = require('../model/User');
const foundUser = await User.findOne({ "user": req.body.user });
const result = await User.updateOne({
Holde: foundUser?.Holde.filter(holde => holde._id !== HOLD_ID_TO_DELETE)
});
Where HOLD_ID_TO_DELETE is the id to delete (you seems to pass the whole user object with all orders at the moment)
But not it would be prettier and more maintenable to create an Order collection, linked to your User one using ref.

Why aren't my GraphQL queries calling my resolvers

I'm following this tutorial on the Apollo blog (here's my forked repo), and I've been going over this for a solid day, and still can't figure out why my resolvers aren't being used, so turning to help here. As near as I can tell, I've tried it exactly as the tutorial claims.
I was able to return data from mocks, so everything up to that point was working.
Here's the simple schema.js:
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
//import mocks from './mocks';
import { resolvers } from "./resolvers";
const typeDefs = `
type Author {
id: Int
firstName: String
lastName: String
posts: [Post]
}
type Post {
id: Int
title: String
text: String
views: Int
author: Author
}
type Query {
getAuthor(firstName: String, lastName: String): Author
allAuthors: [Author]
}
`;
const schema = makeExecutableSchema({ typeDefs, resolvers });
// addMockFunctionsToSchema({ schema, mocks, preserveResolvers: true});
export default schema;
And my resolvers.js file:
const resolvers = {
Query: {
getAuthor(_, args) {
console.log("Author resolved!");
return ({ id: 1, firstName: "Hello", lastName: "world" });
},
allAuthors: () => {
return [{ id: 1, firstName: "Hello", lastName: "world" }];
}
},
Author: {
posts: (author) => {
return [
{ id: 1, title: 'A post', text: 'Some text', views: 2 },
{ id: 2, title: 'A different post', text: 'Different text', views: 300 }
];
}
},
Post: {
author: (post) => {
return { id: 1, firstName: 'Hello', lastName: 'World' };
}
}
};
export default resolvers;
Given that I'm returning static objects, there's no need worry about syncronicity, so any ideas on why my query:
query {
getAuthor {
firstName
}
}
is returning null?
{
"data": {
"getAuthor": null
}
}
Change:
Query: {
getAuthor(_, args) {
console.log("Author resolved!");
return ({ id: 1, firstName: "Hello", lastName: "world" });
},
to:
Query: {
getAuthor(root, {_, args}) {
console.log("Author resolved!");
return ({ id: 1, firstName: "Hello", lastName: "world" });
},
root is not really well documented but you have to include it when you are doing queries on base schema types. You are trying to return an Author type which is an entry point into the GraphQL API because getAuthor is a root query, so therefore you must include root.
Check out https://www.howtographql.com/graphql-js/2-a-simple-query/. Read the section called:
The query resolution process
It explains the root object a little better.

Multiple schema references in single schema array - mongoose

Can you populate an array in a mongoose schema with references to a few different schema options?
To clarify the question a bit, say I have the following schemas:
var scenarioSchema = Schema({
_id : Number,
name : String,
guns : []
});
var ak47 = Schema({
_id : Number
//Bunch of AK specific parameters
});
var m16 = Schema({
_id : Number
//Bunch of M16 specific parameters
});
Can I populate the guns array with a bunch of ak47 OR m16? Can I put BOTH in the same guns array? Or does it require a populate ref in the assets array, like this, which limits it to a single specific type?
guns: [{ type: Schema.Types.ObjectId, ref: 'm16' }]
I know I could just have separate arrays for different gun types but that will create an insane amount of extra fields in the schema as the project scales, most of which would be left empty depending on the loaded scenario.
var scenarioSchema = Schema({
_id : Number,
name : String,
ak47s : [{ type: Schema.Types.ObjectId, ref: 'ak47' }],
m16s: [{ type: Schema.Types.ObjectId, ref: 'm16' }]
});
So back to the question, can I stick multiple schema references in a single array?
What you are looking for here is the mongoose .discriminator() method. This basically allows you to store objects of different types in the same collection, but have them as distinquishable first class objects.
Note that the "same collection" principle here is important to how .populate() works and the definition of the reference in the containing model. Since you really can only point to "one" model for a reference anyway, but there is some other magic that can make one model appear as many.
Example listing:
var util = require('util'),
async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/gunshow');
//mongoose.set("debug",true);
var scenarioSchema = new Schema({
"name": String,
"guns": [{ "type": Schema.Types.ObjectId, "ref": "Gun" }]
});
function BaseSchema() {
Schema.apply(this, arguments);
// Common Gun stuff
this.add({
"createdAt": { "type": Date, "default": Date.now }
});
}
util.inherits(BaseSchema, Schema);
var gunSchema = new BaseSchema();
var ak47Schema = new BaseSchema({
// Ak74 stuff
});
ak47Schema.methods.shoot = function() {
return "Crack!Crack";
};
var m16Schema = new BaseSchema({
// M16 Stuff
});
m16Schema.methods.shoot = function() {
return "Blam!!"
};
var Scenario = mongoose.model("Scenario", scenarioSchema);
var Gun = mongoose.model("Gun", gunSchema );
var Ak47 = Gun.discriminator("Ak47", ak47Schema );
var M16 = Gun.discriminator("M16", m16Schema );
async.series(
[
// Cleanup
function(callback) {
async.each([Scenario,Gun],function(model,callback) {
model.remove({},callback);
},callback);
},
// Add some guns and add to scenario
function(callback) {
async.waterfall(
[
function(callback) {
async.map([Ak47,M16],function(gun,callback) {
gun.create({},callback);
},callback);
},
function(guns,callback) {
Scenario.create({
"name": "Test",
"guns": guns
},callback);
}
],
callback
);
},
// Get populated scenario
function(callback) {
Scenario.findOne().populate("guns").exec(function(err,data) {
console.log("Populated:\n%s",JSON.stringify(data,undefined,2));
// Shoot each gun for fun!
data.guns.forEach(function(gun) {
console.log("%s says %s",gun.__t,gun.shoot());
});
callback(err);
});
},
// Show the Guns collection
function(callback) {
Gun.find().exec(function(err,guns) {
console.log("Guns:\n%s", JSON.stringify(guns,undefined,2));
callback(err);
});
},
// Show magic filtering
function(callback) {
Ak47.find().exec(function(err,ak47) {
console.log("Magic!:\n%s", JSON.stringify(ak47,undefined,2));
callback(err);
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
And output
Populated:
{
"_id": "56c508069d16fab84ead921d",
"name": "Test",
"__v": 0,
"guns": [
{
"_id": "56c508069d16fab84ead921b",
"__v": 0,
"__t": "Ak47",
"createdAt": "2016-02-17T23:53:42.853Z"
},
{
"_id": "56c508069d16fab84ead921c",
"__v": 0,
"__t": "M16",
"createdAt": "2016-02-17T23:53:42.862Z"
}
]
}
Ak47 says Crack!Crack
M16 says Blam!!
Guns:
[
{
"_id": "56c508069d16fab84ead921b",
"__v": 0,
"__t": "Ak47",
"createdAt": "2016-02-17T23:53:42.853Z"
},
{
"_id": "56c508069d16fab84ead921c",
"__v": 0,
"__t": "M16",
"createdAt": "2016-02-17T23:53:42.862Z"
}
]
Magic!:
[
{
"_id": "56c508069d16fab84ead921b",
"__v": 0,
"__t": "Ak47",
"createdAt": "2016-02-17T23:53:42.853Z"
}
]
You can also uncomment the mongoose.set("debug",true) line in the listing to see how mongoose is actually constructing the calls.
So what this demonstrates is that you can apply different schemas to different first class objects, and even with different methods attached to them just like real objects. Mongoose is storing these all in a "guns" collection with the attached model, and it will contain all "types" refernced by the discriminator:
var Gun = mongoose.model("Gun", gunSchema );
var Ak47 = Gun.discriminator("Ak47", ak47Schema );
var M16 = Gun.discriminator("M16", m16Schema );
But also each different "type" is referenced with it's own model in a special way. So you see that when mongoose stores and reads the object, there is a special __t field which tells it which "model" to apply, and hence attached schema.
As one example we call the .shoot() method, which is defined differently for each model/schema. And also you can still use each as a model by itself for queries or other operations, since Ak47 will automatically apply the __t value in all query/upates.
So though the storage is in one collection it can appear to be many collections, but also has the benefit of keeping them together for other useful operations. This is how you can apply the kind of "polymorphism" you are looking for.
I post my solution to this question. With the same concept of using discriminators
baseSchema: Main model collection that contains the array field with mutiples schemas
itemSchema: Schema parent for discriminator use
fizzSchema & buzzSchema: Multiples schemas are wanted to use in array
Model
const itemSchema = Schema({
foo: String,
}, { discriminatorKey: 'kind', _id: false});
const fizzSchema = Schema({
fizz: String,
}, { _id: false });
const buzzSchema = Schema({
buzz: String,
}, { _id: false });
const baseSchema = Schema({
items: [itemSchema],
});
baseSchema.path('items').discriminator('fizz', fizzSchema);
baseSchema.path('items').discriminator('buzz', buzzSchema);
const List = model('list', baseSchema);
Testbench
const body = {
items: [
{ foo: 'foo'},
{ foo: 'foo', fizz: 'fizz'},
{ foo: 'foo', kind: 'fizz', fizz: 'fizz'},
{ foo: 'foo', kind: 'buzz', buzz: 'buzz'},
{ kind: 'buzz', buzz: 'buzz'},
]
};
const doc = new List(body);
console.log(doc);
Output
{
items: [
{ foo: 'foo' },
{ foo: 'foo' },
{ fizz: 'fizz', foo: 'foo', kind: 'fizz' },
{ buzz: 'buzz', foo: 'foo', kind: 'buzz' },
{ buzz: 'buzz', kind: 'buzz' }
],
_id: new ObjectId("626a7b1cf2aa28008d2be5ca")
}
Can be executed here: https://replit.com/#Gabrirf/Mongoose

How to save form data as an objects and array of objects to mongodb using loop in node.js?

I am new to MongoDB, I am using Node.js, Express 4 and mongoose(mongoDB) for my project.I stuck to save form data to mongoDB within loop and my model contains Objects and array of objects as well.
Model :
var Subscriber = new Schema({
first_name: String,
emergency_contact_1: {
name: String,
number: [{
type: String
}]
},
residential: {
phone: String,
address: String,
...
},
medications: [
{
visit_id: { type: Object },
name: String,
....
}],
food_allergies: [
{type: String}
],
....
});
Controller :
I want to save data in this way:
var subscriber = new Subscriber();
//Here I am trying to save all form's fields to mongoBD fields.
for (var field in form_data) {
subscriber[field] = form_data[field];
}
subscriber.save(function (err1, instance) {
if (err) {
console.log("error");
return res.send("...");
}
console.log("saved successfully");
}
Normal fields are getting saved properly using above loop but when objects or arrays came then it won't get save to mongoDB.
Any solution ? or any other way to insert/save data through loop to mongoDB model ?
Any help would be appreciated.Thank you..!!
Nodejs
var PersonSchema = new Schema({
name: {
given: {
type: String,
required: true
},
family: {
type: String,
required: true
}
},
children: [PersonSchema]
});
var Person = mongoose.model('Person', PersonSchema);
app.post('/person', function (req, res) {
Person.create(req.body)
.then(function (created) {
console.log(created);
res.json(created.id);
});
});
Client
$.ajax({
url: '/person',
type: 'POST',
data: {
name: {
family: 'Green'
},
children: [{
name: {
given: 'Matt',
family: 'Green'
}
}, {
name: {
given: 'Dave',
family: 'Green'
}
}]
}
})
As you can see, I have nested objects and arrays. This works fine for me :)

Categories