How to update a message inside Filter (MassTransit) - masstransit

I created a filter where all messages are being captured.
I am trying to modify a messege using GetOrAddPayload() avaiable in PipeContext intereface, but unfortunately I do not understand how to implement it.
public async Task Send(ConsumeContext<TMessage> context, IPipe<ConsumeContext<TMessage>> next)
{
context.GetOrAddPayload()
await next.Send(context);
}
All messages are type of IMyContract interface that has just one property.
public interface IMyContract
{
object Message { get; }
}
How to implement PayloadFactory for above inteface to make it work?

Related

How can I have one page subscribe to messages from multiple different pages using Xamarin MessagingCenter?

Here's the situation that I have. On DeckPage, I subscribe to a message like this:
public void OnAppearing()
{
RefreshLables();
MessagingCenter.Subscribe<CardFormatPageViewModel>(
this,
"RefreshDeckConfigsLabels",
sender => { RefreshLables(); });
}
private void RefreshLables()
{
-- refresh code
}
public void OnDisappearing()
{
MessagingCenter.Unsubscribe<CardFormatPageViewModel>(this,"RefreshDeckConfigsLabels");
}
This works good and I am sending a message from the CardFormatPageViewModel like this:
MessagingCenter.Send(this, "RefreshDeckConfigsLabels");
I also want to be able to send messages to DeckPage from OptionPageViewModel:
MessagingCenter.Send(this, "RefreshDeckConfigsLabels");
Plus from more models.
Is there a way I can code the subscribe so I can subscribe to one common message that will be accepted if it comes from CardFormatPageViewModel, OptionPageViewModel or the other View Models that need to make it refresh or do I have to do a subscription for every one of them?
sure
MessagingCenter.Send<object>(this, "RefreshDeckConfigsLabels");
and
MessagingCenter.Subscribe<object>(
this,
"RefreshDeckConfigsLabels",
sender => { RefreshLables(); });

How to configure Filter with specific message type?

