Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Timestamp from firestore gets converted to a Map when using cloud function

So I have a Timestamp in cloud firestore. I am using cloud function to retrieve data from firestore to flutter. But JSON formats timestamp to map due to which I am not able to use it as timestamp. How to convert it again into timestamp?
This is how I ulpoaded timestamp to firestore.

var reference = Firestore.instance.collection('posts');
      reference.add({
        'postTitle': this.title,
        'timestamp': DateTime.now(),
        'likes': {},
        'ownerId': userId,
      })

To retrieve data this is the code:

 factory Post.fromJSON(Map data){
    return Post(
      timestamp: data['timestamp'],
    );
  }
List<Post> _generateFeed(List<Map<String, dynamic>> feedData) {
    List<Post> listOfPosts = [];

    for (var postData in feedData) {
      listOfPosts.add(Post.fromJSON(postData));
    }

    return listOfPosts;
  }

but this returns an error.

I/flutter (17271): The following assertion was thrown building FutureBuilder<DocumentSnapshot>(dirty, state:
I/flutter (17271): _FutureBuilderState<DocumentSnapshot>#1536b):
I/flutter (17271): type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Timestamp'

This is my cloud function.
getFeed.ts

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

export const getFeedModule = function(req, res){
    const uid = String(req.query.uid);

    async function compileFeedPost(){
        const following = await getFollowing(uid, res)as any;

        let listOfPosts = await getAllPosts(following, res);

        listOfPosts = [].concat.apply([], listOfPosts);

        res.send(listOfPosts);
    }

    compileFeedPost().then().catch();
}

async function getAllPosts(following, res) {
    let listOfPosts = [];

    for (let user in following){
        listOfPosts.push( await getUserPosts(following[user], res));
    }
    return listOfPosts;
}

function getUserPosts(userId, res){
    const posts = admin.firestore().collection("posts").where("ownerId", "==", userId).orderBy("timestamp")

    return posts.get()
    .then(function(querySnapshot){
        let listOfPosts = [];

        querySnapshot.forEach(function(doc){
            listOfPosts.push(doc.data());
        });

        return listOfPosts;
    })
}

function getFollowing(uid, res){
    const doc = admin.firestore().doc(`user/${uid}`)
    return doc.get().then(snapshot => {
        const followings = snapshot.data().followings;

        let following_list = [];

        for (const following in followings){
            if (followings[following] === true){
                following_list.push(following);
            }
        }
        return following_list;
    }).catch(error => {
        res.status(500).send(error)
    })
}

cloud function index.ts

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { getFeedModule } from "./getFeed"
admin.initializeApp();

export const getFeed = functions.https.onRequest((req, res) => {
    getFeedModule(req, res);
})


invoked by this

_getFeed() async {
    print("Starting getFeed");
    FirebaseUser user = await FirebaseAuth.instance.currentUser();

    SharedPreferences prefs = await SharedPreferences.getInstance();

    String userId = user.uid;
    var url =
        'https://us-central1-jaluk-quiz.cloudfunctions.net/getFeed?uid=' + userId;
    var httpClient = HttpClient();

    List<QuizViewer>listOfPosts;
    String result;
    try {
      var request = await httpClient.getUrl(Uri.parse(url));
      var response = await request.close(); 
      if (response.statusCode == HttpStatus.ok) {
        String json = await response.transform(utf8.decoder).join();
        prefs.setString("feed", json);
        List<Map<String, dynamic>> data =
            jsonDecode(json).cast<Map<String, dynamic>>();
        listOfPosts = _generateFeed(data);
        result = "Success in http request for feed";
      } else {
        result =
            'Error getting a feed: Http status ${response.statusCode} | userId $userId';
      }
    } catch (exception) {
      result = 'Failed invoking the getFeed function. Exception: $exception';
    }
    print(result);

    setState(() {
      feedData = listOfPosts;
    });
  }
like image 724
Me BMan Avatar asked May 21 '19 19:05

Me BMan


People also ask

What is the format of firestore timestamp?

firestore. Timestamp. A Timestamp represents a point in time independent of any time zone or calendar, represented as seconds and fractions of seconds at nanosecond resolution in UTC Epoch time. It is encoded using the Proleptic Gregorian Calendar which extends the Gregorian calendar backwards to year one.

How do I change firestore timestamp to date?

To convert a Firestore date or timestamp to a JavaScript Date, we use firebase. firestore. Timestamp. fromDate to convert the a date to a Firestore timestamp.

How does firebase timestamp compare?

You can do this by comparing the seconds and nanoseconds properties on the Timestamp objects. Or, to make it simpler, and you don't need nanosecond precision, you can just compare the results of the results of their toMillis() values. This user is second on the weekly Google Cloud leaderboard.


2 Answers

If you are dealing with a Timestamp that's been serialized as an object with seconds and nanoseconds components, you can use those components to create a new Timestamp object with new Timestamp(seconds, nanoseconds).

like image 190
Doug Stevenson Avatar answered Oct 03 '22 07:10

Doug Stevenson


Indeed, timestamps gets returned as plain Map when using Cloud functions. But if you use Firebase SDK it returns Timestamp object. I use the following function to handle both cases:

/// https://stackoverflow.com/a/57865272/1321917
DateTime dateTimeFromTimestamp(dynamic val) {
  Timestamp timestamp;
  if (val is Timestamp) {
    timestamp = val;
  } else if (val is Map) {
    timestamp = Timestamp(val['_seconds'], val['_nanoseconds']);
  }
  if (timestamp != null) {
    return timestamp.toDate();
  } else {
    print('Unable to parse Timestamp from $val');
    return null;
  }
}

Works perfectly with json_annotation lib:

  @JsonKey(
      fromJson: dateTimeFromTimestamp,
      toJson: dateTimeToTimestamp,
      nullable: true)
  final DateTime subscriptionExpiryDate;
like image 29
Andrey Gordeev Avatar answered Oct 03 '22 09:10

Andrey Gordeev