Making an JS API: array.includes(key) not working as expected - javascript

I wrote this code for a Cloudflare worker.
When a new customer is created on Stripe, a webhook is triggered and returns the new customer data to [...].worker.dev/updateCustomers
The Cloudflare worker will then do the following:
Get the name from the customer JSON object
create a version without the " " around the string
add the new customer to an array called customers[]
for debugging reasons it will response back to Stripe with the following content: New customer name, all customers in the array and the result if the new customer name is contained in the array
If a user opens a connection to "https://[...]workers.dev/validate?licence=eee" it will return "legit" if the name is in the customer array and "failed" if it is not.
Through Stripe I can see the following response from my worker when a webhook is fired: "Customers received: eee Other Customers: demo,demo2,demo3,eee customers.includes(key): true"
That means that the new customer was successfully added to the array and that my code is able to check if a customer is included in the array.
But when a user tries to validate their name directly, only the array contents that are defined in the beginning like "demo", "demo2" get a positive response.
Can anyone help me fix this? Thanks a lot in advance.
addEventListener("fetch", (event) => {
event.respondWith(
handleRequest(event.request).catch(
(err) => new Response(err.stack, { status: 500 })
)
);
});
customers = ["demo", "demo2","demo3"];
/**
* #param {Request} request
* #returns {Promise<Response>}
*/
async function handleRequest(request) {
const { pathname } = new URL(request.url);
if (pathname.startsWith("/api")) {
return new Response(JSON.stringify({ pathname }), {
headers: { "Content-Type": "application/json" },
});
}
if (pathname.startsWith("/validate")) {
let params = (new URL(request.url)).searchParams;
let key = params.get('licence');
console.log(key);
if(customers.includes(key)){
return new Response("legit");
}
else {
return new Response("failed");
}
}
if (pathname.startsWith("/updateCustomers")) {
let clonedBody = await request.clone().json();
let newCustomerName = JSON.stringify(clonedBody.data.object.name);
let newCustomerNameRaw = newCustomerName.substring(1, newCustomerName.length-1);
customers.push(newCustomerNameRaw);
return new Response("Customers recievedd: " + newCustomerNameRaw + " Other Customers: " + customers.toString() + " customers.includes(key): " + customers.includes(newCustomerNameRaw) );
}
//fallback **strong text**not relevant
if (pathname.startsWith("/status")) {
const httpStatusCode = Number(pathname.split("/")[2]);
return Number.isInteger(httpStatusCode)
? fetch("https://http.cat/" + httpStatusCode)
: new Response("That's not a valid HTTP status code.");
}
return fetch("https://welcome.developers.workers.dev");
}

That is because the customers "demo", "demo2" and "demo3" are stored in the webworker as part of the code, so they are present anytime the call is made. The new customer is stored just temporarily in the array whilst the code runs but once the execution ends the customers array content is reset. You will need to use some kind of backend to store the customers if you want to persist the new ones between runs

Related

Dialogflow Firebase Realtime Database Getting Error: No responses defined for platform: Undefined yet method still goes through

