I am designing a call manager with the help of RXSwift (ReactiveX) that continuously interacts with an API. The call manager comprises several objects that itself comprises an indicator (indicating status information loaded from the API) and control (requests to be sent to the API).
class CallManagerObjectA() {
var control = PublishSubject<String>()
var indicator = BehaviorSubject<String>(value: "string status")
}
Within the call manager, a scheduler regularly provides new values to the indicator observable:
<... API response ...>
indicator.onNext(newValue)
Somewhere else in a view controller, the indicator will be observed for a label:
indicator.subscribe(onNext: { label.stringValue = $0 })
Within the same view controller, the user can control the object status via GUI elements continuously:
control.onNext(commandValue)
Within the call manager, the control will be observed for an API call:
control.subscribe(onNext: { (command) in
// API request call
})
So far so good, this is working very well with reactive patterns.
Now, I am looking for a good solution to handle errors, if the call manager recognizes errors during the API interaction and show these errors to the user in the view controller. I was immediately thinking of something like this:
// Call manager recognizes the error
control.onError(error)
...
// Call manager ignores errors for the subscriber
control.retry().ignoreErrors().subscribe(onNext: { (command) in
// API request call
})
...
// View controller shows the errors
indicator.subscribe(onNext: { label.stringValue = $0 })
control.subscribe(onError: { print("error", $0) })
This however ends up in an infinite loop.
I fear that I have a fundamental understanding issue with reactive programming, or I miss something very important, but I am not able to understand how the handle errors in this reactive pattern environment.
Based on the code you have shown, you have a big misunderstanding, not just with how to handle Errors, but with how to program reactively in general. Try watching this video "Reactive Programming: Why It Matters"
To answer your specific question, there are two misunderstandings here:
When you call control.onError(_:) it will be the last call you will be able to make on control. Once it emits an error it will stop working.
The retry() operator asks its source to "try again on Error". If it's source is determinate, then it will just do the exact same thing it did before and emit the exact same output (i.e., the same error it emitted last time.) In the case of a PublishSubject, it doesn't know why onError was called. So the best it can do is just emit the error again.
Honestly, I consider this a bug in the API because subscribing to a publish subject that emitted an error at some point in the past should just do nothing. But then, you wouldn't be asking why you got an infinite loop. Instead you would be asking why your control stopped emitting events.
Related
I am having problems with the event handler in my office addin . Below is an example code i got from microsoft website to explain what i mean.
I have a manifest file that uses the on-send hook as well as a click-based event triggering.
My button calls appendMessageBodyOnClick and onsend i call appendMessageBodyOnSend. Both function primarily do the same thing. I never want to block sending emails regardless.
The problem is that the event object is not properly cleaned up i think.
Scenario 1
When i click my button ; which calls event.completed(), and then after i try to send the message, it says my app is blocking the message, but then when i try to send again it goes through.
Scenario 2
When i leave the subject empty and then send the message, as expected i am prompted that the subject is empty. If i cancel sending the message on this note and then click on my button, the message tries to send as though i clicked send.
I am supposing the is some sort or state clean up issue. What am i doing wrong here?
Function-File.js
function appendMessageBodyOnClick(event) {
// Append string to message body
event.completed();
}
// In the following example, the checkMessage function has
// been registered as an event handler for ItemSend.
function appendMessageBodyOnSend(event) {
// Append string to message body
event.completed({allowEvent = true});
}
Not sure if this will help, but I also have faced some seemingly inconsistent behavior while understanding how to signal that event is fully completed. Once I got my edge cases fixed, then it worked.
One suggestion: Appending string to message body should be an async function. Call the event.completed() from inside the callback function. (i.e: make sure when you are calling event.completed(), nothing else is pending -like another async result)
Something like the following:
Office.context.mailbox.item.body.setAsync("new body", function(asyncResult) {
// handle success and failure
event.completed()
});
Same would be for your scenario 2, make sure event.completed() is called at the very end.
I am building a bot for for Facebook Messenger using Microsoft Bot Framework. I am planning to use CosmosDB for State Management and also as my backend data store. (I am not stuck to CosmosBD and can use any other store if needed)
I need to send daily/weekly proactive messages(push notifications) to users based on their time preference. I will capturing their time preference when they first interact with the bot.
What is the best way to deliver these notifications?
As I will be storing these preferences in CosmosDB, I am thinking using ComosDB trigger of creating an Azure Function and schedule it based on the user time preference. This Azure function will make a call to my webhook which will deliver these messages. If requried, I will change Function schedule when a user changes his/her preference.
My questions are:
Is this a good approach?
Are there any other alternatives (Notifications Hub?)
I should be able to set specific times for notifications (like at the top of the hour or something like that), does it make sense to schedule an Azure Function to run at these hours rather than creating a function based on user preference (I can actually combine these two approaches too)
Thank you in advance.
First, I don't think there's any "right" answer to be given here; it's going to depend a lot on your domain's specific needs. Scale is going to play a major factor in the design of this. Will you have 100 users? 10000 users? 1mil users? I'm going to assume you want to design for maximum scale up front, but it could be overkill.
First, based on what you've described, I don't think a CosmosDB trigger is necessarily the solution to your problem because that's only going to fire when the preference data is created/updated. I assume that, from that point forward, your function needs to continuously fire at the time slot they've opted into, correct?
So let's pretend you let people choose from the 24hrs in the day. A naïve approach would be to simply use a scheduled trigger that fires up every hour, queries the CosmosDB for all the documents where the preference is set to that particular hour and then begins sending out notifications from there. The problem is how you scale from there and deal with issues of idempotency in the face of failures.
First off, a timer trigger only ever spins up one instance. If you were to just go query the CosmosDB documents and start processing them one by one in the scope of that single trigger, you'd hit a ceiling relatively quickly on how many notifications you can scale to. Instead what you'd want to do is use that timer trigger to fan out the notifications to as many "worker" function instances as possible. The timer trigger can act as the orchestrator in the sense that it can own the query against the CosmosDB and then turn each document result it finds for that particular notification time window into a message that it places on a queue to be processed by a separate function which will scale out on its own.
There are actually a couple ways you can accomplish this with Azure Functions, it really depends on how early an adopter of technology you are comfortable with being.
The first is what I would call the "manual" way which would be done by simply using the existing Azure Storage Queue extension by taking an IAsyncCollector<YourNotificationWorkerMessage> as a parameter to the timer function that's bound to the worker queue and then pumping out the messages through that. Then you write a second companion function which uses a QueueTrigger, bind it to that same queue, and it will take care of processing each message. This second function is where you get the scaling, enabling process all of the queued messages as quickly as possible based on whatever scaling parameters you choose to configure. This is the "simplest" approach
The second approach would be to adopt the newer Durable Functions extension. With that model, you don't have to directly think about creating a worker queue. You simply kick off a new instance of your orchestrator function from the timer function and the orchestrator fans out the work by invoking N "concurrent" calls to an action for each notification. Now, it happens to distribute those calls using queues under the covers, but that's an implementation detail that you need no longer maintain yourself. Additionally, if the work of delivering the notification requires more involved work and/or retry logic, you might actually consider using a sub-orchestration instead of a simple action. Finally, another added benefit of this approach, is that you can "fan back in" to your main orchestrator function once all the notifications are delivered to do some follow up work... even if that's simply some kind of event logging that the notification cycle has completed for this hour.
Now, the challenge with either of these approach is actually dealing with failure in initially fetching the candidates for notification from CosmosDB, paging through the results and making sure you actually fan all of them out in an idempotent manner. You need to deal with possible hiccups as you page and you need to deal with the fact that your whole function could be torn down and you might have to restart. Perhaps on the initial run of the 8AM notifications you got through page 273 out of 371 pages and then you got hit with a complete network connectivity fail or the VM your function was running on suffered a power failure. You could resume, but you'd need to know that you left off on page 273 and that you actually processed the 27th record out of that page and start from there. Otherwise, you risk sending double notifications to your users. Maybe that's something you can accept, maybe it's not. Maybe you're ok with the 27 notifications on that page being duplicated as long as the first 272 pages aren't. Again, this is something you need to decide for your domain, but if you want to avoid this issue your orchestrator function will need to track its progress to ensure that it doesn't send out dupes. Again I would say Durable Functions has a leg up here as it comes with the ability to configure retries. Maintaining the state of a particular run is left up to the author in either approach though.
I use pro-active dialog extensively with botframwork and messenger without any issue. During your facebook approval process you simply need to inform them you will be sending notifications trough messenger with your bot. Usually if you use it to inform your user and stay away from promotional content you should be fine.
I also use azure function to trigger the pro-active dialog from a custom controller endpoint.
Bellow sample code for azure function:
public static void Run(TimerInfo notificationTrigger, TraceWriter log)
{
try
{
//Serialize request object
string timerInfo = JsonConvert.SerializeObject(notificationTrigger);
//Create a request for bot service with security token
HttpRequestMessage hrm = new HttpRequestMessage()
{
Method = HttpMethod.Post,
RequestUri = new Uri(NotificationEndPointUrl),
Content = new StringContent(timerInfo, Encoding.UTF8, "application/json")
};
hrm.Headers.Add("Authorization", NotificationApiKey);
log.Info(JsonConvert.SerializeObject(hrm));
//Call service
using (var client = new HttpClient())
{
Task task = client.SendAsync(hrm).ContinueWith((taskResponse) =>
{
HttpResponseMessage result = taskResponse.Result;
var jsonString = result.Content.ReadAsStringAsync();
jsonString.Wait();
if (result.StatusCode != System.Net.HttpStatusCode.OK)
{
//Throw what ever problem as an exception with details
throw new Exception($"AzureFunction - ERRROR - HTTP {result.StatusCode}");
}
});
task.Wait();
}
}
catch (Exception ex)
{
//TODO log
}
}
Bellow sample code for starting the pro-active dialog:
public static async Task Resume<T, R>(string resumptionCookie) where T : IDialog<R>, new()
{
//Deserialize reference to conversation
ConversationReference conversationReference = JsonConvert.DeserializeObject<ConversationReference>(resumptionCookie);
//Generate message from bot to user
var message = conversationReference.GetPostToBotMessage();
var builder = new ContainerBuilder();
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
//From a cold start the service is not yet authenticated with dev bot azure services
//We thus must trust endpoint url.
if (!MicrosoftAppCredentials.IsTrustedServiceUrl(message.ServiceUrl))
{
MicrosoftAppCredentials.TrustServiceUrl(message.ServiceUrl, DateTime.MaxValue);
}
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(CancellationToken.None);
//This is our dialog stack
var task = scope.Resolve<IDialogTask>();
T dialog = scope.Resolve<T>(); //Resolve the dialog using autofac
try
{
task.Call(dialog.Void<R, IMessageActivity>(), null);
await task.PollAsync(CancellationToken.None);
}
catch (Exception ex)
{
//TODO log
}
finally
{
//flush dialog stack
await botData.FlushAsync(CancellationToken.None);
}
}
}
Your dialog needs to be registered in autofac.
Your resumptionCookie needs to be saved in your db.
You might want to check FB policy regarding proactive messages
There’s a 24h limit but it might not be totally screwed in your case
https://developers.facebook.com/docs/messenger-platform/policy/policy-overview#standard_messaging
Had a quick question: I have an Variable<[Session]>, where Session:
class Session {
...
var rx_serverRequestable: Driver<SessionRequestable>
...
}
which emits a .next event every time the session has all the information it needs to be passed on to the backend and I want to be able to flatMapLatest on the array of sessions, and do something like:
let sessions: Variable<[Session]>
sessions
.flatMapLatest { sessions in sessions.map { $0. rx_serverRequestable } }
.flatMap { $0.requestFromServer() }
but I only want to request each session once. There are two ways I see that failing with my current implementation:
1. flatMapLatest gets a new array of sessions, potentially disposing a request from server thats still in progress
2. rx_serverRequestable gets called each time the session has all the information required to be loaded from server, so it will get called multiple times, each time the session loads in any new information. I only want the session to be requested the first time, should I be using something like .multicast or .replay(1)?
any pointers on solving the two issues, or switching up my approach?
This can be solved using the zip operator http://reactivex.io/documentation/operators/zip.html
I have a ServiceWorker registered on my page and want to pass some data to it so it can be stored in an IndexedDB and used later for network requests (it's an access token).
Is the correct thing just to use network requests and catch them on the SW side using fetch, or is there something more clever?
Note for future readers wondering similar things to me:
Setting properties on the SW registration object, e.g. setting self.registration.foo to a function within the service worker and doing the following in the page:
navigator.serviceWorker.getRegistration().then(function(reg) { reg.foo; })
Results in TypeError: reg.foo is not a function. I presume this is something to do with the lifecycle of a ServiceWorker meaning you can't modify it and expect those modification to be accessible in the future, so any interface with a SW likely has to be postMessage style, so perhaps just using fetch is the best way to go...?
So it turns out that you can't actually call a method within a SW from your app (due to lifecycle issues), so you have to use a postMessage API to pass serialized JSON messages around (so no passing callbacks etc).
You can send a message to the controlling SW with the following app code:
navigator.serviceWorker.controller.postMessage({'hello': 'world'})
Combined with the following in the SW code:
self.addEventListener('message', function (evt) {
console.log('postMessage received', evt.data);
})
Which results in the following in my SW's console:
postMessage received Object {hello: "world"}
So by passing in a message (JS object) which indicates the function and arguments I want to call my event listener can receive it and call the right function in the SW. To return a result to the app code you will need to also pass a port of a MessageChannel in to the SW and then respond via postMessage, for example in the app you'd create and send over a MessageChannel with the data:
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
console.log(event.data);
};
// This sends the message data as well as transferring messageChannel.port2 to the service worker.
// The service worker can then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
// See https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
and then you can respond via it in your Service Worker within the message handler:
evt.ports[0].postMessage({'hello': 'world'});
To pass data to your service worker, the above mentioned is a good way. But in case, if someone is still having a hard time implementing that, there is an other hack around for that,
1 - append your data to get parameter while you load service-worker (for eg., from sw.js -> sw.js?a=x&b=y&c=z)
2- Now in service worker, fetch those data using self.self.location.search.
Note, this will be beneficial only if the data you pass do not change for a particular client very often, other wise it will keep changing the loading url of service worker for that particular client and every time the client reloads or revisits, new service worker is installed.
I'm using the MVVM Light Toolkit. I could not find any Ctor of Messenger or Notification class to send a empty message.
ViewModel1:
private int _selectedWeeklyRotation;
public int SelectedWeeklyRotation
{
get { return _selectedWeeklyRotation; }
set
{
if(_selectedWeeklyRotation == value)
return;
_selectedWeeklyRotation = value;
this.OnPropertyChanged("SelectedWeeklyRotation");
if(value > 1)
Messenger.Default.Send();
}
}
ViewModel2:
Ctor:
Messenger.Default.Register(this, CreateAnotherTimeTable);
private void CreateAnotherTimeTable()
{
}
I just need to send a Notification to another ViewModel, no sending of data at all.
Is that possible with MVVM Light Toolkit library?
Unless I'm misunderstanding something, couldn't you accomplish this by creating and sending a custom "signal message" type via the Messenger?
public class WeeklyRotationSignal {}
Messenger.Default.Send(new WeeklyRotationSignal());
Then register to that in another view model:
Messenger.Default.Register<WeeklyRotationSignal>(this, msg => doWork);
You can try sending a simple message with a string tag and receive that message by matching the string tag. Something like this:
Sender portion of the code located possibly in something like ViewModel1.cs
Messenger.Default.Send<string>("Dummy text message", "String_ToHelpMatchTheMsg");
Receiving end portion of the code responding to that message above, possibly located in some other file, something like ViewModel2.cs
...
Messenger.Default.Register<string>(this, "String_ToHelpMatchTheMsg", executeThisFunction);
private void executeThisFunction(string strMsg)
{
//your code would go here to run upon receiving the message
// The following line will display: "Dummy text message"
System.Windows.Browser.HtmlPage.Window.Alert("msg passed: " + strMsg);
}
Please note that you dont have to do anything with the text message that is passed around with the messaging code above. Just one part of the code sending some ping to another part of the code to ask some other section to execute some code. The important string is the one where I used "String_ToHelpMatchTheMsg" because that is the key used to match the sender and the receiver. Almost like creating your own quasi-event, once the Send method runs, the Register method is notified and fire its own function to run also.
I used this with a Close button on a Child Window to close it. The Close button on the View of the Child Window binds to a relay command on its childWindowViewModel. That relay command has the code above to send a message to the ParentViewModel. The Register portion on the ParentViewModel responds to that message by firing a method that closes the ChildWindow which was initially instantied from that parentViewModel.
Once you get more familiar with messaging, there are more attributes that you will be able to use so that the receiver can call back the sender to give a status or some data back. Look for Delegates and lambda function to achieve this.
All this to avoid placing code in the code behind to close the child window! :-)
Use as you see fit.
Cheers.
Mario
There really isn't a way to accomplish this and in someways defies the point of the messenger class. I didn't want to write a your doing it wrong post, but I feel I am stuck. The way the messenger class works is that you have two parties that both subscribe to the same concept, its an observer model. Without that similar concept or message there really isn't a way to tie the two objects together. The generic message whether a simple string or custom message act as the meeting point of the Subscribing and Publishing classes.
If the ViewModel publishing knows the type of ViewModel its trying to Send to it could...
Messenger.Default.Send<Type>(typeof(ViewModelToSendTo);
This would act as a very simple interaction point, you also wouldn't have to create a custom class. Some purist may have an issue with this approach as it couples the publishing class to the subscriber.
I don't think that it is possible and frankly I don't see the point of having that kind of message. You could just as well send a string "SelectedWeeklyRotation". It seems strange to have an empty message that has some kind of meaning as you increase the number of broadcast messages - and receivers in your application.
In the version of MVVM Light that I'm using it is not even possible to send an empty message.
However I did see a method in the ViewModelBase that is :
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
This might be of interest for you.