I want to consume messages only with specific type and properties set. A sort of message content filter before any consumer instance created.
I'm trying to create a filter for specific ConsumeContext:
public class OrderFilter : IFilter<ConsumeContext<CreateOrderMessage>>
{
public Task Send(ConsumeContext<CreateOrderMessage> context, IPipe<ConsumeContext<CreateOrderMessage>> next)
{
if (context.Message.IsTrustedUser)
{
return next.Send(context); // continue processing
}
return Task.CompletedTask; // stop message processing
}
public void Probe(ProbeContext context) { }
}
How can I register such a filter?
I've tried to register it in the endpoint but with no luck. I have
cfg.ReceiveEndpoint("OrderQueue", ep =>
{
ep.UseFilter(new OrderFilter());
ep.Consumer<CreateOrderConsumer>();
});
I have the following error: Cannot convert instance argument type '{MassTransit.IReceiveEndpointConfigurator,MassTransit.RabbitMqTransport.IRabbitMqReceiveEndpointConfigurator}' to 'GreenPipes.IPipeConfigurator<MassTransit.ConsumeContext<Core.CreateOrderMessage>>'
So, there used to be an extension method for this purpose, but I can't find it. You can add the filter prior to the consumer being created by creating a filter specification and adding it as shown below.
var filter = new OrderFilter();
var specification = new FilterPipeSpecification<ConsumeContext< CreateOrderMessage >>(filter);
ep.AddPipeSpecification(specification);
If you want to execute the filter after the consumer has been created (for instance, if you're using container scope to share information), you can use a scope consume filter (which is described in several answers, as well as the documentation) or you can add your filter during consumer configuration.
ep.Consumer<CreateOrderConsumer>(cc =>
{
cc.Message<CreateOrderMessage>(mc => mc.UseFilter(new OrderFilter()));
}

Xamarin.iOS send data from iOS project to Notification Service Extension

In Xamarin.iOS project I am intercepting the push notification and I want to modify it before presenting to the user according to some conditions that are accessible (stored preferences) in the iOS project. How can I pass data from iOS project to the Notification Service Extension, and potentially the other way around as well? Or maybe more precisely, how can I access user preferences from the Xamarin.Forms project in the iOS Notification Service Extension project?
I am mostly interested in accessing user preferences that I stored using Xamarin.Essentials
This is the template code for the Notification Service Extension:
namespace NotifServiceExtension
{
[Register("NotificationService")]
public class NotificationService : UNNotificationServiceExtension
{
Action<UNNotificationContent> ContentHandler { get; set; }
UNMutableNotificationContent BestAttemptContent { get; set; }
protected NotificationService(IntPtr handle) : base(handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public override void DidReceiveNotificationRequest(UNNotificationRequest request, Action<UNNotificationContent> contentHandler)
{
ContentHandler = contentHandler;
BestAttemptContent = (UNMutableNotificationContent)request.Content.MutableCopy();
// Modify the notification content here...
var region = Preferences.Get("region_key", "error"); // I cannot access preferences this way
BestAttemptContent.Title = string.Format("{0}[modified]", BestAttemptContent.Title);
ContentHandler(BestAttemptContent);
}
public override void TimeWillExpire()
{
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
ContentHandler(BestAttemptContent);
}
}
}
The simplest way to do this is by exposing a method on the Xamarin.Forms App class.
In the App.xaml.cs file add a new method like this:
public string GetUserRegion()
{
return Preferences.Get("region_key", "error");
}
and inside your NotificationService class you can get the region like this:
var region = (Xamarin.Forms.Application.Current as App)?.GetUserRegion();

Calling a FormDialog and starting it right away

I am playing around with the Bot Builder and LUIS from Microsoft and I am having some difficulties for quite basic stuff.
I have simple methods for responding to LUIS intents and in one of them I would like to call a FormDialog (so based on a very simple model I have) and that's it. When the intent is recognized, I correctly enter the right method and I also see that the BuildForm method of my model is called but then the bot does not go through the fields to ask the user for values.
Here is the code of my intent method (the code doesn't do much, it has no real pupose yet):
[LuisIntent("SendMessage")]
public async Task SendMessage(IDialogContext context, LuisResult result)
{
// Check if the user has already configured its message box
bool isBoxConfigured = false;
context.UserData.TryGetValue<bool>(Constants.UserData.IsBoxConfigured, out isBoxConfigured);
if (!isBoxConfigured)
{
// Configure box
context.Call(new FormDialog<MessageBox>(new MessageBox(), this._configureMessageBox, FormOptions.PromptInStart), async (c, r) =>
{
await c.PostAsync("Message Box configurée !");
});
}
else
{
// Send message
await context.PostAsync("Votre Message Box est déjà configurée. Le message a été envoyé !");
}
context.Wait(MessageReceived);
}
Here is the constructor and the _configureMessageBox property of my LUIS dialog class:
public readonly BuildFormDelegate<MessageBox> _configureMessageBox;
public LUISDialog(BuildFormDelegate<MessageBox> configureMessageBox)
{
_configureMessageBox = configureMessageBox;
}
And here is my model (the form):
[Serializable]
public class MessageBox
{
[Describe("numéro d'identification")]
[Prompt("Quel est le {&} de votre Message Box ?")]
public int Id { get; set; }
[Describe("surnom")]
[Prompt("Quel {&} voulez-vous lui donner ?")]
public string BoxName { get; set; }
public static IForm<MessageBox> BuildForm()
{
return new FormBuilder<MessageBox>()
.Message("Mmmh... Je ne connais pas votre Message Box. J'ai besoin de quelques informations.")
.Build();
}
}
When I send the message "envoie un message" to the bot, it recognizes the SendMessage intent but immediatly responds with "Message Box configurée !", which is supposed to be sent after the user goes through the form.
Does anyone have an idea about how I am supposed to do?
Thanks :)
There are two things that you will need to change in order to fix this:
Move the context.Wait(MessageReceived) call inside the else clause and inside the ResumeAfter<T> method of the form. This is required because with your current code you are awaiting after calling the form which is wrong. If you call a dialog/form you don't have to wait
Having the ResumeAfter<T> method as an anonymous function can lead into serialization issues, so I would recommend creating a method instead. Per the docs:
Ensure that all dialogs are serializable.
This can be as simple as using the [Serializable] attribute on your IDialog implementations. But beware that anonymous method closures are not serializable if they reference their outside environment to capture variables. We also support a reflection-based serialization surrogate to help serialize types not marked as serializable.
I was having the same problem although when I added the FormOptions.PromptInStart to my code the form started immediately. This is my code and form.
Call from LUIS intent:
var searchForm = new SearchForm();
var form = new FormDialog<SearchForm>(searchForm, SearchForm.BuildTestForm, FormOptions.PromptInStart);
context.Call(form, async (dialogContext, awaitable) =>
{
var r = await awaitable;
//handle result.
});
SearchForm class:
public static IForm<SearchForm> BuildTestForm()
{
return new FormBuilder<SearchForm>()
.Message("Start of form message.")
.Field(...)
...
.Build()
}
I hope this helps.
PS: My LUIS intent fills in some fields in my class and that's why I create it before I create the form.

Automatically detect when storing an object with ServiceStack.Redis

I am looking for a way to subscribe to events like Storing a specific object type to ServiceStack.Redis.
For example I may
using (var redisClient = new RedisClient())
using (var redisMyObjects = redisClient.As<MyObject>())
{
redisMyObjects.Store(myObject);//<-- I want this to trigger an event somehow
}
Is there anything like a OnStore event which I can hook too, anything out of the box? if not, is there any recommendation about how this should be done?
I don't think there is anything you can hook into (could be wrong).
Two options that came to mind:
1 - Make an extension method
2 - Publish a message to store your object and have a handler that listens for a response and does something. This is probably overkill since it's heading into the publish/subscribe realm. But, I think, worth looking into. (Basic example here and see Pub/Sub here).
Extension Method
public static class RedisClientExtensions
{
public static void StoreWithTrigger<T>(this IRedisTypedClient<T> redisClient, T value, Action<T> trigger)
{
redisClient.Store(value);
trigger(value);
}
}
Using ExtensionMethod
public void MyMethod()
{
using (var redisClient = new RedisClient())
using (var redisMyObjects = redisClient.As<MyObject>())
{
redisMyObjects.StoreWithTrigger<MyObject>(new MyObject(), TriggerEvent);//<-- I want this to trigger an event somehow
}
}
private void TriggerEvent<T>(T value)
{
//dosomething
}
Hope this gives you some ideas.

Resources