I am creating multiple consumers in the loop by which I am able to listen to multiple queues. The issue in this approach is I am able to get the events from the different queues but all the queues are using the same consumer so it is hard to recognize for which queue this event happens. It will be good if I will get the queue name under the consumer section.
consumer.Received += async (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
};
The ea variable has some interesting fields, have you check that?
ea.Exchange shows this message has published from which exchange.
ea.RoutingKey shows the route info of the message. probably have the queue name in it.
Also, you can put your headers in the message when your are defining them.
IBasicProperties props = channel.CreateBasicProperties();
props.Headers.Add("queueName", "myQueue1");
channel.BasicPublish(exchangeName,
routingKey, props,
messageBodyBytes);
and in the consumer function you can read them :
consumer.Received += async (model, ea) =>
{
var name = ea.BasicProperties.Headers["queueName"];
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
};
A solution is using "consumerTag" in channel.BasicConsume
channel.BasicConsume(..., consumerTag: "YourQueueName");
And then you can retrieve the queue name in message listner:
consumer.Received += async (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var queueName = ea.ConsumerTag;
};
Warning: In the case that ConsumerTag is important to your system, do not use this solution.
There is no queue field on the message headers or fields, based on my re-search of message object.
The only fields related to queue name are from dead-lettered messages, which save in the headers object of properties the x-first-death-queue.
Related
I'm making a snippet that sends data to a website that analyses it then sends back results.
Is there any way to make my GetAsych wait until the website finishes its calculation before getting a "full response"?
Ps: The await will not know if the page requested contains any asynchronous processing (eg: xhr calls)- I already use await and ReadAsByteArrayAsync()/ReadAsStringAsync()
Thank you!
You will need something like Selenium to not only fetch the HTML of the website but to fully render the page and execute any dynamic scripts.
You can then hook into some events, wait for certain DOM elements to appear or just wait some time until the page is fully initialized.
Afterwards you can use the API of Selenium to access the DOM and extract the information you need.
Example code:
using (var driver = new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)))
{
driver.Navigate().GoToUrl(#"https://automatetheplanet.com/multiple-files-page-objects-item-templates/");
var link = driver.FindElement(By.PartialLinkText("TFS Test API"));
var jsToBeExecuted = $"window.scroll(0, {link.Location.Y});";
((IJavaScriptExecutor)driver).ExecuteScript(jsToBeExecuted);
var wait = new WebDriverWait(driver, TimeSpan.FromMinutes(1));
var clickableElement = wait.Until(ExpectedConditions.ElementToBeClickable(By.PartialLinkText("TFS Test API")));
clickableElement.Click();
}
Source: https://www.automatetheplanet.com/webdriver-dotnetcore2/
What you're looking for here is the await operator. According to the docs:
The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes.
Sample use within the context of an HttpClient object:
public static async Task Main()
{
// send the HTTP GET request
var response = await httpClient.GetAsync("my-url");
// get the response string
// there are other `ReadAs...()` methods if the return type is not a string
var getResult = response.Content.ReadAsStringAsync();
}
Note that the method that encloses the await-ed code is marked as async and has a return type of Task (Task<T> would also work, depending on your needs).
I'm looking for a way to prevent writing more than a given limit of documents to a (sub)collection in a given periode.
For example: Messenger A is not allowed to write more then 1000 Messages per 24 hours.
This should be done in the context of an Firebase Function API endpoint because it's called by third parties.
The endpoint
app.post('/message', async function(req:any, res:any) {
// get the messenger's API key
const key = req.query.key
// if no key was provided, return error
if(!key) {
res.status(400).send('Please provide a valid key')
return
}
// get the messenger by the provided key
const getMessengerResult = await admin.firestore().collection('messengers')
.where('key', '==', key).limit(1).get()
// if there is no result the messenger is not valid, return error
if (getMessengerResult.empty){
res.status(400).send('Please provide a valid key')
return
}
// get the messenger from the result
const messenger = getMessengerResult.docs[0]
// TODO: check if messenger hasn't reached limit of 1000 messages per 24 hours
// get message info from the body
const title:String = req.body.title
const body: String = req.body.body
// add message
await messenger.ref.collection('messages').add({
'title':title,
'body':body,
'timestamp': Timestamp.now()
})
// send response
res.status(201).send('The notification has been created');
})
One thing I've tried was the following piece of code in place of the TODO::
// get the limit message and validate its timestamp
const limitMessageResult = await messenger.ref.collection('messages')
.orderBy('timestamp',"desc").limit(1).offset(1000).get()
if(!limitMessageResult.empty) {
const limitMessage = limitMessageResult.docs[0]
const timestamp: Timestamp = limitMessage.data()['timestamp']
// create a date object for 24 hours ago
const 24HoursAgo = new Date()
24HoursAgo.setDate(24HoursAgo.getDate()-1)
if(24HoursAgo < timestamp.toDate()) {
res.status(405).send('You\'ve exceeded your messages limit, please try again later!')
return
}
}
This code works, but there is a big BUT. The offset does indeed skip the 1000 results, but Firebase will still charge you for it! So every time the messenger tries to add 1 message, 1000+ are read... and that's costly.
So I need a better (cheaper) way to do this.
One thing I've come up with, but haven't yet tried would be adding an index/counter field to a message that increases by 1 every message.
Then instead of doing:
const limitMessageResult = await messenger.ref.collection('messages')
.orderBy('timestamp',"desc").limit(1).offset(1000).get()
I could do something like:
const limitMessageResult = await messenger.ref.collection('messages')
.where('index','==', currentIndex-1000).limit(1).get()
But I was wondering if that would be a save way.
For example, what would happen if there are multiple request at the same time.
I would first need to get the current index from the last message and add the new message with index+1. But could two requests read, and thus write the same index? Or could this be handled with transactions?
Or is there a totally different way to solve my problem?
I have a strong aversion against using offset() in my server-side code, precisely because it makes it seem like it's skipping documents, where it's actually reading-and-discarding them.
The simplest way I can think of to implement your maximum-writes-per-day count is to keep a writes-per-day counter for each messenger, that you then update whenever they write a message.
For example, you could do the following whenever you write a message:
await messenger.ref.collection('messages').add({
'title':title,
'body':body,
'timestamp': Timestamp.now()
})
const today = new Date().toISOString().substring(0, 10); // e.g. "2020-04-11"
await messenger.ref.set({
[today]: admin.firestore.FieldValue.increment(1)
}, { merge: true })
So this adds an additional field to your messenger document for each day, whee it then keeps a count of the number of messages that messenger has written for that day.
You'd then use this count instead of your current limitMessageResult.
const messageCount = (await messenger.get()).data()[today] || 0;
if (messageCount < 1000) {
... post the message and increase the counter
}
else {
... reject the message, and return a message
}
Steps left to do:
You'll want to secure write access to the counter fields, as the messenger shouldn't be able to modify these on their own.
You may want to clean out older message counts periodically, if you're worried about the messenger's document becoming too big. I prefer to leave these types of counters, as they give an opportunity to provide some stats cheaply if needed.
I have a Realtime DB in Firebase and have setup an agent in Google Cloud's Dialogflow. This agent agent is fetching data about bus route names. The end user is asked for a bus number and the agent should get relevant info based on that route number. I can call the database but only for a set bus number.
So for example below I can pull in bus info for 100 based on having the snapshot.child set to 100. But I want the snapshot.child to change based on the askBus parameter from Dialogflow. Any suggestions?
function handleBus(agent) {
const bus = agent.parameters.bus;
agent.add(`Thank you...`);
return admin.database().ref('Routes').once("value").then((snapshot) => {
var routeInfo = snapshot.child('100/route_desc').val();
var routeName = snapshot.child('100/route_long_name').val();
agent.add(`Bus info is ` + routeInfo + ' and is called ' + routeName);
In general, the best way to handle this is to reference the node of the bus number as part of setting up the path to the query. Getting it once you have the result is certainly possible, but means you're pulling in a lot more data than you need to for each query.
But there are a few ways to do this.
The one most similar to how you're doing it now is to generate a string that includes the route number. This example shows how to do it using a back-quote, which is available in the most recent JavaScript, or you can just do string concatenation:
function handleBus(agent) {
const bus = agent.parameters.bus;
agent.add(`Thank you...`);
return admin.database().ref('Routes').once("value").then((snapshot) => {
var routeInfo = snapshot.child(`${bus}/route_desc`).val();
var routeName = snapshot.child(`${bus}/route_long_name`).val();
agent.add(`Bus info is ` + routeInfo + ' and is called ' + routeName);
But if you're just looking for the information from that route, you can setup the reference to the database to include the route, get the entire result and its value, and then treat this as a JavaScript object.
function handleBus(agent) {
const bus = agent.parameters.bus;
agent.add(`Thank you...`);
return admin.database().ref('Routes').child(bus).once("value").then((snapshot) => {
var route = snapshot.val();
var routeInfo = route['route_desc'];
var routeName = route['route_long_name'];
agent.add(`Bus info is ` + routeInfo + ' and is called ' + routeName);
As an aside, I want to point out that you're using Promises perfectly. That is a trap many people fall into, and you've done a good job querying the value through a Promise, handling it as part of Promise fulfillment, and returning a Promise in your handler.
In the webhook use async call to firebase to fetch the bus information.
Fetch the parameter value.
Access Firebase DB.
Fetch information based on parameter using async call.
Use promise to reply back with the correct response. See this for responding via promise.
Promise would be used inside your Firebase function when it fetches the DB information.
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.
I'm building a control where there is visual feedback as progress is made for the server responding to the input from the client.
The control will be visible on multiple clients at once, and I want the client that made the change on the control to get slightly different feedback to all the others that will see less information about the state changes.
Is there a meteor inbuilt function to uniquely identify each client which I could use for this? If not, how could I go about making a non-repudiated identifier? It would need to identify two different tabs in the same browser as two different clients.
I couldn't find any easy way to do that built into Meteor, but you can try this depending on your exact use-case.
Here is a technique to track unique client connections per browser window or tab. Each connectionId below can be thought of as a chat room. Since the this.connection.id property inside a Meteor method is not unique per open window or tab, this will store the connection id along with a timestamp inside a collection. When the client closes the browser tab or window, you can use the callback inside the server method this.connection.onClose to lookup that particular connection by its id along with the timestamp and flag it as closed or offline.
Fiber = Npm.require('fibers');
Future = Npm.require('fibers/future');
Meteor.methods({
'client.disconnect': function(connectionId){
check(connectionId, String);
let query = {_id: connectionId};
let options = {$set: {isOnline: false}};
return Connections.update(query, options);
},
'client.connect': function(connectionId){
check(connectionId, String);
let lastSessionTime = Number(new Date().getTime());
let lastSessionId = this.connection.id;
let offlineQuery = {
_id: connectionId,
lastSessionTime: lastSessionTime,
lastSessionId: lastSessionId
}
let offlineOptions = {
$set: {isOnline: false}
}
// When the connection closes, turn this connection offline.
this.connection.onClose(function(){
// You could also remove the document.
Connections.update(offlineQuery, offlineOptions);
});
let onlineQuery = {
_id: connectionId
}
let onlineOptions = {
$set: {
lastSessionTime: lastSessionTime,
lastSessionId: lastSessionId,
isOnline: true}
}
var future = new Future();
Connections.upsert(onlineQuery, onlineOptions, function(err, res){
if (err){
future.throw('Connections.online error');
}else{
future.return(res);
}
});
}
});