Declaring multiple relayjs connections - javascript

I have to two GraphQLObjectType
const AType = new GraphQLObjectType({
name: 'A'
fields: () => ({
id: globalIdField('A', obj => obj._id),
Id: {
type: GraphQLID,
resolve: obj => obj._id
},
email: { type: GraphQLString },
name: { type: GraphQLString },
status: { type: GraphQLString },
description: { type: GraphQLString }
}),
interfaces: [nodeInterface]
});
and another Type BType, i am using AType inside the type of so t
const BType = new GraphQLObjectType({
name: 'BType',
fields: {
id: globalIdField('BType'),
ApplicantsDetails: {
type: AType,
resolve: obj => {
return obj.applicantsId;
}
},
respo_2: {
type: GraphQLString,
resolve: obj => "I have declared a ObjectType inside another"
}
},
interfaces: [nodeInterface]
});
and the main type from where i am returning a promise, so when i return the promise resolve(d), it should go to the BType and
const { connectionType: EmployerDashBoardConnectionType} =
connectionDefinitions({
name: 'EmployerDash',
nodeType: BType
});
above is the connection
EmployerDashBoardConnection: {
type: EmployerDashBoardConnectionType,
description: 'Employer DashBoard Details',
args: connectionArgsWithJobId,
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(d);
}
});
});
}
}
/* Introduce your new fields here */
}
the response from Dash() function call is
{
applicantsId:
{ _id: 5878548b51179817f48eb1f1,
email: '123#gmail.com',
password: '$2a$10$lDpfl7kL4i/8VPij8aypmeeiD1794g1afACUxca397LdlErMgWa.S',
__v: 0,
name: 'Alpaina',
status: 'Unemployed',
isJobSeeker: true,
to: 2017-01-13T04:16:11.755Z }
}
it only prints null

for using edges you need to pass an array to resolve function
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
// if d is object then
const a = [];
a.push(d);
resolve(connectionFromArray(a, args));
}
});
});
}
this code is to only solve your current problem
NOTE: while using relay connections you should always resolve a list [ ]

There are two problems in your code:
First problem:
The node type of EmployerDashBoardConnectionType is BType. So, the items returned in resolve() function of field EmployerDashBoardConnection should have the same properties - that's not the case in your code.
The structure of BType is:
{
id
ApplicantsDetails {
id,
Id,
email,
name,
status,
description,
}
respo_2,
}
whereas you are passing the following object, which totally does not match.
{
applicantsId: {
_id: 5878548b51179817f48eb1f1,
email: '123#gmail.com',
password: '$2a$10$lDpfl7kL4i/8VPij8aypmeeiD1794g1afACUxca397LdlErMgWa.S',
__v: 0,
name: 'Alpaina',
status: 'Unemployed',
isJobSeeker: true,
to: 2017-01-13T04:16:11.755Z
}
}
Second problem:
This is why you're getting null for edges. Your resolve() function:
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(d);
}
});
});
}
where you return nothing. You can use connectionFromArray from graphql-relay npm module:
resolve: (_, args, auth) => {
return new Promise((resolve, reject) => {
Dash(_, args, auth, function (err, d) {
if (err) {
reject(err);
} else {
resolve(connectionFromArray(d, args));
}
});
});
}
The value of d must be a list where each item should have top level properties id, ApplicantsDetails, respo_2.

Related

Sequelize update instance and got "SyntaxError: "undefined" is not valid JSON"

