I have adapted the below code from Google's Datastore tutorial. I'm vaguely aware of Promises, and I'm using await where I can figure out to do so. I've used the req object with express as below without incident, but it seems to be empty here.
It causes this error:
2021-04-16 04:55:03 default[20210415t215354] (node:10) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'name' of undefined
[SNIP]
name is a reference to a param I'm specifying like this:
curl -X POST https://MYURL/gizmos --data 'name=foo&type=bar&len=29'
What am I doing wrong? How do I avoid this?
/**
* #param {object} gizmo The gizmo record to insert.
*/
const insertGizmo = gizmo => {
return datastore.save({
key: datastore.key('gizmo'),
data: gizmo,
});
};
/**
* Retrieve the latest gizmo
*/
const getLastGizmo = () => {
const query = datastore
.createQuery('gizmo')
.order('createTime', {descending: true})
.limit(1);
return datastore.runQuery(query).then( (entities) => {
return entities[0].map(fromDatastore);
});
};
//Create a Gizmo
app.post('/gizmos', async (req, res, next) => {
const createTime = new Date();
const gizmo = {
name: req.body.name,
type: req.body.type,
length: req.body.len,
createTime: createTime,
};
try {
await insertGizmo(gizmo);
const newGizmo = await getLastGizmo();
//const [newGizmos] = await getLastGizmo();
await insertGizmo(gizmo);
const newGizmo = await getLastGizmo();
//const [newGizmos] = await getLastGizmo();
const gizmoUrl = "https://MYURL/gizmos/"+newGizmo.id;
const resText = {
"id" : newGizmo.id,
"name" : newGizmo.name,
"type" : newGizmo.type,
"length" : newGizmo.length,
"self" : gizmoUrl,
};
res.status(201).set('Content-Type', 'text/plain').send(resText).end();
} catch (error) {
next(error);
}
});
Per #hoangdv, add app.use(express.urlencoded()) somewhere. Rather node-ish not have it as the default if you ask me.
Related
I´m a Java Dev so I need help from NodeJS guys!
Task: create a script that retrieves '_id', 'document', and 'corporateName' from MongoDB, then take the retrieved '_id', and pass it as a parameter to an API request. The last part should be taking 'document', 'corporateName' + 'client_id', 'client_secret' and export it into a single csv file.
It might be a very simple script! Therefore I´ve done this till now:
const {MongoClient} = require('mongodb');
const uri = "mongodb+srv://<privateInfo>/";
const client = new MongoClient(uri);
async function run() {
try {
const database = client.db("merchant-profile");
const ecs = database.collection("merchant_wallet");
const api = `https://<prodAPI>/v1/merchant/wallet/${id}/oauth2`;
const ecOpt = {_id: 1, document: 1, corporateName: 1};
const credOpt = {client_id: 1, client_secret: 1};
const ec = ecs.find({}).project(ecOpt);
let id = ec.forEach(id => cred._id);
const cred = api.find({}).project(credOpt);
await cred.forEach(console.dir);
} finally {
await client.close();
}
}
run().catch(console.dir);
I´m trying to understand how can I take '_id' fetched in 'ec' and pass it as a param to the 'cred' call.
This would already be awesome!
If you could help me out with the CSV issue as well it would be perfect.
So I don´t want just the answer, but understand how to do this.
Thank you all in advance!
This is the way I found to do it:
const { default: axios } = require("axios");
const { MongoClient } = require("mongodb");
const uri = "mongodb+srv://admin:sLKJdsdRp4LrsVtLsnkR#pp-core-prd.fy3aq.mongodb.net/";
const client = new MongoClient(uri);
async function run() {
try {
const database = client.db("merchant-profile");
const ecs = database.collection("merchant_wallet");
const data = [];
await ecs.find({}).forEach(async function teste(response) {
const id = response._id;
const api = `https://api.pedepronto.com.br/v1/merchant/wallet/${id}/oauth2`;
try{
const res = await axios.get(api);
data.push({client_secret: res.data[0].client_secret, client_id: res.data[0].client_id})
}catch(e){
console.log(e);
}
})
} finally {
await client.close();
}
}
run().catch(console.dir);
It iterates over the find method and appends the fetched id to the uri.
I need to test the following createFacebookAdVideoFromUrl() that consumes a retryAsyncCall that I'd like to stub with Sinon :
async function createFacebookAdVideoFromUrl(accountId, videoUrl, title, facebookToken = FACEBOOK_TOKEN, options = null, businessId = null) {
const method = 'POST';
const url = `${FACEBOOK_URL}${adsSdk.FacebookAdsApi.VERSION}/${accountId}/advideos`;
const formData = {
access_token: businessId ? getFacebookConfig(businessId).token : facebookToken,
title,
name: title,
file_url: videoUrl,
};
const callback = () => requestPromise({ method, url, formData });
const name = 'createFacebookAdVideoFromUrl';
const retryCallParameters = buildRetryCallParameters(name, options);
const adVideo = await retryAsyncCall(callback, retryCallParameters);
logger.info('ADVIDEO', adVideo);
return { id: JSON.parse(adVideo).id, title };
}
This retryAsyncCall function is exported as such:
module.exports.retryAsyncCall = async (callback, retryCallParameters, noRetryFor = [], customRetryCondition = null) => {
// Implementation details ...
}
Here is how I wrote my test so far:
it.only("should create the video calling business's Facebook ids", async () => {
const payload = createPayloadDataBuilder({
businessId: faker.internet.url(),
});
const retryAsyncCallStub = sinon.stub(retryAsyncCallModule, 'retryAsyncCall').resolves('random');
const createdFacebookAd = await FacebookGateway.createFacebookAdVideoFromUrl(
payload.accountId,
payload.videoUrl,
payload.title,
payload.facebookToken,
payload.options,
payload.businessId,
);
assert.strictEqual(retryAsyncCallStub.calledOnce, true);
assert.strictEqual(createdFacebookAd, { id: 'asdf', title: 'asdf' });
});
I don't expect it to work straightaway as I am working in TDD fashion, but I do expect the retryAsyncCall to be stubbed out. Yet, I am still having this TypeError: Cannot read property 'inc' of undefined error from mocha, which refers to an inner function of retryAsyncCall.
How can I make sinon stubbing work?
I fixed it by changing the way to import in my SUT :
// from
const { retryAsyncCall } = require('../../../helpers/retry-async');
// to
const retry = require('../../../helpers/retry-async');
and in my test file :
// from
import * as retryAsyncCallModule from '../../../src/common/helpers/retry-async';
// to
import retryAsyncCallModule from '../../../src/common/helpers/retry-async';
The use of destructuring seemed to make a copy instead of using the same reference, thus, the stub was not applied on the right reference.
I have using redis to cache my queries. Its working fine with object but not when i get array. It gives me an error **"Parameter "obj" to Document() must be an object, got kids", **. It also happens with count query. Here is my code :
const mongoose = require("mongoose");
const redis = require("redis");
const util = require("util");
const client = redis.createClient(process.env.REDIS_URL);
client.hget = util.promisify(client.hget);
const exec = mongoose.Query.prototype.exec;
mongoose.Query.prototype.cache = async function (options = {}) {
this.useCache = true;
this.hashKey = JSON.stringify(options.key || "");
this.time = JSON.stringify(options.time || 36000);
return this;
};
mongoose.Query.prototype.exec = async function () {
if (!this.useCache) {
return exec.apply(this, arguments);
}
const key = JSON.stringify(
Object.assign({}, this.getQuery(), {
collection: this.mongooseCollection.name,
})
);
// client.flushdb(function (err, succeeded) {
// console.log(succeeded); // will be true if successfull
// });
const cacheValue = await client.hget(this.hashKey, key);
if (cacheValue) {
const doc = JSON.parse(cacheValue);
/*
this.model refers to the Class of the corresponding Mongoose Model of the query being executed, example: User,Blog
this function must return a Promise of Mongoose model objects due to the nature of the mongoose model object having other
functions attached once is created ( validate,set,get etc)
*/
console.log("Response from Redis");
console.log(doc);
console.log(Array.isArray(doc));
return Array.isArray(doc)
? doc.map((d) => new this.model(d))
: new this.model(doc);
}
//await the results of the query once executed, with any arguments that were passed on.
const result = await exec.apply(this, arguments);
client.hset(this.hashKey, key, JSON.stringify(result));
client.expire(this.hashKey, this.time);
console.log("Response from MongoDB");
return result;
};
module.exports = {
clearHash(hashKey) {
client.del(JSON.stringify(hashKey));
},
};
Data in redis - [ 'kids', 'men', 'women' ]
Query - const collectionType = await Product.find() .distinct("collectionType") .cache({ key: "COLLECTION_TYPE" });
can i anyone please tell me what i am doing wrong?
I have solved by directly returning the doc and its working fine. Not sure if it is the right way if i directly do return doc then sending data from redis only
In my NodeJS app I have this Update:
//here I am passing as string, but even as new ObjectID(docId) does not work.
let filter = { 'TepDocSignId' : docId, 'ClientId': clientId };
let update = { 'Solved' : true, SolvedOn : new Date()};
const resp = await db.collection(process.env.MONGODB_WARNING_COLLECTION_NAME)
.updateOne({ filter }, { '$set' : update });
On the Node it DOES NOT work, The matchedCount and modifiedCount are allways 0
But If I do the same update on RoboMongo, it works fine!
What is going on?
Is there some kind of profiler that I can see what is doing on the Node environment?
I am using :
"aws-sdk": "^2.590.0",
"mongodb": "^3.5.5",
And the data are:
process.env.MONGODB_WARNING_COLLECTION_NAME = 'Warning'
docId = '5e29197dac26760002f5a7b5'
clientId = '5caf91cd800fc20002cad0fb'
The full code is (here I am using the IDs as ObjectID, but does not matter if I parse or pass as string it does not match anyting...)
const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID
let dbConnString = process.env.MONGODB_CONNECTION_STRING;
let dbName = process.env.MONGODB_DATABASE_NAME;
let db;
const client = new MongoClient(dbConnString, {
useNewUrlParser: true,
useUnifiedTopology: true
});
const createConn = async () => {
await client.connect();
db = client.db(dbName);
};
async function partnerWarningOff(docId, partnerId) {
if (!client.isConnected()) {
try {
await createConn();
} catch (e) {
throw new Error(`partnerWarningOff - OpenningConn Error: ${e}`);
}
}
console.log('process.env.MONGODB_WARNING_COLLECTION_NAME', process.env.MONGODB_WARNING_COLLECTION_NAME);
console.log('docId', docId);
console.log('partnerId', partnerId);
let dId = new ObjectID(docId);
let pId = new ObjectID(partnerId);
let filter = { 'TepDocSignId' : dId, 'PartnerId': pId };
let update = { 'Solved' : true, 'SolvedOn' : new Date()};
const resp = await db.collection(process.env.MONGODB_WARNING_COLLECTION_NAME)
.updateOne({ filter }, { '$set' : update });
console.log('resp', resp);
if (!resp) {
throw new Error(`partnerWarningOff Error solving tep warning with DocId ${docId}`);
}
};
cheers
Found the problem.
The handler was passing docId as ObjectID and the ClientId as string.
The only thing I had to do, was change the way the handler was calling the function, the correct way is
myfuncName(doc._id**.toString()**, doc.clientId).
This way I have all in string, the same way is stored in the other entity.
You can Follow this code..
const response = await db.collection.findOneAndUpdate({
TepDocSignId : dId,
PartnerId: pId
}, {
$set: req.body,
}, {
new: true
})
I had implemented firebase functions in my app and previously it was working fine but now it is showing error Cannot read property 'previous' of undefined
Error Logs of function
TypeError: Cannot read property 'previous' of undefined
at exports.LoveNotification.functions.database.ref.onWrite (/user_code/index.js:223:16)
at cloudFunctionNewSignature (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:109:23)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:139:20)
at /var/tmp/worker/worker.js:730:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
The signature of Cloud Functions triggers has changed. You seem to be using beta, but are deploying to the latest version. See the migration guide for complete instructions.
From there:
Before (<= v0.9.1)
exports.dbWrite = functions.database.ref('/path').onWrite((event) => {
const beforeData = event.data.previous.val(); // data before the write
const afterData = event.data.val(); // data after the write
});
Now (>= v1.0.0)
exports.dbWrite = functions.database.ref('/path').onWrite((change, context) => {
const beforeData = change.before.val(); // data before the write
const afterData = change.after.val(); // data after the write
});
So your code should look something like this:
exports.LoveNotification = functions.database.ref("/Member/{pushId}").onWrite((change, context) => {
if (change.before.exists()) {
return;
} else {
var eventLove = change.after.data.val();
var author =eventLove.fullname;
var title = eventLove.course;
var key = eventLove.key;
const payload = {
"data": {
"post_id": key
},
notification: {
title: author +'Joined the app',
body: `Course `+title,
sound: "default",
icon: "ic_launcher",
}
};
const options = {
priority: "high",
timeToLive: 60 * 60 * 24 //24 hours
};
console.log('Sending notifications');
return admin.messaging().sendToTopic("Member", payload, options);
}
});