Getting started querying sync data from Realm - xamarin.forms

I'm attempting to get a list of items from a MongoDB Atlas instance via Realm into my Xamarin Forms application. I'm pretty sure I've followed the instructions correctly, however I can't seem to see any values come in.
I'm using Realm and Realm.Fody version: 10.2.0
I've uploaded my data with the following script
mongoimport --uri mongodb+srv://[user]:[pass]#cluster0.[wxyz].mongodb.net/[company] --collection products --type json --file products --jsonArray
1057 document(s) imported successfully. 0 document(s) failed to import.
When I go to MongoDB Atlas, I see this
In my App.xaml.cs constructor, I create a realm app and log in with anonymous credentials
public const string MasterDataPartitionKey = "master_data";
ctor()
{
var app = Realms.Sync.App.Create("my-app-id");
app.LogInAsync(Credentials.Anonymous()).ContinueWith(task => User = task.Result);
}
After this, in my first ViewModel constructor (it's Rx, but isn't doing anything fancy)
ctor()
{
_listProductsCommand = ReactiveCommand.Create<Realm, IEnumerable<Product>>(ExecuteLoadProducts);
_listProductsCommand.ThrownExceptions.Subscribe(x => { /* checking for errors here */ }).DisposeWith(TrashBin);
Initialize
.ObserveOn(RxApp.MainThreadScheduler)
// note MasterDataPartitionKey is the value I've set on every single record's `_partitionKey` property.
.Select(_ => new SyncConfiguration(MasterDataPartitionKey, App.User))
.SelectMany(Realm.GetInstanceAsync)
.Do(_ => { }, ex => { /* checking for errors here */ })
.ObserveOn(RxApp.MainThreadScheduler)
.InvokeCommand(this, x => x._listProductsCommand)
.DisposeWith(TrashBin);
}
And later, a simple query
private static IEnumerable<Product> ExecuteLoadProducts(Realm realm)
{
var data = realm.All<ProductDto>();
var productDtos = data.ToList();
var products = productDtos.Select(x => x.Map());
return products;
}
Expected:
productDtos (and products) should have a count of 1057
Actual:
productDtos (and products) have a count of 0
I've looked in my Realm logs and I can see the connections and sync logs
My anonymous authentication is turned on
and I've made it so that all of my Collections have read access (in fact developer mode is on)
Here's an example of one of the records
Here's a snipped of the dto I'm trying to pull down
I feel as though I must be missing something simple. Can anyone see anything obvious that could be sending me sideways?

Related

Riverpod Flutter: Looking for best way to combine FutureProvider with StateNotifierProvider