I'm using Sequelize with expressjs for my api, I have a model named "Order" for the orders of the website im currently making, this is my model order:
module.exports = (sequelize, Sequelize) => {
const Order = sequelize.define("order", {
uid: {
type: Sequelize.STRING
},
author: {
type: Sequelize.TEXT('long'),
get: function() {
return JSON.parse(this.getDataValue('author'));
},
set: function(author) {
this.setDataValue('author', JSON.stringify(author));
}
},
cart: {
type: Sequelize.TEXT('long'),
get: function() {
return JSON.parse(this.getDataValue('cart'));
},
set: function(cart) {
this.setDataValue('cart', JSON.stringify(cart));
}
},
delivery: {
type: Sequelize.TEXT('long'),
get: function() {
return JSON.parse(this.getDataValue('delivery'));
},
set: function(delivery) {
this.setDataValue('delivery', JSON.stringify(delivery));
}
},
shipmethod: {
type: Sequelize.STRING
},
paymethod: {
type: Sequelize.STRING
},
totalprice: {
type: Sequelize.DECIMAL
},
cryptoprice: {
type: Sequelize.DECIMAL
},
payed: {
type: Sequelize.DECIMAL
},
promoCode: {
type: Sequelize.STRING
},
status: {
type: Sequelize.STRING
}
});
return Order;
}
All was working good, until I try to update an order, like this:
router.post('/payment/crypto', async (req, res) => {
const {order, currency} = req.body;
if (!order, !currency) return res.status(404).json({error: 'not found'});
console.log(order);
console.log(currency);
try {
const orderDb = await db.orders.findOne({where:{uid: order}});
if (!orderDb) return res.status(404).json({error: 'not found'});
const cryptoPrice = await crypto.convertPrice(currency, (parseFloat(orderDb.totalPrice) + 5));
// HERE
await db.orders.update({cryptoprice: cryptoPrice}, { where: {uid: order}});
const wallet = await crypto.generateWallet(currency, '1P5ZEDWTKTFGxQjZphgWPQUpe554WKDfHQ', 'https://google.fr');
return res.status(200).json({address: wallet.address, price: cryptoPrice});
} catch (error) {
console.error(error);
return res.status(404).json({error: error});
}
});
The findOne method works correctly, but when just after I update this order, I got this error:
SyntaxError: "undefined" is not valid JSON
at JSON.parse (<anonymous>)
at model.get (shopapp\back\src\database\models\Order.model.js:9:29)
at model.get (shopapp\back\node_modules\sequelize\lib\model.js:2116:41)
at model.get (shopapp\back\node_modules\sequelize\lib\model.js:2138:33)
at order.update (shopapp\back\node_modules\sequelize\lib\model.js:1877:44)
at shopapp\back\src\routes.js:132:29
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
The line number 9 of my Order.model.js is :
get: function() {
return JSON.parse(this.getDataValue('author'));
},
I dont understand cause the author column is not null, and even with the findOne I can get it

Sequelize - Foreign keys are always null

Description
I'm starting to learn how to use Sequelize and I've run into an issue with foreign key relationships for one of my tables. I have three tables: Users, Projects, Times. Users and Projects have a Many to One relationship with Times.
Time.belongsTo(User)
Time.belongsTo(Project)
User.hasMany(Time)
Project.hasMany(Time)
After I create an entry in the Times table and then fetch it the resulting record always has Nulls for the two foreign keys.
db.Time.findAll().then(result => {
res.status(200).send(JSON.stringify(result))
})
Model
const {Sequelize, DataTypes} = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
const Project = sequelize.define('Project', {
name: {
type: DataTypes.STRING,
allowNull: false
}
});
const Time = sequelize.define('Time', {
description: {
type: DataTypes.STRING,
allowNull: false
},
start: {
type: DataTypes.STRING,
allowNull: false
},
stop: {
type: DataTypes.STRING,
allowNull: false
}
});
console.log('Setting up Models')
Time.belongsTo(User)
Time.belongsTo(Project)
User.hasMany(Time)
Project.hasMany(Time)
const db = {
"sequelize": sequelize,
"User": User,
"Project": Project,
"Time": Time
}
exports.User = User;
exports.Project = Project;
exports.Time = Time;
exports.db = db;
Populating with Default Data
const {data} = require('./data')
const {db} = require('../data/models')
const dbHelper = {
connectDB: () => {
return new Promise((resolve, reject) => {
db.sequelize.authenticate().then(() => {
resolve(db)
}).catch((e) => {
reject(e);
})
})
},
hydrateDB: (db) => {
return new Promise((resolve, reject) => {
try {
db.sequelize.sync().then(() => {
hydrateUser(db).then(() => {
hydrateProject(db).then(() => {
hydrateTime(db).then(() => {
resolve()
})
})
})
})
} catch (e) {
reject(e)
}
})
}
}
const hydrateUser = (db) => {
return new Promise(resolve => {
data.user.forEach((datum) => {
db.User.create(datum).then(() => {
resolve()
})
})
})
}
const hydrateProject = (db) => {
return new Promise(resolve => {
data.project.forEach((datum) => {
db.Project.create(datum).then(() => {
resolve()
})
})
})
}
const hydrateTime = (db) => {
return new Promise(resolve => {
data.time.forEach((datum) => {
db.Time.create({
description: datum.description,
start: datum.start,
stop: datum.stop,
userId: 1,
projectId: 1
}).then(() => {
resolve()
})
})
})
}
exports.dbHelper = dbHelper;
You indicated fields in camel case here:
userId: 1,
projectId: 1
And actual fields created for Time are in pascal case as you showed in the screenshot.
So just correct names:
db.Time.create({
description: datum.description,
start: datum.start,
stop: datum.stop,
UserId: 1,
ProjectId: 1
}).then(() => {
resolve()
})

