Timestamp not working with firebase-functions-test - javascript

I'm using firebase-admin 8.6.0 and firebase-functions-test 0.1.6 which has support for Firestore Timestamps in testing (as of 0.1.5 https://github.com/firebase/firebase-functions-test/releases) but am still receiving an error message when trying to use them with test.firestore.makeDocumentSnapshot.
Can someone help me understand the error in my implementation?
import * as admin from 'firebase-admin';
admin.initializeApp();
const db = admin.firestore();
const settings = { timestampsInSnapshots: true};
db.settings(settings);
const timestamp = admin.firestore.FieldValue.serverTimestamp();
const testingTimestamp1 = admin.firestore.Timestamp.now();
const testingTimestamp2 = admin.firestore.Timestamp.fromDate(new Date);
import * as TestFunctions from 'firebase-functions-test';
const firebaseConfig = {
databaseURL: 'https://...HIDDEN...',
projectId: '...HIDDEN...',
storageBucket: '...HIDDEN...appspot.com',
}
const test = TestFunctions(firebaseConfig, 'service-account-dev.json');
const data({
timestamp,
testingTimestamp1,
testingTimestamp2,
});
const snap = test.firestore.makeDocumentSnapshot(data, path);
const wrapped = test.wrap(processImport);
await wrapped(snap, {params: testParams});
I can't get any of the three timestamp options to work. The latter one I tried learning from #the0rem in https://github.com/firebase/firebase-functions-test/pull/28 but to no avail. I always receive this error:
Cannot encode [object Object]to a Firestore Value. Local testing does not yet support Firestore geo points.`

I was excited when I saw your question because I just faced the same issue. Anyway, here is how I finally solved it. This is the JSON data that I saved from my Firestore database:
const customer = {
username: "A8tAz6wdtucMNKvWSgDkx4bquc2",
timestamp: {
_seconds: 1578762627,
_nanoseconds: 828000000
},
role: "user"
};
Note that timestamp is just a plain object with two properties: _seconds and _nanoseconds. This is where the error "Cannot encode [object Object]to a Firestore Value." comes from i.e. the data object, customer, contains another object, timestamp, which Firestore is unable to parse. What we do to solve this is to make sure that timestamp is not a plain object but an instance of admin.firestore.Timestamp. And here is how you do that:
const seconds = customer.timestamp._seconds;
const nanosecs = customer.timestamp._nanoseconds;
// create a new Timestamp object passing in the required constructor arguments
const properTimestamp = new admin.firestore.Timestamp(seconds, nanosecs);
customer.timestamp = properTimestamp; // update the timestamp reference in your data object
// Now you can do this
test.firestore.makeDocumentSnapshot(customer, '/users/A8tAz6wdtucMNKvWSgDkx4bquc2')
Now if you do console.log(customer); you will see that the timestamp object is an instance of admin.firestore.Timestamp, here is the console output:
// OUTPUT
{
username: 'A8tAz6wdtucMNKvWSgDkx4bquc2',
timestamp: Timestamp { _seconds: 1578762627, _nanoseconds: 828000000 },
role: 'user'
}
PS: I got the answer by checking how Firebase was trying to parse the values/objects in the data. Here is where the parsing was failing and the exception was being thrown (in the file: ../node_modules/firebase-functions-test/lib/providers/firestore.js:165:15):
if (val instanceof firebase_admin_1.firestore.Timestamp) {
return {
timestampValue: val.toDate().toISOString(),
};
}

Related

agora start method error : post method api body check failed

I'm building a video-calling app using Next js and agora.io 4, I followed the steps mentioned in the Docs.
I enabled agora cloud recording
called the acquire method and got the resourceId.
Then, I called the start method. but it always failed with an error post method API body check failed!
However, it works perfectly on Postman.
Here's the code :
import axios from "axios";
import chalk from "chalk";
// AWS S3 storage bucket credentials
const secretKey = process.env.S3_SECRET_KEY;
const accessKey = process.env.S3_ACCESS_KEY;
const bucket = process.env.S3_BUCKET_NAME;
const region = process.env.S3_BUCKET_REGION;
const vendor = process.env.S3_VENDOR;
//agora credentials
const appId = process.env.APP_ID;
const key = process.env.KEY;
const secret = process.env.SECRET;
export default async function startHandler(req, res) {
//call agora start method
const { uid, cname, resourceId, token } = req.body;
const plainCredential = `${key}:${secret}`;
const encodedCredential = Buffer.from(plainCredential).toString("base64"); // Encode with base64
const authorizationField = `Basic ${encodedCredential}`;
const data = {
uid,
cname,
clientRequest: {
recordingConfig: {
streamMode: "standard",
channelType: 0,
subscribeUidGroup: 0,
},
storageConfig: {
accessKey,
region,
bucket,
secretKey,
vendor,
},
},
};
const headers = {
"Content-Type": "application/json",
Authorization: authorizationField,
};
const startUrl = `https://api.agora.io/v1/apps/${appId}/cloud_recording/resourceid/${resourceId}/mode/individual/start`;
try {
const response = await axios.post(startUrl, data, {
headers,
});
res.status(200).send(response.data);
} catch (error) {
console.error(error);
res.send(error);
}
}
Any help/hint would be much appreciated
I found the fix!
First, you may be tricked by the uid returned from the agora join method, it's returning a Number, surprisingly! the start method
expect the uid to be a string, so don't forget to do a
uid.toString().
In the storageConfig object, you should check the type of each of its attributes. each of region and vendor is expected to be of type Number. That said, if you're storing this info in a .env file, remember that environment files only stores strings. Therefore, you should convert them to Numbers!
This problem took me 2 days, so I hope this will be useful for you!

