Cloud functions error: Converting circular structure to JSON - javascript

Trying to use the admin SDK from Firebase to set custom claims within Firebase Cloud Functions. The issue seems to be the claims object I pass into the function. I understand what a circular object structure is, but I'm not sure why it is happening here.
The Error:
Here is the cloud function code
exports.setCustomClaims2 = functions.https.onCall((uid, claims) => {
return admin.auth().setCustomUserClaims(uid,claims).then(() => {
return {
message: `Success! User updated with claims`
}
})
.catch(err => {
return err;
})
});
And here is the front end code to call it:
let uid = "iNj5qkasMdYt43d1pnoEAIewWWC3";
let claims = {admin: true};
const setCustomClaims = firebase.functions().httpsCallable('setCustomClaims2');
setCustomClaims(uid,claims)
What is interesting is that when I replace the claims parameter directly in the cloud function call like so
admin.auth().setCustomUserClaims(uid,{admin: true})
This seems to work just fine.
Is there a difference in how the object gets received as a parameter?

You're not using the callable type function correctly. As you can see from the documentation, the function you pass to the SDK always receives two arguments, data and context, no matter what you pass from the app. A single object you pass from the app becomes the single data parameter. You can't pass multiple parameters, and that parameter doesn't get broken up into multiple parameters.
What you should do instead is combine uid and claims into a single object, and pass it:
setCustomClaims({ uid, claims })
Then receive it as a single parameter in the function:
exports.setCustomClaims2 = functions.https.onCall((data, context) => {
// data here is the single object you passed from the client
const { uid, claims } = data;
})
I'll note that using console.log in the function will help you debug what your function is doing. If you logged the values of uid and claims, this probably would have been easier to figure out.

Related

RTKQ - Select data without hooks

I'm trying to select cached data from RTKQ without using the auto-generated query hook, but I'm having trouble understanding the docs
const result = api.endpoints.getPosts.select()(state)
const { data, status, error } = result
This is how the docs describe how to access the data, but I can't find any references on how to inject the state object "select()(state)".
I can't figure out how to access the data if I only call the select?
api.endpoints.getPosts.select()
Can someone explain me the difference between "select()" and "select()(state)"
Or what is the optimal solution to access the cached data from RTKQ?
The result of api.endpoints.getPosts.select() is a selector function for the result of using the "getPosts" endpoint without arguments.
Similarly, result of api.endpoints.getPosts.select({ page: 5 }) is a selector function for the result of using the "getPosts" endpoint the argument { page: 5 }.
A selector function is then called as selector(state) or passed into useSelector(selector).
If you write that altogether, you end up with api.endpoints.getPosts.select()(state).
#phry
Thank you for your answer! I'm not 100% sure I understood your answer. But it pointed me in a direction that enabled me to get the data.
I ended up creating a selector like the docs.
export const selectUser = (state) => userApi.endpoints.getUser.select()(state);
and in my function, I referenced it with getting the exported store from configureStore() method
const { data } = selectUser(store.getState());
But I'm not sure if this is the intended way to do it.

Firestore - Store a property id is reference type by firestore Batch transaction

I trying to store data content reference type by batch transaction, then I got an exception:
Function WriteBatch.set() called with invalid data. Unsupported field value: a custom object (found in document orders/OC9dZErupEhPsamp8QEd)
Is there a way we can use batch transaction to store reference type?
this is my code:
batch.update(orderRef, {
userId: firestore.doc(userId),
});
Normaly update() use to update existing firestore data. Review firestore docs for the same. In that given example they are updating population by increments of value or with new population number but before passing it in each update function values are stored in one cost value if it is not static value. as Asked by #dharmaraj please edit your questions by posting with full code you can also read given firestore documentation for your own studies.
import firebase from "firebase/app";
const app = firebase.initializeApp({});
const firestore = app.firestore();
const batch = firestore.batch();
const newUserId = firestore.doc(userId);
batch.update(orderRef, {
userId: newUserId,
});
Log newUserId value and see what are you getting into it.
You can't store the reference object that doc() returns, it's an object that may have circular references and functions in it. doc() is not the id of the document. If you want to get the id (which is a string), then:
const newUserId = firestore.doc(userId).ref.id;
batch.update(orderRef, {
userId: newUserId,
});
I don't know why batch validate input should be a pure object. I tried to push reference type id inside nested object then it work well, yeah I know it already is a trick, but it work.
change:
batch.update(docRef, {
user: firestore.collection('users').doc(userId)
})
to:
batch.update(docRef, {
user: {
id: firestore.collection('users').doc(userId)
}
})

How to get id of an object from firebase