I've been trying for a project I'm working on to develop a function for a Food chatbot. What I'm currently working on is to perform a method for a user to make a purchase of an order that is stored in firebase realtime database.
The method is set as the method for an actionMap and the actionMap is linked to an intent for knowing when to call the method and for retrieving the parameters.
My current method uses a simple check for a user's existence and status within the database before identifying the existence of the order they're trying to make a purchase for by its id by going through the user's reference path and doing a .forEach to check every order found and look at its parent folder name to check if it matches the user's order id. My code is as follows:
const MakePurchaseACTION = 'Make Purchase';
function makePurchase(app){
let email = parameter.email;
let orderId = parameter.orderId;
var currDate = currDateGenerator();
var name = email.split(".com");
//Check if User exists first in database
var userRef = database.ref().child('user/' + name);
return userRef.once('value').then(function(snapshot) {
if (snapshot.exists()) {
let statusRetrieved = snapshot.child('Status').val();
//Check if user's status in database is signed in.
if (statusRetrieved == "Signed In") {
var orderRef = database.ref().child('order/' + name);
//Check the order table for the user.
return orderRef.once('value').then(function(orderSnapshot){
let orderVal = orderSnapshot.val();
console.log(orderVal);
//Check through every child for the matching id.
orderSnapshot.forEach(function(childSnapshot) {
let orderIdFound = childSnapshot.key;
//let cost = childSnapshot.child('Cost').val();
console.log(orderIdFound);
if(orderId == orderIdFound) {
let eateryName = childSnapshot.child('Eatery').val();
let eateryLocation = childSnapshot.child('EateryLocation').val();
let deliveryAddress = childSnapshot.child('DeliveryAddress').val();
let orderItem = childSnapshot.child('OrderItem').val();
let quantity = childSnapshot.child('Quantity').val();
let cost = childSnapshot.child('Cost').val();
var purchaseRef = database.ref().child('purchase/' + name + "/" + currDate + "/" + orderId);
purchaseRef.set({
"Eatery" : eateryName,
"EateryLocation" : eateryLocation,
"DeliveryAddress": deliveryAddress,
"OrderItem" : orderItem,
"Quantity": quantity,
"Cost": cost,
"DateCreated": currDate
});
app.add("You have successfully purchased Order " + orderId);
} else {
app.add("There is no order with that id.");
}
});
});
} else {
app.add("You need to be signed in before you can order!");
}
}
else {
app.add("Sorry pal you don't exist in the database.");
}
});
}
actionMap.set(MakePurchaseACTION, makePurchase);
After checking through some firebase logs
Firebase Logs screenshot here
Firebase Realtime Database Order Table Sample
I found that the method actually completes Purchase table sample but my dialogflow returns with the stated error of:
Error: No responses defined for platform: undefined and displays "Not Available" back to the user. My question is how do I go about resolving this error?

MongoDB - Mongoose - TypeError: save is not a function