I am working on a basic Support Ticket System. I get the Tickets from Firebase (Either as a Stream or Future).
I want to allow some Filtering Options (e.g. sort by Status and Category).
For this, I thought about using A Future Provider to get the List and a StateNotiferProvider to update the List depending on which filter is being used.
This is the code I have so far:
final ticketListStreamProvider =
RP.FutureProvider((_) => FirestoreService.getTicketList());
class TicketListNotifier extends RP.StateNotifier<List<Ticket>> {
TicketListNotifier() : super([]);
void addTicket(Ticket ticket) {
state = List.from(state)..add(ticket);
}
void removeTicket(Ticket ticket) {
state = List.from(state)..remove(ticket);
}
}
final ticketsController =
RP.StateNotifierProvider<TicketListNotifier, List<Ticket>>(
(ref) => TicketListNotifier(),
);
There are multiple issues I have with that. Firstly it doesn't work.
The StateNotifier accepts a List and not a Future<List>. I need to convert it somehow or rewrite the StateNotifier to accept the Future.
I was trying to stay close to one of the official examples.
(https://github.com/rrousselGit/riverpod/tree/master/examples/todos)
Unfortunately, they don't use data from an outside source like firebase to do it.
What's the best approach to get resolve this issue with which combination of providers?
Thanks
You can fetch your ticketlist in your TicketListNotifier and set its state with your ticket list.
class TicketListNotifier extends RP.StateNotifier<List<Ticket>> {
TicketListNotifier() : super([]);
Future<void> fetchTicketList() async {
FirestoreService.getTicketList().when(
//success
// state = fetched_data_from_firestore
// error
// error handle
)
}
}
final ticketsController =
RP.StateNotifierProvider<TicketListNotifier, List<Ticket>>(
(ref) => TicketListNotifier(),
);
Call this method where you want to fetch it /*maybe in your widget's initState method
ref.read(ticketsController.notifier).fetchTicketList();
Now ref.read(ticketsController); will return your ticket list
Since you have the ticket list in your TicketListNotifier's state you can use your add/remove method like this:
ref.read(ticketsController.notifier).addTicket(someTicket);

How to check if client's contacts are using my app?

I'm currently developing an app using Firebase.
My Firestore Database looks like below:
Once the user passes the Firebase authentication procedure, I'm creating a user document with a field "Phone:" which contains his phone number. Basically, everyone who is gonna using the app will be listed in the database.
And here is my challenge:
I'm using the plugin easy_contact_picker to store all the contacts of the users device to a List.
How can I find out whether the users contacts are using the app or whether they are listed in the database?
My goal is create a contact List Widget which shows me all my contacts. But those contacts which are using the app or which are listed in the database, should be highlighted or marked particularly.
Which is the best way to realize that if we consider that millions of users (to minimize computing power)are listed in the database?
Anyone has an idea?
Thanks a lot
First of all try to awoid giving everyone access to read all users. That is something most ppl do when handling such a problem. The do it because the query over all users won't work if you don't give the rights to read all of them.
Because of security reasons I would move the logic for checking if a user exists into callable function (not a http function!). That way you can call it inside of your app and check for a single user or multiple of them in an array. That would depend how your frontend would handle it.
Very importand would be to store all phone numbers in the absolute same format. That way you could query for them. Regardless of the number of users you could always find a specific one like here:
var citiesRef = db.collection("users");
var query = citiesRef.where("Phone", "==", "+4912345679");
The numbers need to be absolutely the same without any emtpy spaces - chars and the +49 or 0049 also needs to be the same.
You could create two callable funcitons. One to check if a single user exists in your app and another where you send an array of phone numbers and you get an array back. The cloud function can use Promise.all to performe such queries in parallel so you get your responce quite fast.
I'm using a similar approach to add users in my app as admins to specific groups where you just enter the email of the user and if he is in the app he will be added. I not he get's an invitation on the email to join the App.
With the help of Tarik's answer, Ayrix and I came up with the following solution.
Important: Read Tarik's answer for more information.
Client: callable_compare_contacts.dart
import 'package:cloud_functions/cloud_functions.dart';
Future<List<Object>> getMembersByPhoneNumber(List<String> allPhoneNumbers) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('membersByPhoneNumber');
final results = await callable.call(<String, dynamic>{'allPhoneNumbers': allPhoneNumbers});
return results.data;
}
Server: index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
if (admin.apps.length === 0) {
admin.initializeApp({
credential: admin.credential.applicationDefault(),
});
}
exports.membersByPhoneNumber = functions.https.onCall((data, context) => {
return new Promise((resolve, reject) => {
if (!data || !data.allPhoneNumbers.length) return resolve([]);
const phoneNumbers = data.allPhoneNumbers;
// TODO: different scope? move vars for future use
const db = admin.firestore();
const collectionRef = db.collection("User");
let batches = [];
// because of wrong eslint parsing (dirty)
batches = [];
while (phoneNumbers.length) {
// firestore limits batches to 10
const batch = phoneNumbers.splice(0, 10);
// add the batch request to to a queue
batches.push(
new Promise((response) => {
collectionRef.where("Phone", "in", [...batch]).get()
.then((results) =>
response(results.docs.map(function(result) {
return result.data().Phone;
} )));
})
);
}
// response / return to client
Promise.all(batches).then(function(content) {
// console.log("content.flat()");
// console.log(content.flat());
return resolve(content.flat());
});
});
});
Note: This is our first callable/cloud function .. so Suggestions for changes are welcome.

Is Firestore onSnapshot update event due to local client Set?

Simple version:
When I register to onSnapshot in Firestore, is there a way to know if I am the client that pushed the update?
Detailed Version:
When a webpage is opened, it registers an onSnapshot callback to a Firestore collection called notes. The collection is then downloaded and stored in a local array var notes = [].
var window.notes = []
let notesRef = refs.db.collection('notes')
notesRef.onSnapshot(querySnapshot => {
querySnapshot.docChanges.forEach((docChange) => {
switch (docChange.type) {
case 'added':
notes.push(docChange.doc.data())
break
}
})
}
We then update a note locally and push it to the database.
notes[0] = 'changed contents'
firestore.notesRef.doc(0).set(notes[0])
This will send the note out to firestore, update it in the remote database, then because we're registered to onSnapshot for the notes collection we'll trigger that event and get a modified change.
let notesRef = refs.db.collection('notes')
notesRef.onSnapshot(querySnapshot => {
querySnapshot.docChanges.forEach((docChange) => {
switch (docChange.type) {
case 'added':
notes.push(docChange.doc.data())
break
case 'modified':
// ********** We end up here!!! ***********
// Probably want to do:
notes[docChange.doc.id] = docChange.doc.data()
break
}
})
}
Is there a way to detect if this same client called the set that triggered the modified docChange?
The point is this: If the user made the change themself then they don't care about the change ad we should ignore it. If a different user made the change then this user should be given a choice of whether or not they want to update their local version.
In my experience, when firestore.notesRef.doc(0).set(notes[0]) is called it takes some time to receive the update from the server. If the local user modifies the note again in this time but hasn't pushed it to the server yet then their changes are lost (overridden by the servers version - the version they last sent to the server). It's essentially a race condition.
So, when I register to onSnapshot in Firestore, is there a way to know if I am the client that pushed the update?
You can see if it was a local or remote change with:
docChange.doc.metadata.hasPendingWrites
If hasPendingWrites is true, then origin of the update was local. The local cache has been updated and the write to the server is still pending.
If hasPendingWrites is false, then the origin of the update was remote.
See how it can be used with onSnapshot in this snippet:
let notesRef = refs.db.collection('notes')
notesRef.onSnapshot(querySnapshot => {
querySnapshot.docChanges.forEach((docChange) => {
switch (docChange.type) {
case 'added':
notes.push(docChange.doc.data())
break
case 'modified':
// ********** New Code ***********
let source = docChange.doc.metadata.hasPendingWrites ? 'Local' : 'Server'
if (source === 'Server') {
notes[docChange.doc.id] = docChange.doc.data()
} else {
// Do nothing, it's a local update so ignore it
}
// ********** New Code End ***********
break
}
})
}

PACT Consumer Driven setUP test data in Provider

I am executing some test whereby if Consumer set some ID or any Text which is not exists inside Provider Database then I want to do the below step in Provider Tests
Receive the PACT file with the information as what are the things needs to setup first
Then I will have my function , which will start inserting those unavailable data into DB
Then make API calls to , which will provide the Actual response.
Now I want to know , which field Consumer should use to let Provider know that , there is some prerequisite or pre setup needed before actual API call.
I saw the sample , where there is a setUp : InsertIntoDatabase but doesnot say that how to find which is the input supplied by consumer.
[TestMethod]
public void Ensure_OfferApi_HonoursPact_WithDeal_ForSendingLatestSoftOffer()
{
//Arrange
var outputter = new CustomOutputter();
var config = new PactVerifierConfig();
config.ReportOutputters.Add(outputter);
IPactVerifier pactVerifier = new PactVerifier(() => { InsertEventIntoDatabase(); }, () => { }, config);
pactVerifier
.ProviderState(
"Given the Offer Exist in Offer System I WANT TO See Latest SoftOffer",
setUp: InsertEventsIntoDatabase); // in case you want to insert something
//Act / Assert
using (var client = new HttpClient { BaseAddress = new Uri("http://localhost:9999") })
{
pactVerifier
.ServiceProvider("Offer API", client)
.HonoursPactWith("Consumer")
.PactUri(#"C:\TOSS\TestSample\log\deal-offer.json")
.Verify();
}
// Verify that verifaction log is also sent to additional reporters defined in the config
Assert.IsNotNull(outputter.Output);
}
Lets say the setup function is InsertEventsIntoDatabase and I want to add events what ever consumer is providing via PACT file. so that I dont need to update this code when ever Consumer changes the input.

How do I make an Angular2 application update instantly on all connected devices as the events are fired?

I need to build a restaurant management mobile application with Angular2 and Ionic2 and a website for the same restaurant with Angular2 that constantly make http connections to store and retrieve data from the database in order to maintain the latest data.
For example, If an order request was fired from a waiter's mobile phone, the new order is posted to the database, which I can accomplish. the chef needs to get the instant notification that a new order has been created. Also, the data needs to be reflected in other employees' phones and also on the website.
All I can come up with is the use of setInterval, but since I've never done anything like this before, I'm not sure if this is the correct way.
component
orders: Order[];
constructor(private orderService: OrderService) {
setInterval(function() {
this.orderService
.getOrders()
.subscribe(
(orders: Order[]) => this.orders = orders,
(error: Response) => console.log(error)
)}, 3000);
}
placeanOrder(order) {
this.orderService.postOrder(order);
}
service
getOrders() Observable<any>{
this.http.get(...).map(...);
}
placeOrder(order) {
this.http.post(...).map(...);
}
When I try something like this, I get the same error logged to the console every second.
Cannot read property 'getOrders' of undefined
Why am I getting the error?
How would I convert the Observable json data retrieved from server into my interface data type, in this case, of type Order?
What is a better approach to this?
Cause wrong usage of callbacks! You have to use the arrow-syntax!
wrong: setInterval(function() {
right: setInterval(() => {
Just describe your function with correct types:
getOrders(): Observable<Order[]> {
Nothing else needed. Property-names needs to be the same (interface/json)!
It's ok to use a timer. "Better" could be a never-closed-connection to the server, so server could SEND you that data and client don't have to POLL.
https://www.websocket.org/
http://socket.io/

Resources