MongoError: FiledPath field names may notstart with '$'

I'm following the udemy cours by Brad Schiff- Learn JavaScript Full-Stack from Scratch, and on the section 7-7. Live Search Feature, I'm having problems with searching for posts. the problem happened after installation of axios.
Tried with the exact code from course but still se same error: MongoError: FiledPath field names may notstart with '$'
The code with the search feature:
import axios from "axios";
import DOMPurify from "dompurify";
export default class search {
constructor() {
this.injectHTML();
this.headerSearchIcon = document.querySelector(".header-search-icon");
this.overlay = document.querySelector(".search-overlay");
this.closeIcon = document.querySelector(".close-live-search");
this.inputField = document.querySelector("#live-search-field");
this.resultsArea = document.querySelector(".live-search-results");
this.loaderIcon = document.querySelector(".circle-loader");
this.typingWaitTimer;
this.previousValue = "";
this.events();
events() {
this.inputField.addEventListener("keyup", () => this.keyPressHandler());
this.closeIcon.addEventListener("click", () => this.closeOverlay());
this.headerSearchIcon.addEventListener("click", (e) => {
e.preventDefault();
this.openOverlay();
});
}
keyPressHandler() {
let value = this.inputField.value;
if (value == "") {
clearTimeout(this.typingWaitTimer);
this.hideLoaderIcon();
this.hideResultsArea();
}
if (value != "" && value != this.previousValue) {
clearTimeout(this.typingWaitTimer);
this.showLoaderIcon();
this.hideResultsArea();
this.typingWaitTimer = setTimeout(() => this.sendRequest(), 750);
}
this.previousValue = value;
}
sendRequest() {
axios
.post("/search", { searchTerm: this.inputField.value })
.then((response) => {
console.log(response.data);
this.renderResultsHTML(response.data);
})
.catch(() => {
alert("Hello, the request failed.");
});
}
}
The other code in Post.js file
Post.reusablePostQuery = function (uniqueOperations, visitorId) {
return new Promise(async function (resolve, reject) {
let aggOperations = uniqueOperations.concat([
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "authorDocument",
},
},
{
$project: {
title: 1,
body: 1,
createdDate: 1,
authorId: "$author",
author: { $arrayElemAt: ["$authorDocument", 0] },
},
},
]);
let posts = await postsCollection.aggregate(aggOperations).toArray();
// clean up author property in each post object
posts = posts.map(function (post) {
post.isVisitorOwner = post.authorId.equals(visitorId);
post.authorId = undefined;
post.author = {
username: post.author.username,
avatar: new User(post.author, true).avatar,
};
return post;
});
resolve(posts);
});
};
Post.findSingleById = function (id, visitorId) {
return new Promise(async function (resolve, reject) {
if (typeof id != "string" || !ObjectID.isValid(id)) {
reject();
return;
}
let posts = await Post.reusablePostQuery(
[{ $match: { _id: new ObjectID(id) } }],
visitorId
);
if (posts.length) {
console.log(posts[0]);
resolve(posts[0]);
} else {
reject();
}
});
};
Post.findByAuthorId = function (authorId) {
return Post.reusablePostQuery([
{ $match: { author: authorId } },
{ $sort: { createdDate: -1 } },
]);
};
Post.search = function (searchTerm) {
return new Promise(async (resolve, reject) => {
if (typeof searchTerm == "string") {
//this. added
let posts = await Post.reusablePostQuery([
{ $match: { $text: { $search: searchTerm } } },
{ $sort: { score: { $meta: "textScore" } } },
]);
resolve(posts);
} else {
reject();
}
});
};
the code in postController.js file
exports.search = function (req, res) {
Post.search(req.body.searchTerm)
.then((posts) => {
res.json(posts);
})
.catch(() => {
res.json([]);
});
};
I was also facing this issue for days, and after looking out for different answers and quite some documentations I have figured out that you need to do a slight change in order to make this live search work.
the code for search function in Post.js file should be this
Post.search = function (searchTerm) {
return new Promise(async (resolve, reject) => {
if (typeof searchTerm == "string") {
// sort by search score (or relevancy to search string)
let posts = await Post.reusablePostQuery([
{ $match: { $text: { $search: searchTerm } } },
{ $project: { _id: true, "score": false } },
{ $sort: { score: { $meta: "textScore" } } },
]);
resolve(posts);
} else {
reject();
}
});
};
Try this and It will work fine..