Cloud Functions: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore

Hi I have this function written
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { setDoc, doc } = require("firebase/firestore");
admin.initializeApp();
const db = admin.firestore();
// // Create and Deploy Your First Cloud Functi
exports.idiot = functions.pubsub.schedule('* * * * *').onRun(async (context) => {
// // Add a new document with a generated id
const moment = require('moment')
console.log("testtesttest 603")
console.log(db.collection)
const Questions =
{
Questions: {
QoD: "test",
Upvotes: 0,
}
}
const Comments =
{
Comments: {
Body: "hello",
Replies: [],
Upvotes: 0
}
}
await setDoc(doc(db, "Questions", moment().format('MMM Do YYYY')), Questions);
await setDoc(doc(db, "Comments", moment().format('MMM Do YYYY')), Comments);
});
And suddenly I am getting this error:
2022-09-28T00:59:00.846793Z ? idiot: FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore
2022-09-28T00:59:00.848764071Z D idiot: Function execution took 9 ms. Finished with status: error
But it was writing to the document before.
Is there any reason why this error would pop up when the script was working before?
You're importing two different SDKs here:
const admin = require("firebase-admin");
const { setDoc, doc } = require("firebase/firestore");
That first line imports the Firebase Admin SDK, which is designed to be used in trusted environments such as Cloud Functions.
The second line imports two functions from the client-side JavaScript SDK, which is made for use in regular applications.
While both SDKs have largely the same API, the two are not binary compatible. So the db variable points to a Firestore object the Admin SDK, while your doc function is expecting a Firestore object from the client-side JavaScript SDK.
To only use the Admin SDK:
const questionRef = db.doc("Questions", moment().format('MMM Do YYYY'));
await questionRef.set(Questions);
And then the equivalent for the comments.
In the documentation, you'll want to look for the v8 code samples for this syntax, like here in the documentation on writing to a document reference.

Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object

