Argument "options" is not a valid SetOptions error on Firestore - javascript

I'm trying to copy my real time database contents to firestore. But I'm getting this error when I use the set function.
exports.copyPosts = functions.https.onRequest((req, res) => {
var i = 0;
var username;
db.ref("feeds").child("all").limitToLast(2000).once("value", function (postSnap) {
console.log(postSnap.numChildren());
postSnap.forEach(function(topic){
i = i + 1;
console.log(topic.key);
firestore.collection("topics").doc("all").collection(i+"").set({
caption: topic.child("caption").val(),
time: topic.child("time").val(),
username: topic.child("username").val(),
category: topic.child("category").val(),
pic: topic.child("pic").val()
},function(error) {
if (error) {
alert("Data could not be saved." + error);
} else {
alert("Data saved successfully.");
}
});
if(postSnap.numChildren()==i){
res.contentType('application/json');
res.status(200).send("Success");
}
});
});
});
Error Log
Error: Argument "options" is not a valid SetOptions. Input is not an
object.
at exports.(anonymous function) (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/validate.js:86:15)
at Object.exports.(anonymous function) [as isOptionalSetOptions] (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/validate.js:91:35)
at WriteBatch.set (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/write-batch.js:268:14)
at DocumentReference.set (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/reference.js:425:8)
at /user_code/index.js:2172:47
at /user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/api/DataSnapshot.js:126:20
at LLRBNode.inorderTraversal (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/core/util/SortedMap.js:170:13)
at LLRBNode.inorderTraversal (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/core/util/SortedMap.js:169:27)
at LLRBNode.inorderTraversal (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/core/util/SortedMap.js:169:27)
at LLRBNode.inorderTraversal (/user_code/node_modules/firebase-admin/node_modules/#firebase/database/dist/cjs/src/core/util/SortedMap.js:169:27)

The API documentation for the set() method states that it takes two arguments:
data: A map of the fields and values for the document.
options: (Optional) An object to configure the set behavior.
You're passing it two arguments, an object, and a function. It appears that you expect the function to be called in the event of an error, but that's not how the documentation is saying that it works. The error you're getting the API seeing that you're not passing a valid options object at the optional second parameter.
If you're interested in the result of the set() operation, pay attention to the promise that it returns. The convention is that a promise will be rejected in the case of an error. If you're going to be writing Cloud Functions code, you definitely need to be familiar with how promises work.

Related

TypeError: Cannot read properties of undefined (reading 'location') in res.redirect

I have the following function to retrieve an object from a database and extract an URL:
async redirect(id: string, redirectFunction: Function) {
if (!IdExists(id)) {
throw new Error(`ID ${id} does not exist.`);
}
const redirectLocation: string = await prisma.url.findUnique({
where: { uniqueId: id },
select: { url: true },
}).then((data) => {
return data?.url!;
});
redirectFunction('http://' + redirectLocation);
}
The function is called in the following segment of code:
app.get('/:id', async (req, res) => {
try {
redirectController.redirect(req.params.id, res.redirect);
} catch (error) {
console.error(error);
}
});
However, I get the TypeError: Cannot read properties of undefined (reading 'location'), I see that the error is related to the res.redirect method. However, when I replace it by console.log for debugging, the URL is showed properly. What may be causing this error?
This line of code:
redirectController.redirect(req.params.id, res.redirect);
Passes res.redirect (a function reference) as the second argument, but all that is passed is just the function so the res gets lost when you later try to call it. That causes the method to have a wrong this value when it executes and lots of things go wrong.
You can fix that several different ways. Once such way is with .bind():
redirectController.redirect(req.params.id, res.redirect.bind(res));
.bind() creates a small stub function that remembers the value of res so that when the stub function is called, it will be called with the right res reference and thus the this value inside the function will be correct.
Another way to solve it is to create your own little stub function:
redirectController.redirect(req.params.id, (...args) => {
res.redirect(...args);
});
When it calls your stub function, you call res.redirect() properly and pass it whatever arguments the controller called your stub function with.
As a small demonstration, you can see this effect here:
const obj = {
greeting: "Hello",
talk: function() {
if (this && this.greeting) {
console.log(`this.greeting is "${this.greeting}"`);
} else {
console.log("value of this is wrong");
}
}
}
console.log("calling as obj.talk()");
obj.talk(); // works
console.log("-------------------------");
// function we pass a method to and then call that method
function callTalk(fn) {
fn();
}
console.log("calling by passing method to another function");
callTalk(obj.talk); // doesn't work
// call it using .bind()
console.log("-------------------------");
console.log("calling using .bind()");
callTalk(obj.talk.bind(obj)); // works

How to define types properly in error handler

I wrote simple function for error handling in typescript
async function exitOnError() {
const errors = ['SIGTERM', 'SIGINT']
errors.map((type) => {
process.once(type, async () => {
try {
console.log(`Error process.on ${type}`);
await service.disconnect;
} finally {
console.log(`Unexpected error happen on ${type}`);
process.kill(process.pid, type);
}
});
});
}
I am getting following error when I use typescript.
No overload matches this call.
The last overload gave the following error.
Argument of type 'string' is not assignable to parameter of type 'Signals'.ts(2769)
process.d.ts(406, 17): The last overload is declared here.
(parameter) type: string
How can I fix it I tried if guards but still get error. As I understand it is Signal type. And how can I improve to to proper typescript syntax.

Angular-ui-router : Got error when assign number to resolve's property

I use angularjs v1.6.3. I got an error when assign a number to resolve's property of state.
.state('products-new', {
url: "/rent/whats-new/",
resolve: {
pageType: 2,
}
...
}
This is the error message i have got :
Error: ng:areq
Bad Argument
Argument 'fn' is not a function, got number
Can anyone explain to me why this happen ?
As I said in my comment, Resolves should either be a function which returns the value or a string to a service that gets injected. For more information look at the documentation I've linked above. So change your code to the below and all should work.
.state('products-new', {
url: "/rent/whats-new/",
resolve: {
pageType: function () {
return 2;
},
}
}

Can't return more than one value from a sinon stub

I have a method that I want to stub using sinon, so that the first time it is called it returns one value, and then returns a different value on the second call. However currently only the first value is being returned. My code is in Typescript and uses Sinon and Bluebird (promises).
import sinon = require('sinon')
import * as MyService from "../main/Service"
import * as Promise from "bluebird"
it("test things", function(done) {
let serviceStub = sinon.stub(MyService, 'method')
serviceStub.onFirstCall().returns(Promise.reject("rejected"))
.onSecondCall().returns(Promise.resolve("resolved"))
MyService.method().then(function(value) {
console.log("success 1: "+value.value())
}, function(error) {
console.log("error 1: "+error)
})
MyService.method().then(function(value) {
console.log("success 2: "+value.value())
}, function(error) {
console.log("error 2: "+error)
})
done()
})
I presume I must be doing something wrong with the stubbing as this is the first time I have used sinon. If it returns Promise.reject("rejected") and then Promise.resolve("resolved") as I would expect it to, it would have the following output.
error 1: rejected
success 2: resolved
However it just prints out the same error both times, so the onSecondCall() method isn't working. The first value I gave it, Promise.reject("rejected"), is being returned both times the method is called.
error 1: rejected
error 2: rejected
Does anyone know what I'm doing wrong with my stubbing?
Note: For anyone unfamiliar with bluebird/promises, in the method then(function(value){}, function(error){}) the first function handles what happens if the promise is resolved and the second function handles what happens if the promise is rejected .
I think your usage is probably correct, but the dependencies are messing up; due to the following test:
I tried your example (for simplicity in js since there were only import statements from es6/typescript) and with slight modifications it works as intended.
So perhaps by removing taking one step at a time from the working to broken can show you which component is misbehaving.
The following code uses native Promises from Node v6.6 with value.value() replaced by simply value, as string does not contain a method 'value'
let sinon = require('sinon')
let MyService = { method() {}}
let serviceStub = sinon.stub(MyService, 'method')
serviceStub.onFirstCall().returns(Promise.reject("rejected"))
.onSecondCall().returns(Promise.resolve("resolved"))
MyService.method().then(function (value) {
console.log("success 1: " + value)
}, function (error) {
console.log("error 1: " + error)
})
MyService.method().then(function (value) {
console.log("success 2: " + value)
}, function (error) {
console.log("error 2: " + error)
})
returns
>node sinon.js
error 1: rejected
success 2: resolved

How to pass error object to socket.io callback

I am using callbacks with socket.io
Client code :
socket.emit('someEvent', {data:1}, function(err, result) {
console.log(err.message);
});
Server code :
socket.on('someEvent', function(data, callback) {
callback(new Error('testing error'));
});
With the above code the client side always prints out undefined. If I change the server side code to the following I can see the error message.
socket.on('someEvent', function(data, callback) {
callback({message:'testing error'});
});
I can pass my own custom objects to the client just fine, just not the error object. Any ideas?
socket.io data is serialized as JSON, which can only represent plain objects. You will need to serialize any errors into a recognizable plain-object format, or let Socket.IO's standard serialization do its thing (which will result in a plain empty object for Error instances.
I also think it's odd that Socket.IO doesn't seem to provide explicit built in support for passing Error objects in a meaningful way.
If you want to have Error objects seralized correctly in Socket.IO callbacks you can define a method like this to specify how serialization with .toJSON for Error messages should be handled:
if (!('toJSON' in Error.prototype)) {
Object.defineProperty(Error.prototype, 'toJSON', {
value: function () {
let result = {}
Object.getOwnPropertyNames(this).forEach((key) => {
if (key === 'stack') return
result[key] = this[key];
}, this)
return result
},
configurable: true,
writable: true
})
}
If you are throwing messages from the server you will want to define this on the server, and if throwing errors from a client to the server you will need to define it on the client too.
Note: In this example it strips the 'stack' property when returning errors to the client (to avoid exposing internals to a client), but this may be something worth leaving in, at least for development mode.
Update: This is a lot easier with ES6
Adapted from a blog post I wrote recently on this:
https://medium.com/#iaincollins/error-handling-in-javascript-a6172ccdf9af
I'm happy to say this easier/cleaner with ES6.
If you define your class extending Error like this:
class ValidationError extends Error {
constructor(message) {
super(message)
this.name = 'ValidationError'
this.message = message
}
toJSON() {
return {
error: {
name: this.name,
message: this.message,
stacktrace: this.stack
}
}
}
}
Instead of this being the object that gets passed back:
{ name: 'ValidationError' }
It now looks something like this:
{
error: {
name: 'ValidationError',
message: 'A validation error',
stacktrace: '…'
}
}
You can see what to expect by doing this:
console.log(JSON.stringify(new ValidationError('A validation error'))
Here's a little router function that works with plain error objects. You might want to remove the stack variable if you're shy.
socket.on('api', async (method, args, callback) {
try {
const result = await api[method](...args)
callback(null, result)
} catch (error) {
const { name, message, stack } = error
callback(error.toJSON ? error : { name, message, stack })
}
})

Categories

Resources