I am attempting to perform an update to a MongoDB document (using mongoose) by first using .findById to get the document, then updating the fields in that document with new values. I am still a bit new to this so I used a tutorial to figure out how to get it working, then I have been updating my code for my needs. Here is the tutorial: MEAN App Tutorial with Angular 4. The original code had a schema defined, but my requirement is for a generic MongoDB interface that will simply take whatever payload is sent to it and send it along to MongoDB. The original tutorial had something like this:
exports.updateTodo = async function(todo){
var id = todo.id
try{
//Find the old Todo Object by the Id
var oldTodo = await ToDo.findById(id);
}catch(e){
throw Error("Error occured while Finding the Todo")
}
// If no old Todo Object exists return false
if(!oldTodo){
return false;
}
console.log(oldTodo)
//Edit the Todo Object
oldTodo.title = todo.title
oldTodo.description = todo.description
oldTodo.status = todo.status
console.log(oldTodo)
try{
var savedTodo = await oldTodo.save()
return savedTodo;
}catch(e){
throw Error("And Error occured while updating the Todo");
}
}
However, since I don't want a schema and want to allow anything through, I don't want to assign static values to specific field names like, title, description, status, etc. So, I came up with this:
exports.updateData = async function(update){
var id = update.id
// Check the existence of the query parameters, If they don't exist then assign a default value
var dbName = update.dbName ? update.dbName : 'test'
var collection = update.collection ? update.collection : 'testing';
const Test = mongoose.model(dbName, TestSchema, collection);
try{
//Find the existing Test object by the Id
var existingData = await Test.findById(id);
}catch(e){
throw Error("Error occurred while finding the Test document - " + e)
}
// If no existing Test object exists return false
if(!existingData){
return false;
}
console.log("Existing document is " + existingData)
//Edit the Test object
existingData = JSON.parse(JSON.stringify(update))
//This was another way to overwrite existing field values, but
//performs a "shallow copy" so it's not desireable
//existingData = Object.assign({}, existingData, update)
//existingData.title = update.title
//existingData.description = update.description
//existingData.status = update.status
console.log("New data is " + existingData)
try{
var savedOutput = await existingData.save()
return savedOutput;
}catch(e){
throw Error("An error occurred while updating the Test document - " + e);
}
}
My original problem with this was that I had a lot of issues getting the new values to overwrite the old ones. Now that that's been solved, I am getting the error of "TypeError: existingData.save is not a function". I am thinking the data type changed or something, and now it is not being accepted. When I uncomment the static values that were in the old tutorial code, it works. This is further supported by my console logging before and after I join the objects, because the first one prints the actual data and the second one prints [object Object]. However, I can't seem to figure out what it's expecting. Any help would be greatly appreciated.
EDIT: I figured it out. Apparently Mongoose has its own data type of "Model" which gets changed if you do anything crazy to the underlying data by using things like JSON.stringify. I used Object.prototype.constructor to figure out the actual object type like so:
console.log("THIS IS BEFORE: " + existingData.constructor);
existingData = JSON.parse(JSON.stringify(update));
console.log("THIS IS AFTER: " + existingData.constructor);
And I got this:
THIS IS BEFORE: function model(doc, fields, skipId) {
model.hooks.execPreSync('createModel', doc);
if (!(this instanceof model)) {
return new model(doc, fields, skipId);
}
Model.call(this, doc, fields, skipId);
}
THIS IS AFTER: function Object() { [native code] }
Which showed me what was actually going on. I added this to fix it:
existingData = new Test(JSON.parse(JSON.stringify(update)));
On a related note, I should probably just use the native MongoDB driver at this point, but it's working, so I'll just put it on my to do list for now.
You've now found a solution but I would suggest using the MongoDB driver which would make your code look something along the lines of this and would make the origional issue disappear:
// MongoDB Settings
const MongoClient = require(`mongodb`).MongoClient;
const mongodb_uri = `mongodb+srv://${REPLACE_mongodb_username}:${REPLACE_mongodb_password}#url-here.gcp.mongodb.net/test`;
const db_name = `test`;
let db; // allows us to reuse the database connection once it is opened
// Open MongoDB Connection
const open_database_connection = async () => {
try {
client = await MongoClient.connect(mongodb_uri);
} catch (err) { throw new Error(err); }
db = client.db(db_name);
};
exports.updateData = async update => {
// open database connection if it isn't already open
try {
if (!db) await open_database_connection();
} catch (err) { throw new Error(err); }
// update document
let savedOutput;
try {
savedOutput = await db.collection(`testing`).updateOne( // .save() is being depreciated
{ // filter
_id: update.id // the '_id' might need to be 'id' depending on how you have set your collection up, usually it is '_id'
},
$set: { // I've assumed that you are overwriting the fields you are updating hence the '$set' operator
update // update here - this is assuming that the update object only contains fields that should be updated
}
// If you want to add a new document if the id isn't found add the below line
// ,{ upsert: true }
);
} catch (err) { throw new Error(`An error occurred while updating the Test document - ${err}`); }
if (savedOutput.matchedCount !== 1) return false; // if you add in '{ upsert: true }' above, then remove this line as it will create a new document
return savedOutput;
}
The collection testing would need to be created before this code but this is only a one-time thing and is very easy - if you are using MongoDB Atlas then you can use MongoDB Compass / go in your online admin to create the collection without a single line of code...
As far as I can see you should need to duplicate the update object. The above reduces the database calls from 2 to one and allows you to reuse the database connection, potentially anywhere else in the application which would help to speed things up. Also don't store your MongoDB credentials directly in the code.

firebase database not updating, even though successful response

