Add delay to cloud functions's onCall? - firebase

wondering if it is possible to add a delay between onCall requests.
The end goal is to send data to Firestore. I want to reduce abuse such as spam requests, by implementing delays between requests.
If one user sends a request, any "onCall" requests from the same user, should be omitted for the next 10 seconds so I can reduce invocations/ costs. (Returning a message on the user's screen: "Please try again in 10 seconds")
exports.sendToFirestore = functions.https.onCall((data, context, "delay?") => {
//send data to Firestone
});

Related

How do I send push notifications with expo and react native?

I am writing a simple app, where I need to send push notifications. For instance, a user liked a post -> send a push notification; or a user commented under a post -> send a push notification; or a user sent you a message -> send a push notification.
I am using Notifications from expo-notifications. I have set up my Notifications.addNotificationReceivedListener and Notifications.addNotificationResponseReceivedListener. I tested them using Expo's push notification tool and it all works fine.
However, I am struggling to send notifications. As suggested per expo's docs they have a library for Node.js called expo-server-sdk-node which takes care of sending notifications. As per their doc:
The Expo push notification service accepts batches of notifications
so that you don't need to send 1000 requests to send 1000
notifications. We recommend you batch your notifications to reduce
the number of requests and to compress them (notifications with
similar content will get compressed).
And I agree with the above statement. Sending notifications on batch makes sense, however, I have a question regarding this:
How do I implement it? Do I keep counter on the user's notification, and lets say, the user has liked 10 posts -> then I send 10 notifications as a batch request? What if they liked 8 posts and then closed the app? Do I send the notifications on closing the app? It doesn't seem right to me. Also, if the user has sent a message, I believe I should straight away send the notification, rather than waiting for a batch request for the user to send 10 messages.
The implementation they offer on their docs is the following:
// Create the messages that you want to send to clients
let messages = [];
for (let pushToken of somePushTokens) {
// Each push token looks like ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]
// Check that all your push tokens appear to be valid Expo push tokens
if (!Expo.isExpoPushToken(pushToken)) {
console.error(`Push token ${pushToken} is not a valid Expo push token`);
continue;
}
// Construct a message (see https://docs.expo.io/push-notifications/sending-notifications/)
messages.push({
to: pushToken,
sound: 'default',
body: 'This is a test notification',
data: { withSome: 'data' },
})
}
Which is OK and understandable. But then I struggle with the next step:
let chunks = expo.chunkPushNotifications(messages);
let tickets = [];
(async () => {
for (let chunk of chunks) {
try {
let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
console.log(ticketChunk);
tickets.push(...ticketChunk);
} catch (error) {
console.error(error);
}
}
})();
That is, at which point do I call expo.chunkPushNotifications(messages) and then await expo.sendPushNotificationsAsync(chunk). Do I wait 10 similar notifications to be collected, do I wait some time, do I do it when the user closes the app, or something else?

How to send external messages to a specific channel with discord py?

I'm trying to forward messages to channel when I receive messages from api.
I'm using
def trigger(messagecontent):
async def messagesender():
channel = ... #get channel id
await channel.send(messagecontent)
await bot.close()
#bot.event
async def on_ready():
await messagesender()
bot.run(token=Token)`
What happens is, when I receive message from api, I call trigger(messagecontent), the bot logs in, sends message and closes, then the rest of my external script (sync version) executes.
But on the next loop, when new message from api is received, trigger(messagecontent) gives error
Runtime: Session is closed
If I don't close the bot with bot.close(), my sync script will be stuck at discord part. This is why I need to close the bot.
I don't want to use webhooks because there are many channels where messages are to be sent using the same bot.
Why are you shutting down the bot via await bot.close()?
Ofc you will get that error if you shut down the bot.

Best setup for ApiGateway-MicroService communication pattern in MassTransit and RabbitMQ

I am implementing ApiGateway-MicroService communication protocol in my app with MassTransit and RabbitMQ. That protocol is meant to replace "traditional" REST API communication between ApiGateway and Microservices (I am talking about simple request-response here and not about any kind of events, sagas, etc). So on microservice(s) side I have consumers (which respond to requests) and on ApiGateway side I have request clients. Usually microservice has let's say ~10 consumers (for example OrderingMicroservice has consumers for following requests: CreateOrder, UpdateOrder, GetOrderById, ListUserOrders etc). I am trying to figure out best topology (Masstransit + RabbitMQ) for this scenario.
Here are my goals, at least I think it should work like this:
A. Request messages (that are routed to consumer queue) should be durable for short time only (for example 20s) and then removed from the consumer queue (and request client should receive timeout error) and not routed to any other queue. So when microservice is temporary down or it is temporary too busy to receive next request from queue then request messages should be kept in the queue for 20s and then disappear.
B. Since RequestClient should timeout after ~20s, Response messages (that are routed to client "response-queue") should also be durable for short amount of time (~20s). Then they can disappear. If ApiGW is offline / too busy to receive response then response(s) should be discarded.
So basically I want to use MassTransit/RabbitMQ as a short-lived buffer between ApiGW and microservice(s).
// ApiGw MassTransit configuration
services.AddMassTransit(x =>
{
x.SetKebabCaseEndpointNameFormatter();
x.UsingRabbitMq((context, cfg) =>
{
});
x.AddRequestClient<ICreateGroupPayload>();
});
// Service MassTransit configuration
services.AddMassTransit(x =>
{
x.SetKebabCaseEndpointNameFormatter();
var entryAssembly = Assembly.GetEntryAssembly();
x.AddConsumers(entryAssembly);
x.UsingRabbitMq((context, cfg) =>
{
cfg.ConfigureEndpoints(context);
});
});
// Single consumer definition in service
public class CreateGroupActionDefinition : ConsumerDefinition<CreateGroupAction>
{
public CreateGroupActionDefinition()
{
EndpointName = "group-service";
}
}
This setup creates following exchanges and queues:
exchange ICreateGroupPayload (fanout, durable) => bind exchange:group-service
exchange group-service (fanout, durable) => bind queue:group-service
exchange PublicGateway_bus_4wdoyyro5ycgmgbybdcx1gp3r3 (fanout, autoDelete) => bind queue:PublicGateway_bus_4wdoyyro5ycgmgbybdcx1gp3r3
queue group-service (durable)
queue PublicGateway_bus_4wdoyyro5ycgmgbybdcx1gp3r3 (x-expires: 60000)
When I terminate ApiGw following exchanges/queues are removed from RabbitMQ within ~1min:
exchange PublicGateway_bus_4wdoyyro5ycgmgbybdcx1gp3r3
queue PublicGateway_bus_4wdoyyro5ycgmgbybdcx1gp3r3
My questions are:
Should I use separate queues (endpoint names) for different consumers in a microservice? Or I can use same queue (group-service for example) for different consumers/message types?
How I can modify my configuration to set expiration time on my consumer queues? Right now it's durable but I want messages to be removed after ~20s. Also I think such queue should not be deleted after consumer is disconnected because it should be able to send requests even when consumer is offline (but only for 20s).
How I can modify my configuration to set expiration time on my request client response queue to be 20s (currently it seems it's 60s by default?).
Maybe someone have any other suggestions on how to adjust topology to best fit for this scenario? The aim is to have the setup as fast as possible just for simple request-response + short time buffering for edge cases.
All the work is done by MassTransit, as you can understand from the request documentation. You can change the default request timeout from 30 seconds to 20 seconds when adding the request client to the container. There is also an .AddGenericRequestClient() method to automatically add requests clients for whatever request type is needed.
You can also specify the request timeout for each request, and it will set the message TimeToLive to match that value. The responses should be sent with a TimeToLive as needed.

Firebase Cloud Messaging Notification not received in web app

I am using FCM for notification. FCM gets triggered on creation of data from the Firebase database. I received first message. After that other consecutive messages is not received. I'm running this in a local environment. Is the problem due to the below message "Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions" or any other issue. Do I need to get into a billing plan for receiving messages. Working in test environment and that is the reason not moving to billing plan. If the issue is not related to billing plan can someone point any other problem with the code.
Firebase function log
6:22:52.133 PM
sendFollowerNotification
Function execution started
6:22:52.133 PM
sendFollowerNotification
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions
6:22:52.143 PM
sendFollowerNotification
Function execution took 10 ms, finished with status: 'ok'
6:22:52.401 PM
sendFollowerNotification
1 messages were sent successfully
Node js code
exports.sendFollowerNotification = functions.database.ref('/notification/message/{gId}/{pId}')
.onCreate(async (change, context) => {
//console.log('Group id:', context.params.gId," Push ID:",context.params.pId, "Change",change);
const notificationData = change.val();
var topic = notificationData.topic;
var title = notificationData.title;
var body = notificationData.body;
var registrationTokens = notificationData.tokens;
const message = {
notification: {
title: title,
body: body
},
tokens: registrationTokens,
};
admin.messaging().sendMulticast(message)
.then((response) => {
// Response is a message ID string.
console.log(response.successCount + ' messages were sent successfully');
})
.catch((error) => {
console.log('Error sending message:', error);
});
});
That message does not indicate an error. It's just a warning letting you know that outbound networking does not work if your project is not on a payment plan. FCM messaging does not fall in this category - it should work.
The problem is that your code doesn't return a promise that resolves after all asynchronous work is complete. Right now, it returns nothing, and the function terminates immediately before the message is sent. Please read and understand the documentation about this.
Minimally, you will need to return the promise chain to let Cloud Functions know when the message is sent, and it's safe to terminate.
return admin.messaging().sendMulticast(message)
.then((response) => {
// Response is a message ID string.
console.log(response.successCount + ' messages were sent successfully');
})
.catch((error) => {
console.log('Error sending message:', error);
});
Note the return keyword above.
If the message still isn't being sent, then there is some other problem here that we can't see. You might not be handling your device tokens correctly.
I think this might answer your question: Why will I need a billing account to use Node.js 10 or later for Cloud Functions for Firebase?:
Because of updates to its underlying architecture planned for August 17, 2020, Cloud Functions for Firebase will rely on some additional paid Google services: Cloud Build, Container Registry, and Cloud Storage. These architecture updates will apply for functions deployed to the Node.js 10 runtime. Usage of these services will be billed in addition to existing pricing.
In the new architecture, Cloud Build supports the deployment of functions. You'll be billed only for the computing time required to build a function's runtime container.
On the other hand, the Service Firebase Clud Messaging itself is free:
Firebase Cloud Messaging (FCM) provides a reliable and battery-efficient connection between your server and devices that allows you to deliver and receive messages and notifications on iOS, Android, and the web at no cost.
Given that you are using Node in your CFs, the Platform requires to you a Billing Account.

What is the expected timeframe for authentication in firebase?

I'm using the built-in Firebase password authentication and I'm wondering what a "reasonable" timeout for logging in via authWithPassword(). I had thought initially that this would be sub-second but now it appears there is a lot of volatility and even at 3 seconds I'm getting a lot of timeouts.
note: I suspect this might not be the highest priority because for typical client app the logging in process is a one-time affair but for micro-services the headroom of 3 seconds is pretty substantial (most operations overall run time is 1-2 seconds). Happy to be wrong.
This is fairly subjective to the app, but Firebase login should be faster than 3 seconds. If you're consistently seeing long times and connection errors, then you should contact support#firebase.com.
You can also let Firebase handle the timeout and/or errors for you in the callback:
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.authWithPassword({
email : "bobtony#firebase.com",
password : "correcthorsebatterystaple"
}, function(error, authData) {
if (error) {
// this is your login issue
console.error("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});

Resources