I am trying to get id of an object after set that object. But I am getting type error. TypeError: Cannot read properties of undefined (reading 'val'). How should I do that with firebase 9?
Here is the code that I want to work:
set(push(ref(db, "expenses")), expense)
.then((snapshot) => {
console.log(snapshot.val());
dispatch(
addExpense({
id: snapshot.key,
...expense,
})
);
})
.catch((e) => {
console.log("This failed.", e);
});
Thanks in advance.
Why your code doesn't work
The documentation of set(ref, value) shows that is is defined as:
function set(ref: DatabaseReference, value: unknown): Promise<void>
It returns a Promise<void>, so there's no snapshot being passed to your then.
If the promise resolves (and thus your then callback gets called) that the expense was written to the database on the server as is.
How to fix it
If you want to get the key of the push call, you can capture that outside of the set call already:
const newRef = push(ref(db, "expenses"));
set(newRef, expense)
.then(() => {
dispatch(
addExpense({
id: newRef.key,
...expense,
})
);
})
.catch((e) => {
console.log("This failed.", e);
});
Calling push is a pure client-side operation, which is synchronous, so that doesn't require await or then (which should be used with asynchronous operations).
Further considerations
Note though that now you're only showing the expense locally after it's been written to the server. If that is a requirement for your use-case, then 👍. But when using Firebase it is quite common to:
Use a permanent, onValue listener on expenses to show the latest expenses in the UI.
Write the new expense with a simple call, without a then() listener: set(push(ref(db, "expenses")), expense);
The Firebase SDK will then immediately call the local onValue listener with the new value, with the assumption that the write will succeed.
So your UI will show the local value straight away, giving the user an almost instant response.
In the (more uncommon) case that the server (i.e. your security rules) rejects the write operation, the SDK calls onValue again with the corrected data, so your UI can update the state.

Angular 8: Run Component Class Methods Synchronously/Sequentially

How do I run methods synchronously in Angular Typescript?
These are not functions, but methods.
First one calls a service, and then second saves into an array.
runTwoMethods()
{
this.validateAddress();
this.saveJsonArchive();
}
Validate address may call more sub-method,may not even be Api, so want to wait until everything completes before saving.
Following syntax is for functions, currently searching for class methods,
Angular / TypeScript - Call a function after another one has been completed
At the end, data is stored into a current data object. And I want to be
save to an archive. Maybe another possibility, how do I keep AddressCurrentMailing and JSONArchive[2] in sync?
Current Data object is sourced from API, not sure where (nor I am not allowed to edit the APIs, then calls transformations), and then would like to save into JsonArchive.
this.jsonArchive[this.jsonArchiveCounter] = this.addressCurrentMailingFinalData
You can use Observable for the 1st and use your 2nd method after Subscribe in callback:
validateAddress(): Observable<Someclassname>
{
return this._http.get<Someclassname>('url');
}
this.yourService.validateAddress().subscribe(
(result) => {
this.saveJsonArchive();
},
(error) => console.log('NO saveJsonArchive'));
EDIT (validateAddress with more one Observable)
const ox: Observable<Someclassname>[] = [];
ox.push(this.http.get<Someclassname>(url1));
ox.push(this.http.get<Someclassname>(url2));
ox.push(this.http.get<Someclassname>(url3));
forkJoin(ox).subscribe(result => this.saveJsonArchive());
You could return a Promise in "this.validateAddress();" and await it.
I assume you use the Angular HttpClient.
For example:
private validateAddress(): Promise<someclassname>
{
return this._http.get<someclassname>("someurl").toPromise()
}
async addSeasonal()
{
await this.validateAddress();
await this.saveJsonArchive();
}
I hope this helps.

Firebase firestore - Data must be an object, but it was: a custom Object object

I'm using Firestore in conjunction with realtime database in order to provide a user presence system for my application.
Update: article I followed
https://blog.campvanilla.com/firebase-firestore-guide-how-to-user-presence-online-offline-basics-66dc27f67802
In one of the methods I use this code here:
const usersRef = this.$fireStore.collection('users').doc(uid)
const whoIsOnlineRef = this.$fireDb.ref('.info/connected')
whoIsOnlineRef.on('value', (snapshot) => {
this.$fireDb.ref(`/status/${uid}`)
.onDisconnect()
.set('offline')
.then(() => {
usersRef.set({ online: true }, { merge: true })
this.$fireDb.ref(`/status/${uid}`).set('online')
})
})
The .set method, however, is giving me the error mentioned in the title and I can't quite understand why. I'm simply passing a javascript object to .set method and this should technically work.
Can you spot what is wrong with the code?
Looks like the reason why this wasn't working is that the code was running on the server in an SSR application.
I moved that very same logic to the browser and it started working nicely. I still don't see why this wouldn't work in the server as, at the end of the day I was still passing a simple js object to the .set() method.
Either way, there you have it

Categories

Resources