I have a chat app that I am building in ionic and firebase. I have a simple join channel function that takes the channel id, and adds the current user id to the list of member in the database. According to everything I am seeing it is working correctly, however whenever I go to the firebase console it is not showing the new item in the database. Here is the code I am using to add the item to the database when a user joins a chat channel...
joinChannel(type, user_id) {
return new Promise((resolve, reject) => {
if(this.availableChannels[type] !== undefined) {
console.log("joining channel: " + type);
console.log('for user: ' + user_id);
let update = {};
update['/chat/members/' + type + '/' + user_id] = {active: true, joinedAt: firebase.database.ServerValue.TIMESTAMP};
this.afDB.database.ref().update(update).then(() => {
console.log("updated /chat/members/" + type + "/" + user_id + " to");
console.log({active: true, joinedAt: firebase.database.ServerValue.TIMESTAMP});
this.activeChannel = '/channels/' + type;
resolve(true);
})
} else {
reject("channel_not_found");
}
});
}
Now, if I open up the developer console in chrome and click to join the channel I get this in the console...
joining channel: news
chat.ts:210 for user: [USERIDHERE]
chat.ts:214 updated /chat/members/news/[USERIDHERE] to
chat.ts:215 Object
active: true
joinedAt: Object
.sv: "timestamp"
__proto__: Object
__proto__: Object
So the console shows that the database ran the update, however if I then log into my firebase console, there is no "news" item in the "chat/members" database. I have tried it with different channels, and always I get the same exact response from firebase, saying it updated the database, but in the firebase console that item remains blank.
Just in case it is needed the this.afDB variable is
public afDB: AngularFireDatabase
in the constructor. I know the database is working because I can update things, send messages in chat channels and they appear in the firebase console, but for whatever reason, when a user joins a channel, I get the successful console log, but the database is not updating with the new information. I am really lost on this one and could really use some help.
it seems to me you want to use a set instead of update
this.afDB.database.ref('/chat/members/' + type + '/' + user_id)
.set({active: true, joinedAt: firebase.database.ServerValue.TIMESTAMP});

wit.ai + story with 2 or more conversations to get entities

I am trying different stories in wit.ai. Here is one scenario where I want to report lost credit card. When user says he lost credit card, bot has to ask his SSN followed by mother/maiden name in 2 steps and then it has to block the card. Here is the application link:
https://wit.ai/Nayana-Manchi/CreditCardApp/stories/f7d77d9e-e993-428f-a75e-2e86f0e73cb3
Issues:
In the entites list I found that, it takes only 2nd input (i.e. mother name in this case, SSN is null) in the entities list when it calls action. I put some logs in JavaScript code to find the entities list.
Do I need to follow slot based approach for these scenarios as well?
Slot based approach is not suitable here, as the user does not know what are the security questions.
In actions tab only if (has/doesn’t have) options are there. Kindly explain its usage. If I set required entities (in this case: SSN and mother name) there, bot asks for SSN continuously like a loop.
Code is similar to quickstart sample with some changes to read entities.
You should save enetities belongs to same session in send Action.
send(request, response) {
const {sessionId, context, entities} = request;
const {text, quickreplies} = response;
const motherName = userSession[sessionId].fbid;
const motherName = firstEntityValue(entities, 'mother_name');
const SSN = firstEntityValue(entities, 'SSN');
// set context in user sessions to used in actions
// act as merge operation of old wit.ai api
if (motherName && SSN) {
sessions[sessionId].context.motherName = firstEntityValue(entities, 'mother_name');
sessions[sessionId].context.SSN = firstEntityValue(entities, 'SSN');
}
return new Promise(function (resolve, reject) {
console.log("Sending.. " ,text);
resolve();
});
},
To use it in custom actions
//to use saved entities from customAction
findCreditCard({sessionId, context, text, entities}) {
const SSN = sessions[sessionId].context.SSN;
const motherName = sessions[sessionId].context.motherName;
return new Promise(function (resolve, reject) {
// custom action code
//if everything gets completed then set context.done=true
if(completed) context.done=true
resolve(context);
});
});
To stop it from re running your actions, delete conext
wit.runActions(
sessionId,
text, // the user's message
sessions[sessionId].context // the user's current session state
).then((context) => {
console.log('Wit Bot haS completed its action', context);
// this will clear the session data
if (context['done']) {
console.log("clearing session data");
delete sessions[sessionId];
}
else {
console.log("updating session data");
// Updating the user's current session state
sessions[sessionId].context = context;
}
}).catch((err) => {
console.log('Oops! Got an error from Wit: ', err.stack || err);
});

