Receiving updates from the server across all devices - http

I'm using Angular 2 to send requets to my Laravel PHP API and show them in my client.
So far, I used the following code to check for new updates from the server and update my data (making an API call in interval of 3 seconds):
ngOnInit()
{
this.dataRefresh = setInterval(() => {
this.dataInit();
}, 3000);
}
dataInit()
{
this.http.get('APICALL')
.map((res:Response) => res.json())
.subscribe(
data => {
this.data = data;
},
err => {
console.error(err);
});
}
Now I'm thinking further. Let's say 50 computers are connected in the same time to my app. Each of them making an API call every 3 seconds, which means the server will have to handle about 16 requests every second.
My question is:
Can I handle this scenario using Angular 2 / Laravel?
If no, what's the current way to do this?
Thanks in advance.

You can use a real time database like firebase.
Instead of your clients asking for updates to your API every second, your API will let know the clients when there is any update.
You can find firebase tutorials here & here.
There are also some packages available like angular2-firebase and angularfire2 to help your Angular2 app connect to your firebase db easily.

Related

Firestore Native Client SDK cold start? (React Native Firebase)

In short: Is there some kind of cold start when connecting to Firestore directly from Client SDK
Hey. I'm using Firestore client sdk in Andoid and IOS application through #react-native-firebase.
Everything works perfectly but I have noticed weird behavior I haven't found explanation.
I have made logging to see how long it takes from user login to retrieve uid corresponding data from Firestore and this time has been ~0.4-0.6s. This is basically the whole onAuthStateChanged workflow.
let userLoggedIn: Date;
let userDataReceived: Date;
auth().onAuthStateChanged(async (user) => {
userLoggedIn = new Date();
const eventsRetrieved = async (data: UserInformation) => {
userDataReceived = new Date();
getDataDuration = `Get data duration: ${(
(userDataReceived.getTime() - userLoggedIn.getTime()) /
1000
).toString()}s`;
console.log(getDataDuration)
// function to check user role and to advance timing logs
onUserDataReceived(data);
};
const errorRetrieved = () => {
signOut();
authStateChanged(false);
};
let unSub: (() => void) | undefined;
if (user && user.uid) {
const userListener = () => {
return firestore()
.collection('Users')
.doc(user.uid)
.onSnapshot((querySnapshot) => {
if (querySnapshot && querySnapshot.exists) {
const data = querySnapshot.data() as UserInformation;
data.id = querySnapshot.id;
eventsRetrieved(data);
} else errorRetrieved();
});
};
unSub = userListener();
} else {
if (typeof unSub === 'function') unSub();
authStateChanged(false);
}
});
Now the problem. When I open the application ~30-50 minutes after last open the time to retrieve uid corresponding data from Firestore will be ~3-9s. What is this time and why does it happen? And after I open the application right after this time will be low again ~0.4-0-6s.
I have been experiencing this behavior for weeks. It is hard to debug as it happens only on build application (not in local environments) and only between +30min interval.
Points to notice
The listener query (which I'm using in this case, I have used also simple getDoc function) is really simple and focused on single document and all project configuration works well. Only in this time interval, which seems just like cold start, the long data retrieval duration occurs.
Firestore Rules should not be slowing the query as subsequent request are fast. Rules for 'Users' collection are as follows in pseudo code:
function checkCustomer(){
let data =
get(/databases/$(database)/documents/Users/$(request.auth.uid)).data;
return (resource.data.customerID == data.customerID);
}
match /Users/{id}{
allow read:if
checkUserRole() // Checks user is logged in and has certain customClaim
&& idComparison(request.auth.uid, id) // Checks user uid is same as document id
&& checkCustomer() // User can read user data only if data is under same customer
}
Device cache doesn't seem to affect the issue as application's cache can be cleaned and the "cold start" still occurs
Firestore can be called from another environment or just another mobile device and this "cold start" will occur to devices individually (meaning that it doesn't help if another device opened the application just before). Unlike if using Cloud Run with min instances, and if fired from any environment the next calls right after will be fast regardless the environment (web or mobile).
EDIT
I have tested this also by changing listener to simple getDoc call. Same behavior still happens on a build application. Replacing listener with:
await firestore()
.collection('Users')
.doc(user.uid)
.get()
.then(async document => {
if (document.exists) {
const data = document.data() as UserInformation;
if (data) data.id = document.id;
eventsRetrieved(data);
}
});
EDIT2
Testing further there has been now 3-15s "cold start" on first Firestore getDoc. Also in some cases the timing between app open has been only 10 minutes so the minimum 30 min benchmark does not apply anymore. I'm going to send dm to Firebase bug report team to see things further.
Since you're using React Native, I assume that the documents in the snapshot are being stored in the local cache by the Firestore SDK (as the local cache is enabled by default on native clients). And since you use an onSnapshot listener it will actually re-retrieve the results from the server if the same listener is still active after 30 minutes. From the documentation on :
If offline persistence is enabled and the listener is disconnected for more than 30 minutes (for example, if the user goes offline), you will be charged for reads as if you had issued a brand-new query.
The wording here is slightly different, but given the 30m mark you mention, I do expect that this is what you're affected by.
In the end I didn't find straight answer why this cold start appeared. I ended up changing native Client SDK to web Client SDK which works correctly first data fetch time being ~0.6s (always 0.5-1s). Package change fixed the issue for me while functions to fetch data are almost completely identical.

How to test otp login in cypress?

How does one E2E test OTP login?
I have set up an OTP login, I want to write a Cypress test for it where the user enters the OTP and gets it in email. How do I write a test for this, given that the OTP changes every time I send an email?
The current solutions I have are:
To create a test account and hardcode a static OTP for it on the server.
To create a mock API with static responses and use that for testing (currently I'm using the actual deployed API for testing)
If I'm understanding your requirement, you can use otplib to bypass the email reading/parsing stage and directly generate the token that would otherwise be sent to the user in an email.
The package cypress-otp is a thin wrapper around otplib, but unfortunately it's not up-to-date and is awfully noisy and hard to follow for such a simple task.
This is how I unraveled the code and updated for Cypress ver 10.10.0:
Install otplib
yarn add -D otplib or npm install otplib --save-dev
Add a task to call otplib from you test
This takes the place of reading a mail and parsing out the token, which you don't need to test because normally a user does that and enters the token into your app under test.
In cypress.config.js
const { defineConfig } = require("cypress");
const otplib = require("otplib");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('task', {
generateToken(secret) {
return otplib.authenticator.generate(secret);
}
})
},
},
});
Example test taken from cypress-otp (simplified)
describe('Example of generating a token for use in OTP tests', () => {
let secret; // in this example, secret is taken from the app page,
// but normally you will pass it in from a fixture
// or an environment variable
beforeEach(() => {
cy.visit('https://otplib.yeojz.dev'); // we use this page as a sample app
// Get the secret
cy.contains("Demo Secret")
.parent().parent()
.find('input').invoke('val')
.should('not.eq', 'Loading...') // simpler than waitUntil()
.then(value => secret = value)
})
it('tests the token entry', () => {
cy.task('generateToken', secret)
.then(token => {
cy.contains('Verify Token').click();
cy.contains('Please input a token')
.parent()
.find('input')
.type(token);
cy.contains('The token is valid in this current window')
.should('be.visible')
})
})
})
This test is the one given in cypress-otp, a simplified version of it which is more easily understood with moderate programming skills.
It's a bit contrived, because the app provides both the secret and the token, and then also verifies it.
The essential part is the cy.task('generateToken', secret) which makes receiving the token very easy.
If your application is sending OTP via emails then I have a solution for you.
Mailhog.
You can point the SMTP to mailhog and all the outbound emails will appear in Mailhog's management console. Something like this.
After that it's a piece of cake. You can access those email inside cypress tests using this plugin.
https://www.npmjs.com/package/cypress-mailhog
It's been two months but I hope this helps.