I'm following Firebase's instructions and my functions is as follows:
import { DataSource, DataSourceConfig } from "apollo-datasource";
import { KeyValueCache } from "apollo-server-caching";
import firebase from "firebase";
import admin from "firebase-admin";
import "#firebase/firestore";
import { Request } from "apollo-server-env";
export class FirebaseDataSource<TContext = any> extends DataSource {
context!: TContext;
db: firebase.firestore.Firestore;
constructor({
firebaseConfig,
serviceAccount,
databaseURL,
}: {
serviceAccount: any;
firebaseConfig: firebase.app.App;
databaseURL: string;
}) {
super();
this.context;
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL,
});
}
if (!this.db) {
this.db = firebase.firestore();
}
}
async initialize(config: DataSourceConfig<TContext & { request: Request }>) {
this.context = config.context;
}
async user_updateRestaurantFavorites(data: {
uid: string;
someId: string;
add: boolean;
}) {
const collectionRef = this.db.collection("users");
const documentRef = collectionRef.doc(data.uid);
let favorites;
if (data.add) {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayUnion(
data.someId
),
});
} else {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayRemove(
data.someId
),
});
}
return favorites;
}
}
export default FirebaseDataSource;
I dubugged it and I do pass the uid, add, and someId correctly.
someId is a string and add is a boolean (true)
When I run this, I get:
Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object (found in field favorites in document users/XXXXXXX)
I am just running their own function with a simple string.
Below is an image of my firestore showing the user record does indeed have an empty array ready to accept strings
What am I doing wrong?
You're mixing up the web client and admin client SDKs. You can't use the FieldValue objects exported by firebase-admin when calling methods exported by firebase. The error message is coming from the web client SDK, and it's effectively telling you that you passed an object that it doesn't understand (from the Admin SDK).
You should pick one or the other, and completely remove the one you aren't using in order to avoid problems. If this runs on a backend, you should only use the Firebase Admin SDK, and skip the web client altogether. If you do this, you will need to assign this.db using the admin SDK, probably as this.db = admin.firestore().
Firebase can only store primitive types, maps and array of same. In your case you are saving the result of admin.firestore.FieldValue.arrayUnion(...) for the property favorites.
My guess is that the result is not returning a supported type. I have not used FieldValue before ... is that the correct way to use the API?
It simply means that you need to send exact data which was received by the query. Partial object not allowed
db.collection("users").where("name", "==", somename).limit(1).get().then(query => {
console.log(query);
const thing = query.docs[0];
console.log(thing.data());
let tmp = thing.data();
tmp.current_game_play = tmp.current_game_play + 1;
console.log(tmp);
thing.ref.update(tmp);
});

Adding a new field in Firestore

In firestore, a field is like a column in mysql. How can I add a field in every existing document in Firestore using Javascript?
I am interested to add an empty array field in every document.
I thought this is common but somehow I could not find any relevant info.
const firebase = require("firebase");
require("firebase/firestore");
firebase.initializeApp({
apiKey: "XXXX",
authDomain: "xxxx",
projectId: "xxxx"
});
var db = firebase.firestore();
function getMyDocs() {
const snapshot =
firebase.firestore().collection('testusers').get()
return snapshot.docs.map(doc => {
db.collection("testusers").doc(doc.id).update({
title: "Testing"
})
});
};
getMyDocs();
This is a nodejs code but I am getting TypeError: Cannot read property 'map' of undefined
You have to fetch all docs and update each one seperatly.
JS Code:
async getMyDocs() {
const snapshot = await db.collection('myCollection').get()
return snapshot.docs.map(doc => {
db.collection("myCollection").doc(doc.id).update({myNewField:"fieldValue"})
});
}
Firestore is nosql.There are no columns or rows.
Instead, you store data in documents, which are organized into collections.
Each document contains a set of key-value pairs. The structure is dynamic.
You can think it like a big json object.

Get a document snapshot from the database and convert its ServerValue.TIMESTAMP value to a date

When we read a DocumentSnapshot from the database, the value of all ServerValue.TIMESTAMP fields is { '.sv': 'timestamp' }. How can we read this value as a Date or an equivalent time-related value?
This is what we have tried:
// create a document reference
const documentReference = firestoreDb.collection('test').doc();
// write the document to the database
const writeResult = await documentReference.set({
timestamp: firebase.database.ServerValue.TIMESTAMP
});
// read the document from the database
const documentSnapshot = await documentReference.get();
// the timestamp value is { '.sv': 'timestamp' }
console.log(documentSnapshot.data().timestamp);
You're trying to use a Realtime Database server timestamp. That's different than a Firestore server timestamp. Use this instead:
Node SDK
import { FieldValue } from '#google-cloud/firestore';
const timetamp = FieldValue.serverTimestamp();
Web SDK
const timetamp = firebase.firestore.FieldValue.serverTimestamp();

Categories

Resources