J'ai un cas d'utilisation dans lequel je dois utiliser la bibliothèque geofirex dans Google Cloud Functions. La fonction init de la bibliothèque attend une instance de FirebaseApp. Est-il possible de l'initialiser dans les fonctions du cloud afin d'interroger la base de données Firestore en utilisant firebase-admin?
Ce que j'ai essayé ..
fonctions> src> index.ts
TypeError: app.firestore is not a function at new GeoFireCollectionRef (/user_code/node_modules/geofirex/dist/index.cjs.js:1465:24) at GeoFireClient.collection (/user_code/node_modules/geofirex/dist/index.cjs.js:1639:16) at exports.geofenceUserNotifications.functions.https.onRequest (/user_code/lib/index.js:2197:13) at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:57:9) at /var/tmp/worker/worker.js:783:7 at /var/tmp/worker/worker.js:766:11 at _combinedTickCallback (internal/process/next_tick.js:73:7) at process._tickDomainCallback (internal/process/next_tick.js:128:9)
functions> package.json
{ "name": "functions", "scripts": { "lint": "tslint --project tsconfig.json", "build": "tsc", "serve": "npm run build && firebase serve --only functions", "shell": "npm run build && firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", "logs": "firebase functions:log" }, "main": "lib/index.js", "dependencies": { "firebase-admin": "~6.0.0", "firebase-functions": "^2.1.0", "moment": "^2.23.0", "firebase": "^5.10.1", "geofirex": "0.0.6", "rxjs": "^6.3.3", "rxjs-compat": "^6.5.1" }, "devDependencies": { "tslint": "~5.8.0", "typescript": "~2.8.3" }, "private": true }
Aucune erreur lors du déploiement, mais lorsque j'appelle la fonction geofenceUserNotifications, j'obtiens cette erreur:
import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; admin.initializeApp(); admin.firestore().settings({timestampsInSnapshots: true}); // Init GeoFireX import * as firebase from 'firebase/app'; import * as geofirex from 'geofirex'; const geoFire = geofirex.init(firebase); exports.geofenceUserNotifications = functions.https.onRequest((req, res) => { if(!req || !req.body || !req.body.length || !req.body[0].latitude || !req.body[0].longitude || !req.body[0].uid || !req.body[0].userFcmToken){ // end function with 401 status res.status(401).send('Authentication required.'); } const uid = req.body[0].uid; const userFcmToken = req.body[0].userFcmToken; const latitude = req.body[0].latitude; const longitude = req.body[0].longitude; const field = 'position'; geoFire.collection('users').setPoint(uid, field, latitude, longitude).then(result => { res.end('User geoPoint updated!'); }).catch( err => { console.log(err); res.end('error when update user geo point!'); }); });
3 Réponses :
Lors de la deuxième inspection, il semble que l ' administrateur Firebase Le SDK pour Node.js a le concept de FirebaseApp . Vous pouvez essayer de passer admin.app ()
dans GeofireX pour l'initialiser.
FirebaseApp
.
D'après une analyse rapide, il semble que GeofireX n'a été conçu que pour être utilisé avec le SDK JavaScript côté client de Firebase. Vous voudrez peut-être déposer une demande de fonctionnalité sur son dépôt , ou mieux encore un PR. :)
Merci @Frank pour votre aide. j'ai essayé const geoFire = geofirex.init (admin.app ());
ne fonctionne pas mais je le fais d'une autre manière, d'abord const geofirex = require ('geofirex'); code> puis
const geoFire = geofirex.init (admin.app ());
et je pense que cela fonctionne mais maintenant j'ai rencontré un autre problème lorsque j'appelle la fonction, j'obtiens TypeError: this.app.firestore .GeoPoint n'est pas un constructeur chez GeoFirePoint.get [as geoPoint] (/user_code/node_modules/geofirex/dist/index.cjs.js:1390:20) chez GeoFirePoint.get [as data] (/ user_code / node_modules / geofirex / dist / index.cjs.js: 1411: 31) à GeoFireCollectionRef.setPoint (/ u ....
Ouais ... bien que GeoFireX puisse être utilisé dans votre contexte, il semble plus axé sur le Web. Vous allez devoir fouiller dans le code source de GeoFireX pour cette trace de pile pour savoir ce qui l'a provoquée. C'est ce que j'ai fait pour arriver à ma réponse révisée ci-dessus, qui a apparemment résolu votre premier problème. Il s'agit de voir vers quoi pointe la trace de la pile et de travailler à rebours à partir de là.
J'ai tracé le code source de GeoFireX mais je n'ai pas pu déterminer la cause du problème: (la ligne à node_modules / geofirex / dist / index.cjs.js: 1390
est: return new this.app .firestore.GeoPoint (this.latitude, this.longitude);
TypeError: this.app.firestore.GeoPoint n'est pas un constructeur chez GeoFirePoint.get peut être résolu en appelant .data à la fin
le problème a été résolu comme suit:
const query = geoFire.collection('places', queryRef).within(center, radius, field); const nearbyPlaces = await get(query); // await query.pipe(operators.first()).toPromise();
Puis
exports.geofenceUserNotifications = functions.https.onRequest(async (req, res) => { //setPoint (id, field, lat, lng) geoFire.collection('users').setPoint(uid, field, latitude, longitude).then(result => { res.end('User geo point is updated!'); }).catch( err => { res.end('error when update user geo point'); return; }); });
Et geoFireX fonctionne bien.
mais maintenant je suis confronté à un autre problème avec la requête geoFireX.
J'ai essayé d'obtenir des éléments à proximité, mais geoFireX renvoie Observable
et cela ne fonctionne pas dans les fonctions du cloud.
J'ai essayé d'utiliser get () par geoFireX:
const functions = require('firebase-functions'); const admin = require('firebase-admin'); admin.initializeApp(); admin.firestore().settings({timestampsInSnapshots: true}); // Init GeoFireX const geofirex = require('geofirex');// <-- Use require Instead of import geofirex import { get } from 'geofirex'; const geoFire = geofirex.init(admin); // <-- admin not admin.app()
mais malheureusement cela ne fonctionne pas. et j'obtiens l'erreur suivante:
Erreur: l'argument "onNext" n'est pas une fonction valide. à Validator. (fonction anonyme) .values [as isFunction] (/srv/node_modules/@google-cloud/firestore/build/src/validate.js:99:27) à Query.onSnapshot (/srv/node_modules/@google-cloud/firestore/build/src/reference.js:1524:25) à Observable._subscribe (/srv/node_modules/geofirex/dist/index.cjs.js:1597:33) à Observable._trySubscribe (/srv/node_modules/rxjs/internal/Observable.js:44:25) à Observable.subscribe (/srv/node_modules/rxjs/internal/Observable.js:30:22) à MapOperator.call (/srv/node_modules/rxjs/internal/operators/map.js:32:23) à Observable.subscribe (/srv/node_modules/rxjs/internal/Observable.js:25:31) à Object.subscribeToResult (/srv/node_modules/rxjs/internal/util/subscribeToResult.js:12:23) à CombineLatestSubscriber._complete (/srv/node_modules/rxjs/internal/observable/combineLatest.js:76:46) sur CombineLatestSubscriber.Subscriber.complete (/srv/node_modules/rxjs/internal/Subscriber.js:78:18)
- C'est peut-être la solution complète -
Deux étapes: -
- Ajout de Geopoint et Geohashes dans Firestore
- Requête avec des données de géopoint
Solution de l'étape 1: Pour cela, vous devez ajouter firebase-admin sdk et configurer le geofirex avec geofirex.init (admin). Ex:
router.get('/expert', (req, res, next) => { const radius = req.query.radius const lat = req.query.lat const lng = req.query.lng const field = 'position' geo.collection('ex-pending').within(geo.point(lat, lng), radius, field).subscribe(snap => { res.status(200).send(snap) }) })
maintenant vous pouvez stocker vos Geopoint et Geohashes comme ceci: -
import * as firebase from 'firebase'; var firebaseConfig = { apiKey: API_KEY_HERE, authDomain: DOMAIN_HERE, databaseURL: FIREBASE_REALTIME_DATABASE_URL, projectId: PROJECT_ID, storageBucket: STORAGE_BUCKET, messagingSenderId: MESSAGING_SENDER_ID, appId: FIREBASE_APP_ID, measurementId: MEASUREMENT_ID }; firebase.initializeApp(firebaseConfig); import geofirex = require('geofirex');// <-- Now, use import Instead of require('geofirex') const geo = geofirex.init(firebase.app()); // <-- firebase or, firebase.app()
Stockez ceci dans la base de données en utilisant Firestore.
Solution de l'étape 2: Pour cela, vous devez utiliser firebase web sdk pour configurer le geofirex. Sinon, vous ne pourrez pas interroger votre base de données Firestore! Ex:
const point = geo.point(40.1, -119.1);
Vous pouvez obtenir firebaseConfig à partir de la configuration du sdk Web de la console Firebase. Copiez et collez cela! ;) Vous pouvez maintenant interroger votre géopoint Firestore comme ceci: -
const geofirex = require('geofirex');// <-- Use require Instead of import geofirex import { get } from 'geofirex'; const geo = geofirex.init(admin); // <-- admin not admin.app()
C'est tout! : D
Encore une chose! Si vous rencontrez un problème avec la méthode "subscribe" de geofirex, vous pouvez l'utiliser pour interroger: const promise = geo.collection ('ex-pending'). Within (geo.point (lat, lng), radius, field) const snap = await get (promise)
c'est la méthode d'attente asynchrone au lieu de RxJs.
ceci montre un exemple pour un backend node js et non des fonctions cloud. Ils sont très similaires, mais dans mon cas, j'utilise la méthode https.onCall et cela ne fonctionne pas pour moi.