How to query objects in the CloudCode beforeSave?

I'm trying to compare a new object with the original using CloudCode beforeSave function. I need to compare a field sent in the update with the existing value. The problem is that I can't fetch the object correctly. When I run the query I always get the value from the sent object.
UPDATE: I tried a different approach and could get the old register ( the one already saved in parse). But the new one, sent in the request, was overridden by the old one. WHAT?! Another issue is that, even thought the code sent a response.success(), the update wasn't saved.
I believe that I'm missing something pretty obvious here. Or I'm facing a bug or something...
NEW APPROACH
Parse.Cloud.beforeSave('Tasks', function(request, response) {
if ( !request.object.isNew() )
{
var Task = Parse.Object.extend("Tasks");
var newTask = request.object;
var oldTask = new Task();
oldTask.set("objectId", request.object.id);
oldTask.fetch()
.then( function( oldTask )
{
console.log(">>>>>> Old Task: " + oldTask.get("name") + " version: " + oldTask.get("version"));
console.log("<<<<<< New Task: " + newTask.get("name") + " version: " + newTask.get("version"));
response.success();
}, function( error ) {
response.error( error.message );
}
);
}
});
OBJ SENT {"name":"LLL", "version":333}
LOG
I2015-10-02T22:04:07.778Z]v175 before_save triggered for Tasks for user tAQf1nCWuz:
Input: {"original":{"createdAt":"2015-10-02T17:47:34.143Z","name":"GGG","objectId":"VlJdk34b2A","updatedAt":"2015-10-02T21:57:37.765Z","version":111},"update":{"name":"LLL","version":333}}
Result: Update changed to {}
I2015-10-02T22:04:07.969Z]>>>>>> Old Task: GGG version: 111
I2015-10-02T22:04:07.970Z]<<<<<< New Task: GGG version: 111
NOTE: I'm testing the login via cURL and in the parse console.
CloudCode beforeSave
Parse.Cloud.beforeSave("Tasks", function( request, response) {
var query = new Parse.Query("Tasks");
query.get(request.object.id)
.then(function (oldObj) {
console.log("-------- OLD Task: " + oldObj.get("name") + " v: " + oldObj.get("version"));
console.log("-------- NEW Task: " + request.object.get("name") + " v: " + request.object.get("version"));
}).then(function () {
response.success();
}, function ( error) {
response.error(error.message);
}
);
});
cURL request
curl -X PUT \
-H "Content-Type: application/json" \
-H "X-Parse-Application-Id: xxxxx" \
-H "X-Parse-REST-API-Key: xxxxx" \
-H "X-Parse-Session-Token: xxxx" \
-d "{\"name\":\"NEW_VALUE\", \"version\":9999}" \
https://api.parse.com/1/classes/Tasks/VlJdk34b2A
JSON Response
"updatedAt": "2015-10-02T19:45:47.104Z"
LOG
The log prints the original and the new value, but I don't know how to access it either.
I2015-10-02T19:57:08.603Z]v160 before_save triggered for Tasks for user tAQf1nCWuz:
Input: {"original":{"createdAt":"2015-10-02T17:47:34.143Z","name":"OLD_VALUE","objectId":"VlJdk34b2A","updatedAt":"2015-10-02T19:45:47.104Z","version":0},"update":{"name":"NEW_VALUE","version":9999}}
Result: Update changed to {"name":"NEW_VALUE","version":9999}
I2015-10-02T19:57:08.901Z]-------- OLD Task: NEW_VALUE v: 9999
I2015-10-02T19:57:08.902Z]-------- NEW Task: NEW_VALUE v: 9999
After a lot test and error I could figure out what was going on.
Turn out that Parse is merging any objects with the same class and id into one instance. That was the reason why I always had either the object registered in DB or the one sent by the user. I honestly can't make sense of such behavior, but anyway...
The Parse javascript sdk offers an method called Parse.Object.disableSingeInstance link that disables this "feature". But, once the method is called, all object already defined are undefined. That includes the sent object. Witch means that you can't neither save the sent object for a later reference.
The only option was to save the key and values of the sent obj and recreate it later. So, I needed to capture the request before calling disableSingleInstance, transform it in a JSON, then disable single instance, fetch the object saved in DB and recreate the sent object using the JSON saved.
Its not pretty and definitely isn't the most efficient code, but I couldn't find any other way. If someone out there have another approach, by all means tell me.
Parse.Cloud.beforeSave('Tasks', function(request, response) {
if ( !request.object.isNew() ) {
var id = request.object.id;
var jsonReq;
var Task = Parse.Object.extend("Tasks");
var newTask = new Task;
var oldTask = new Task;
// getting new Obj
var queryNewTask = new Parse.Query(Task);
queryNewTask.get(id)
.then(function (result) {
newTask = result;
// Saving values as a JSON to later reference
jsonReq = result.toJSON();
// Disable the merge of obj w/same class and id
// It will also undefine all Parse objects,
// including the one sent in the request
Parse.Object.disableSingleInstance();
// getting object saved in DB
oldTask.set("objectId", id);
return oldTask.fetch();
}).then(function (result) {
oldTask = result;
// Recreating new Task sent
for ( key in jsonReq ) {
newTask.set( key, jsonReq[key]);
}
// Do your job here
}, function (error) {
response.error( error.message );
}
);
}
});
If I were you, I would pass in the old value as a parameter to the cloud function so that you can access it under request.params.(name of parameter). I don't believe that there is another way to get the old value. An old SO question said that you can use .get(), but you're claiming that that is not working. Unless you actually already had 9999 in the version...
edit - I guess beforeSave isn't called like a normal function... so create an "update version" function that passes in the current Task and the version you're trying to update to, perhaps?
Rather than performing a query, you can see the modified attributes by checking which keys are dirty, meaning they have been changed but not saved yet.
The JS SDK includes dirtyKeys(), which returns the keys that have been changed. Try this out.
var attributes = request.object.attributes;
var changedAttributes = new Array();
for(var attribute in attributes) {
if(object.dirty(attribute)) {
changedAttributes.push(attribute);
// object.get(attribute) is changed and the key is pushed to the array
}
}
For clarification, to get the original attribute's value, you will have to call get() to load those pre-save values. It should be noted that this will count as another API request.
Hey this worked perfectly for me :
var dirtyKeys = request.object.dirtyKeys();
var query = new Parse.Query("Question");
var clonedData = null;
query.equalTo("objectId", request.object.id);
query.find().then(function(data){
var clonedPatch = request.object.toJSON();
clonedData = data[0];
clonedData = clonedData.toJSON();
console.log("this is the data : ", clonedData, clonedPatch, dirtyKeys);
response.success();
}).then(null, function(err){
console.log("the error is : ", err);
});
For those coming to this thread in 2021-ish, if you have the server data loaded in the client SDK before you save, you can resolve this issue by passing that server data from the client SDK in the context option of the save() function and then use it in the beforeSave afterSave cloud functions.
// eg JS client sdk
const options = {
context: {
before: doc._getServerData() // object data, as loaded
}
}
doc.save(null, options)
// #beforeSave cloud fn
Parse.Cloud.beforeSave(className, async (request) => {
const { before } = request.context
// ... do something with before ...
})
Caveat: this wouldn't help you if you didn't have the attributes loaded in the _getServerData() function in the client
Second Caveat: parse will not handle (un)serialization for you in your cloud function, eg:
{
before: { // < posted as context
status: {
is: 'atRisk',
comment: 'Its all good now!',
at: '2021-04-09T15:39:04.907Z', // string
by: [Object] // pojo
}
},
after: {
status: { // < posted as doc's save data
is: 'atRisk',
comment: 'Its all good now!',
at: 2021-04-09T15:39:04.907Z, // instanceOf Date
by: [ParseUser] // instanceOf ParseUser
}
}
}

Categories

Resources