AccessDenied error in GraphQl API, can't figure it out - mongodb

GraphQl PLayground Error screenshot (same error I get in the client)
Hello everyone, I am having a very frustrating problem on a small project i'm working on. I cannot delete items from my cart on the client because I get an accessDenied error. This website has no authentification, and it is not supposed to. I want everyone who is on the site to be able to add and delete cart items at will. I am using GraphQl, Keystone, and mongoDB, I have tried everything I can in the keystone.js database to allow access, I have the access field on all lists and set everything to true. I can delete CartItems just fine in the graphQl Playground, but not on the client or in the admin playground. If anyone can offer some help or advice it would be greatly appreciated, thanks!
Here is my keystone DB:
const { Keystone } = require('#keystonejs/keystone');
const { GraphQLApp } = require('#keystonejs/app-graphql');
const { AdminUIApp } = require('#keystonejs/app-admin-ui');
const { MongooseAdapter: Adapter } = require('#keystonejs/adapter-mongoose');
const FoodItem = require('./lists/FoodItem');
const Category = require('./lists/Category');
const Salad = require('./lists/Salad');
const Order = require('./lists/Order');
const saladCartItem = require('./lists/saladCartItem');
const foodCartItem = require('./lists/foodCartItem');
const Cart = require('./lists/Cart');
const PROJECT_NAME = 'ghost-grits';
const adapterConfig = { mongoUri: process.env.DATABASE_URL };
const keystone = new Keystone({
adapter: new Adapter(adapterConfig),
cookieSecret: process.env.COOKIE_SECRET,
defaultAccess: {
list: true,
field: true
}
});
keystone.createList('FoodItem', FoodItem)
keystone.createList('Category', Category)
keystone.createList('Salad', Salad)
keystone.createList('Order', Order)
keystone.createList('saladCartItem', saladCartItem)
keystone.createList('foodCartItem', foodCartItem)
keystone.createList('Cart', Cart)
module.exports = {
keystone,
apps: [new GraphQLApp({ isAccessAllowed: true }), new AdminUIApp({ name: PROJECT_NAME, enableDefaultRoute: true, isAccessAllowed: true })],
};
And here is the access field, I have put an identical one of these on all my lists:
const FoodItem = list({
access: {
read: true,
update: true,
delete: true,
create: true
},

This is known issue in keystone. When you have no item with that Item id, it will give you access denied error even if there is no access control setup.
check if your client app is sending correct variable to graphql. you can use chrome devtools and look for api calls. make sure the query is right and variable has correct data. You can also check by querying the item using that id and if that returns any result.

Related

Mongoose not creating document on mongodb

I am using Next.js and MongoDB (mongoose) to make an education portal website. The school admin can add important notices from their dashboard and the student can view them from theirs. Most mongoose operations have to be done in an API that next.js provides them self so my code to create the document on MongoDB is located in /API/ directory. Code:
/**
* #param {import("next").NextApiRequest} req
* #param {import("next").NextApiResponse} res
*/
import circulars from '../../../model/hw'
export default async function stuffss(req, res) {
try{
const {number, text} = req.body
console.log("Connecting to mongo")
mongoose.connect("mongodb+srv://usernameherer:passowordhere#cluster0.clusteridhere.mongodb.net/stuff?retryWrites=true&w=majority")
console.log("Connected to mongo")
console.log("Creating document")
const createdCircular = await circulars.create(req.body)
console.log("Created document")
res.json({ createdCircular })
}
catch(error) {
console.log(error)
res.json({ error })
}
}
The circulars schema:
const mongoose = require('mongoose')
const circularsSchema = new mongoose.Schema({
number: {
unique: true,
required: true,
type: Number
},
text: {
required: true,
type: String
}
})
const circulars = mongoose.models.circularss || mongoose.model("circularss", circularsSchema)
export default circulars
Sending a post request using insomnia gives the following result:
{
"createdCircular": {
"number": 15463,
"text": "loreum ipsum",
"_id": "63cb86421ca543a731cb39c3",
"__v": 0
}
}
but when I go and look for it on the MongoDB website it doesn't have any results.
I believe it may be because of how I am connecting or that I am not sending it properly to a collection.
This is the structure of my database:
Is there a way to connect to the 'stuff' database and 'circulars' collection?
Looks like that the database needs to be specified in connection string.
E.g.
...clusteridhere.mongodb.net/stuff?retryWrites=true&w=majority
Ref: https://mongoosejs.com/docs/connections.html
Here is how I fixed this issue. In my schema I had put the same circulars which created created a new collection for it, I also had to remove await from mongoose.connect. This combined fixed the issue for me. You should also make sure that mongoose.models.name is the same as the mongoose.model("name", schema)

How to update an array in Mongoose JS

Currently, I'm trying to create a Discord bot that will save a to-do list in a database. To do so, I wanted to use an array that contains any items the person adds by typing !listadd (something) that will then be stored in a MongoDB database. However, while this seems to work for Strings, I cannot seem to change an array at all using this method.
await list.findOneAndUpdate({_id: id},{$push:{todoList: msg}, itemToAdd:msg},{upsert : true})
This line above is what I'm using to update the list. While the change to itemToAdd is reflected perfectly in the database, nothing is pushed to todoList. I can't even seem to update todoList at all past setting what the default value is in the schema. (Like, if I put {todoList:['one','two']} as the update object, it still won't do anything)
I've tried using the .updateOne() method and, again, it works fine for itemToAdd, but nothing happens to todoList. I've also tried using the .markModified() method and I've either used it wrong or it isn't working here because, still, nothing changes in the database.
Full Code:
const mongoose = require('mongoose');
const todoSchema = new mongoose.Schema({
todolist : [String],
_id: {
type : String,
required : true
},
itemToAdd:{
type: String,
required: false
},
indexOfItemToRemove:{
type: Number,
required: false
}
});
const list = mongoose.model('list', todoSchema);
module.exports = async (client) => {
client.on('message', async (message) => {
const { author } = message;
const { id } = author;
const { bot } = author;
if (!bot){
console.log('AUTHOR:', author);
if (message.content.includes('!listadd ')){
const msg = message.content.substring(9)
await list.findOneAndUpdate({_id: id},{$push:{todoList: msg}, itemToAdd:msg},{upsert : true})
message.reply('Entry Added!')
}
if (message.content.includes('!listget')){
let doc = await list.findOne({_id:id})
message.reply(doc.itemToAdd)
}
}
})
}

Meteor and React Native - FileCollection Access

i posted this question some time ago at the FilesCollection Github Repo (https://github.com/veliovgroup/Meteor-Files), but I'll hope to reach out here anyone who is familiar with Meteor and React Native.
My problem is, that I'm not sure how to use a FilesCollection with React Native (RN) and Meteor.
I used the official Meteor guide to set up a RN app: https://guide.meteor.com/react-native.html
So i have access to #meteorrn/core but how is it now possible for me to access a FileCollection.
Usually you would do this with (on a non-RN-web app):
import { FilesCollection } from "meteor/ostrio:files";
export const ImageDB = new FilesCollection(chosenSettings);
where chosenSettings are some settings might be e.g.
const chosenSettings = {
collectionName: "Images",
allowClientCode: false,
onBeforeUpload(file) {
if (file.size <= 10485760 && /png|jpg|jpeg/i.test(file.extension)) {
return true;
}
return "Please upload image, with size equal or less than 10MB";
},
storagePath: `${process.env.PWD}/images`,
};
However, with RN it is not possible to access FilesCollection from meteor/ostrio:files as I don't have any meteor relation here
With other collections I can use Mongo.Collection from #meteorrn/core, however this is not possible with FilesCollection:
const collection = new Mongo.Collection("myCollection");
Any suggestions?
There is no "out-of-the-box" solution, as there is no FilesCollection on the RN client available. However, you could create a workaround strategy using a Meteor method.
Create a publication for the FilesCollection's underlying collection:
server
Meteor.publish('imagesForRN', function () {
return ImageDB.collection.find({}) // note the Mongo.collection is accessible here
})
Create a Method to resolve links:
server
Meteor.methods({
resolveRNLinks ({ ids }) {
return ImageDB.collection
.find({ _id: { $in: ids}})
.map(linkImage);
}
})
const linkImage = function (fileRef) {
const url = Images.link(fileRef, 'original', Meteor.absoluteUrl())
return { _id: fileRef._id, url }
}
Get the URLS of the subscribed docs
This assumes you have subscribed to the Image Collection as shown in the guide.
client
import Meteor from '#meteorrn/core'
const MyComponent = props => {
const { loading, myTodoTasks } = this.props;
useEffect(() => {
const ids = myTodoTasks.map(doc => doc._id)
// note, you should filter those ids, which you have already
// resolved so you only call for the new ones
Meteor.call('resolveRNLinks', { ids }, (err, urls) => {
// store urls in state or ref, depending on your usecase
})
})
}
Resources
https://github.com/TheRealNate/meteor-react-native
https://github.com/veliovgroup/Meteor-Files/blob/master/docs/collection.md
https://github.com/veliovgroup/Meteor-Files/blob/master/docs/link.md

Firestore cloud functions - can I send emails to users every time an document is added to a different collection (not 'users')?

I'm very new to cloud functions but have set up a couple of firestore cloud functions & got them working sending emails to individuals when their user document is created or updates but I really want to send emails to each user when a document is added to another collection (it's a react app displaying videos - I want to update all subscribed users when a new video is added). I can restructure the db if necessary but it currently has users and videos as the only 2 root level collections.
I've tried using .get() to the users collection to collect all their email addresses to put in the 'to' field of the email, but I just get an error saying 'db.get() is not a function'. After researching I found a couple of things to try but both got the same error:
functions.firestore
.document('users/{uid}').get()
.then((querySnapshot) => {
and
const admin = require('firebase-admin');
var db = admin.firestore();
db.document('users/{uid}').get()
.then((querySnapshot) => {
Can anyone help? Is it possible to do this? It seems that in theory the new Email Trigger Extension might do this but tbh I'd rather code it myself and learn how it works as I go - especially having 'cracked' the first two! But I can't find any way to access the contents of two collections within one function & I've spend days looking in all the usual places for any info so I'm beginning to think maybe cloud functions can only access one collection per function - but I also can't find anything that actually says that...?
Here is the whole function using the format I have working for the other 2 functions (apart from trying to access the users):
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const admin = require('firebase-admin');
var db = admin.firestore();
//google account credentials used to send email
var transporter = nodemailer.createTransport({
host: process.env.HOST,
port: 465,
secure: true,
auth: {
user: process.env.USER_EMAIL,
pass: process.env.USER_PASSWORD,
}
});
//Creating a Firebase Cloud Function
exports.sendNewVidEmail = functions.firestore
.document('videos{uid}')
.onCreate(async (snap, context) => {
const newValue = snap.data();
// access title & description
const newVideoTitle = newValue.title;
const newVideoDescription = newValue.description;
//try to access users
db.document('users/{uid}').get()
.then((querySnapshot) => {
let users = [];
querySnapshot.forEach((doc) => {
// check for data
console.log(doc.id, " => ", doc.data());
users.push(doc.data().subscriberEmail)
//check for 'users' array
console.log('users = ', users)
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
// perform desired operations ...
if (newVideoTitle) {
const mailOptions = {
from: process.env.USER_EMAIL,
to: users,
subject: 'new video!',
html: `
<h2> xxx has a new video called ${newVideoTitle}</h2>
<p> xxx says this about ${newVideoTitle}: ${newVideoDescription}.
</p></ br>
<h4>Watch ${newVideoTitle} here.and please tick 'like' if you like it!</h4>
<p>Yours,</p>
<p>xxx :-) </p>`
};
return transporter.sendMail(mailOptions)
.then(() => {
console.log('sent')
})
.catch(error => {
console.log(error)
return
})
}
});
Well, I fixed it!! The code I was using was almost there, and thanks to a great youtube tutorial from Jeff Delaney (fireship) [here][1] I got the code I needed. 2 lines and so simple, and now I'm kicking myself, but in case anyone else gets stuck on this, my error was to try & use .forEach() (from the docs) and .push() to get the users' emails array when just using .map() on the snapshots creates the users array perfectly and then it worked!
const userSnapshots = await admin.firestore().collection('users').get();
const emails = userSnapshots.docs.map(snap => snap.data().subscriberEmail);
Hope it helps someone down the line:
https://www.youtube.com/watch?v=vThujL5-fZQ

How can i use MongoDB with Cloud Functions for Firebase?

I want to use Cloud Functions for Firebase and MongoDB. The problem is I don't know how to connect my Mongo database with Cloud Functions. My database is deployed at matlab.
I made this schema:
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var patientSchema = new Schema({
name: {
type: String,
required : true,
},
disease:{
type: String,
required : true,
},
medication_provided: {
type: String,
required : true,
},
date : {
type : Date,
}
})
const patient = mongoose.model('patientInfo', patientSchema)
module.exports = patient
Then I require my schema in project index.js file, and export a function called getAllPatient.
const patient = require('../Patient')
const functions = require('firebase-functions');
const mongoose = require('mongoose')
mongoose.connect('mongodb://patient:patient123#ds139869.mlab.com:39869/patient',{useMongoClient: true})
exports.getAllPatient = functions.https.onRequest((request, response) => {
patient.find({}).then((data) => {
response.send(data)
})
})
but gives me an error that "Error: could not handle the request"
I was recently facing this type of error and found that firebase free plan doesn't allow the outbound connections from within the functions. If you need to call external http/tcp connections, you are required to be on the flame or blaze plan. see the screenshot attached below or see the section cloud functions -> outbound networking at this link Firebase Pricing
Try to design the cloud function in a similar way shown in a link below:-
https://github.com/firebase/functions-samples/blob/master/authorized-https-endpoint/functions/index.js
I've tried with mongoose long time back and it was working fine and but it's slow because for every new request it's going to open the mongoose connection and serve you the data.
Hope this helps!!