react-native-firebase see if user has an internet connection

I'm trying to upload something to my firestore database, but if the user doesn't have an internet connection it just tries to upload it foreever withour giving me an error.
Is there a way to cancel it when I don't have a connection?
I see two options:
use react-native-netinfo to detect if there's a connection before uploading, something like
NetInfo.fetch().then(({isConnected}) => {
if (isConnected) {
doSomethingWithFirebase();
}
});
Add a timeout to make it fail, like so:
// make it a promise, if it isn't already
const firebaseResult = new Promise(resolve => {
doSomethingWithFirebase()
.then(() => resolve(true))
.catch(() => resolve(false))
})
// resolve after 30s
const timeout = new Promise(resolve => setTimeout(() => resolve(false), 30 * 1000));
const didUpload = await Promise.race([firebasePromise, timeOut]);
I'd personally go with #2, because you can show the user an error (something like "failed to upload. does your connection work?") but it depends what it's for, like if it's analytics data that you don't want them to know about #1 is good for that.
Edit: as OP pointed out, with #2 the action would take place when the connection came back online again, without notice, which may not be desired behavior. You could indicate that there's an open connection somehow, like with an "uploading" icon, and clear it when it finally resolves (with failure or success).

Google Cloud Print or other service to auto print using C# or PHP