joinmonster is not a function - GraphQL

I am using the library join-monster with GraphQL and postgres, with GraphiQL as client. When querying the database, I got the error: "joinMonster is not a function".
The method joinMonster() is provided by the library and is used in the resolvers.
The connection to the database is by knex and, apparently, it works. if I run the following code, I get the data form the table:
knex('students').then(rows => console.log(rows))
Database diagram
GraphiQL outpup
This is the schema-resolvers code:
const joinMonster = require('join-monster');
const knex = require('knex')({
client: 'postgres',
connection: {
host: 'localhost',
user: 'postgres',
password: 'myPassword',
database: 'test'
}
});
const { graphQLSchema } = require("graphql");
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLInt,
GraphQLList,
GraphQLNonNull,
GraphQL
} = require('graphql');
const Subject = new GraphQLObjectType({
name: "Subject",
sqlTable: 'subjects',
uniqueKey: 'id',
fields: () => ({
id: {
type: GraphQLInt
},
name: {
type: GraphQLString
},
idEncoded: {
description: 'The ID base-64 encoded',
type: GraphQLString,
sqlColumn: 'id',
// specifies SQL column and applies a custom resolver
resolve: user => toBase64(user.idEncoded)
},
teacher: {
type: GraphQLString
},
students: {
type: new GraphQLList(Student),
junction: {
sqlTable: 'class',
sqlJoins: [
(subjectTable, junctionTable, args) => `${subjectTable}.id = ${junctionTable}.subject_id`,
(junctionTable, studentTable, args) => `${junctionTable}.student_id = ${studentTable}.id`
]
}
}
})
});
const Student = new GraphQLObjectType({
name: "Student",
sqlTable: 'students',
uniqueKey: 'id',
fields: () => ({
id: {
type: GraphQLInt
},
name: {
type: GraphQLString
},
idEncoded: {
description: 'The ID base-64 encoded',
type: GraphQLString,
sqlColumn: 'id',
resolve: user => toBase64(user.idEncoded)
},
lastname: {
type: GraphQLString
},
subjects: {
type: new GraphQLList(Subject),
junction: {
sqlTable: 'class',
sqlJoins: [
(studentTable, junctionTable, args) => `${studentTable}.id = ${junctionTable}.student_id`,
(junctionTable, subjectTable, args) => `${junctionTable}.subject_id = ${subjectTable}.id`
]
}
}
})
});
const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
student: {
type: Student,
args: {
id: {
type: GraphQLInt
}
},
where: (studentsTable, args, context) => {
if (args.id) return `${studentsTable}.id = ${args.id}`
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster(resolveInfo, {}, sql => {
return knex.raw(sql)
})
}
},
subject: {
type: Subject,
args: {
id: {
type: GraphQLInt
}
},
where: (subjectsTable, args, context) => {
if (args.id) return `${subjectsTable}.id = ${args.id}`
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster(resolveInfo, {}, sql => {
return knex.raw(sql)
})
}
}
})
})
const schema = new GraphQLSchema({
query: QueryRoot,
});
module.exports = schema;
function toBase64(clear) {
return Buffer.from(String(clear)).toString('base64')
}
I have followed the documentation from https://join-monster.readthedocs.io/
Thanks
You can also import the function like this:
const joinMonster = require('join-monster').default;
The problem is that when making available the library to the file, an object is provided instead of a function.
const joinMonster = require('join-monster');
console.log(joinMonster)
// output
{ default: { [Function: joinMonster] getNode: [Function: getNode], version: '2.0.16' } }
I don't know why is served an object instead of a function. But
now, I invoke joinMonster.default, and it works:
resolve: (parent, args, context, resolveInfo) => {
return joinMonster.default(resolveInfo, {}, sql => {
return knex.raw(sql)
})

GraphQL cyclical reference using thunks - Error: field type must be Output Type but got: undefined

I have to solve a cyclic definition for GraphQL, but for some reason I´m not being able to solve. I´ve checked several posts about thunks and lazy loading and here is my current code:
History/types.js
const fields = {
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => dbIdToNodeId(obj._id, "History")
},
timestamp: {
type: GraphQLLong
},
objectId: {
type: GraphQLString
},
user: {
type: require('../User/connections').default,
args: connectionArgs,
resolve: (source, args) => {
return UserModel.findOne({ id: source.id }).exec();
}
},
action: {
type: new GraphQLNonNull(GraphQLString)
}
};
export const HistoryType = new GraphQLObjectType({
name: 'History',
description: 'History',
interfaces: () => [NodeInterface],
isTypeOf: (value) => value instanceof HistoryModel,
fields: () => (fields)
});
History/connections.js:
import { HistoryType } from './types';
const { connectionType: HistoryConnectionType } = connectionDefinitions( { nodeType: HistoryType });
export default HistoryConnectionType;
User/types.js
const fields = {
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => dbIdToNodeId(obj._id, "User")
},
email: {
type: GraphQLString
},
history: {
type: require('../History/connections').default,
args: connectionArgs,
resolve: (source, args, context) => {
return HistoryModel.find({ deleted: false, objectId: source.id}).sort('timestamp').exec();
}
}
};
export const UserType = new GraphQLObjectType({
name: 'User',
description: 'User',
interfaces: () => [NodeInterface],
isTypeOf: (value) => value instanceof UserModel,
fields: () => (fields)
});
User/connections.js
import { UserType } from './types';
const { connectionType: UserConnectionType } = connectionDefinitions( { nodeType: UserType });
export default UserConnectionType;
Company/types.js
const fields = {
id: {
type: new GraphQLNonNull(GraphQLID),
resolve: (obj) => dbIdToNodeId(obj._id, "Company")
},
name: {
type: GraphQLString
},
users: {
type: require('../User/connections').default,
args: connectionArgs,
resolve: (source, args) => {
const { id } = source;
return UserModel.find({ companyId: id }).then((rows) => connectionFromArray(rows,args));
}
},
history: {
type: require('../History/connections').default,
args: connectionArgs,
resolve(source, args, context) {
return loaders.getHistory(source.id);
}
}
};
export const CompanyType = new GraphQLObjectType({
name: 'Company',
description: 'Company',
interfaces: () => [NodeInterface],
isTypeOf: (value) => value instanceof CompanyModel,
fields: () => (fields)
});
Company/connections.js
import { CompanyType } from './types';
const { connectionType: CompanyConnectionType } = connectionDefinitions( { nodeType: CompanyType });
export default CompanyConnectionType;
I can´t make it work. When running I´m getting the following output:
History.user field type must be Output Type but got: undefined
I guess you resolved it by now, but for anyone passing by:
In history/types.js, the fields object must be inlined in the fields function of HistoryType, so it gets created at runtime.
const HistoryType = new GraphQLObjectType({
...
fields: () => ({ ... }), // not a var, object is inlined
...
});

Categories