1
votes

Comment utiliser Geofirex dans les fonctions Google Cloud

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!');
  });

});


0 commentaires

3 Réponses :


1
votes

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.

Dans Cloud Functions, vous utilisez le SDK Firebase Admin pour Node.js , qui n'a pas le concept de 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. :)


4 commentaires

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'); 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



1
votes

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)


0 commentaires

2
votes

- C'est peut-être la solution complète -

Deux étapes: -

  1. Ajout de Geopoint et Geohashes dans Firestore
  2. 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


2 commentaires

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.