Are there ANY code examples of how to use Google Cloud Print (using the new OAuth 2) and how when a document comes into the Google Cloud Print queue to automatically print it?
Pretty much what I am trying to do is not spend thousands of dollars that when an order is submitted to our online store, that the order automatically gets printed to our printer. Any ideas, pointers, code examples.
I have done a bunch of searching, and a lot of examples using C#, use Google's old service, not the OAuth2, documentation.
Pretty much, I need a service that will sent a print command to our printer when we get an order in. I can write the part from the store to the service, it is the service to the printer part I have a ton of trouble with.
Thanks in advance.
There's a brilliant PHP class you can download and use that does exactly that:
https://github.com/yasirsiddiqui/php-google-cloud-print
The problem with what you want to achieve is that Google Cloud Print is meant for authenticated users submitting their own print jobs. If I understand correctly, you want to have the server submit a print job as a callback after receiving an order. Therefore, print jobs need to be submitted by a service account, not a Google user. This can be done (we use it in production at the company I work for) using a little hack, described here:
Share printer with Google API Service Account
I can't help you with C# or PHP code, basically you need to be able to make JWT authenticated calls to Google Cloud Print, here you are a code snippet in NodeJS, hope it helps:
var request = require('google-oauth-jwt').requestWithJWT();
service.submitJob = function(readStream,callback) {
// Build multipart form data
var formData = {
printerid: cloudPrintConfig.googleId,
title: 'My Title',
content: readStream,
contentType: "application/pdf",
tag: 'My tag',
'ticket[version]': '1.0',
'ticket[print]': ''
};
// Submit POST request
request({
uri: cloudPrintConfig.endpoints.submit,
json: true,
method: 'post',
formData: formData,
jwt: cloudPrintConfig.jwt
}, function (err, res, body) {
if (err) {
callback(err,null);
} else {
if (body.success == false) {
callback('unsuccessful submission',null);
} else {
callback(null, body);
}
}
});
}
Details about JWT credentials can be found here

Connect two Meteor applications using DDP

I have two applications that I need to synchronise. One of them will receive data from users and the other will display the data. Both applications will work on different servers. They could be disconnected at some times and they need to continue working until reconnect, so I will replicate the data from the first application on the second application.
On Meteor documentation I found DDP.connect(url)but I'm not sure how to use it. I found many questions and examples connecting non Meteor applications with Meteor using DDP, but nothing about connecting two Meteor applications.
My first approach was something like this:
Application 1
Items = new Meteor.Collection('items');
Items.insert({name: 'item 1'});
if (Meteor.isServer) {
Meteor.publish('items', function() {
return Items.find();
});
}
Application 2
Items = new Meteor.Collection('items')
if (Meteor.isServer) {
var remote = DDP.connect('http://server1.com/);
remote.onReconnect = function() {
remote.subscribe('items');
var items = Items.find();
console.log(items.count()); // expected to be 1 but get 0
}
}
On the second application, how can I get the items from the first application?
I got a clue from this question How to properly use Meteor.connect() to connect with another Meteor server. I missed it because it was about the old Meteor.connect() that changed to DDP.connect().
This worked on client and server
var remote = DDP.connect('http://server1.com/');
Items = new Meteor.Collection('items', remote);
remote.subscribe('items', function() {
var items = Items.find();
console.log(items.count()); // get 1
});
Now I can watch for changes in application 1 from application 2 using Items.find().observe()
Warning
There is a bug on Meteor that will stop the connection between applications:
https://github.com/meteor/meteor/issues/1543
DDP between two servers doesn't reconnect
Update
The bug was solved
Update 2
This is a sample project tested with Meteor 0.6.6.2 https://github.com/camilosw/ddp-servers-test

Resources