How to save MS bot state data and conversations ? - node.js

I am using node js and Microsoft Bot builder sdk to write a BOT. Currently, I am saving everything in data bags (conversationData, userData etc) and using CosmosDb to store the state. I followed this article to configure CosmosDB & made changes as per nodejs.
https://azure.microsoft.com/en-in/blog/bot-conversation-history-with-azure-cosmos-db/
There are few concerns with this approach,
conversationData bag is cleared when we call endConversation() in dialog. Thats expected by sdk design but we would like to persist this data for multiple conversation flows with same user (same conversation id.) Now, json cosmosDb in db gets replaced with new keys on conversationData when user start new intent.
Ex: schedule a meeting with {name} for {day} at {place}.
we save conversationData.name , conversationData.day , and conversationData. place.
same user starts over schedule a meeting with {name2} for {day2} at {place2}.
documentDb entry gets replaced with conversationData.name1 , conversationData.day2 , and conversationData. place2
Ideally, we would like to keep everything.
Is there a better way to save chat history & conversationData, userData
databags in MS BOT ?.

All storage implementations just write getData and saveData, they internally have a key:value store where key is typically userId + conversationId, but you can make it whatever you want as long as you can reliably derive it from the arguments passed to getData and setData.
Looking at a sample in redis https://github.com/suttna/botbuilder-redis-storage - https://github.com/suttna/botbuilder-redis-storage/blob/master/src/storage.ts for an example storage implementation that's pretty easy to follow.
You would use a custom implementation like this
// Create new storage with redis client
var storage = new YourStorage()
// this is just here for the sake of initializing a `bot`
var connector = new builder.ChatConnector()
var bot = new builder.UniversalBot(connector)
// Configure bot to use YourStorage
bot.set('storage', storage)
bot.set('persistConversationData', true);
storage is just an object that implements
public saveData(context: IBotStorageContext, data: IBotStorageData, callback?: (err: Error) => void)
public getData(context: IBotStorageContext, callback: (err: Error, data: IBotStorageData) => void)
I totally just copied those signatures from the linked redis module, but they are the same in the BotBuilder source for the default storage - https://github.com/Microsoft/BotBuilder/blob/5cf71c742f27d89e9dbc4076f850122bd6edac11/Node/calling/src/storage/BotStorage.ts
The samples are in typescript. If you are unfamiliar, ignore the bit right after : which indicates the type of a thing.

Related

Passing OAuth token in multi threaded application

We have a SpringBoot application based on the Sap Cloud SDK (3.32.0) and are using PrincipalPropegation to our on-prem SAP environment.
Our application is also using the Axon Framework (an eventsourcing framework). This means our calls to our RestControllers are send as commands to the Aggregates, which in turn sends out events on the eventbus. Normally we pass the oauth token by adding metadata on the event messages. This is handled by the axon framework. Events are dispatched on different threads then the ones that process the commands.
However, we recently started using the cloud sdk and generated OData V2 clients to send/retrieve information to our on-prem SAP instances. The SAP cloud SDK tries to fetch the AuthToken from the ThreadContext, however, due to the async nature of the Axon framework, this does not work properly.
Is there a way pass the correct token in some other way and skip the default behaviour of the SDK? Since we have the token needed for doing the user token exchange for PrincipalPropegation in the event metadata (which can be accessed by the eventhandler).
Any suggestions would be great!
Danny
You can conveniently propagate the thread context to new threads using the ThreadContextExecutor:
ThreadContextExecutor executor = new ThreadContextExecutor();
Callable operationWithContext = () -> executor.execute(() -> operation());
invokeAsynchronously(operationWithContext);
Check out the documentation on the topic.
Is there a way pass the correct token in some other way and skip the default behaviour of the SDK?
In case the solution with ThreadContextExecutor is not working for you, we can look for a workaround: If you are looking for a way to pass an access token inside the child thread, then use the following code sample:
import com.sap.cloud.sdk.cloudplatform.security.AuthTokenAccessor;
import com.sap.cloud.sdk.cloudplatform.security.AuthToken;
DecodedJWT jwt = JWT.decode("your-access-token");
AuthToken authToken = new AuthToken(jwt);
AuthTokenAccessor.executeWithAuthToken(authToken, () -> {
// do things..
});
Please note: Besides current auth-token, the Cloud SDK may also extract principal and tenant information from the passed JWT.

Why are all activities showing Unknown for the User/Actor .NET Server API w/React-JS client components

I am working on a proof of concept with GetStream.io using the .NET server side API to add activities and the react-js client components to render feeds. For some reason every activity is coming into my feeds with Unknown in bold at the top. I assume this is supposed to be the username or something? I read a post about passing in a reference to the user instead of the string userId but the .NET API constructor signature creating a new Activity only takes in a string userId parameter. I have verified that I am passing in a valid userId. Any suggestions on what I am doing wrong here?
Stream stores the unique reference and replaces it at read time. In some complex cases, you need to be able to generate a reference to an existing object and embed that inside of an activity.
Then, can you try this way:
// Since we know their IDs we can create a reference without reading from APIs
var userRef = Users.Ref(userId);
// And then add an activity with these references
var activity = new Activity(userRef, activityAction, message)

Timer based Azure function with Table storage, HTTP request, and Azure Service Bus

I have a process written in a console application right now that fires on a scheduled task to read data from Azure table storage and based on that data, make API calls to a third party vendor we use, deserialize the response data, loop over an array in the results, save the individual iterations of the loop into a different table in Azure table storage, and then publish messages for each iteration of the loop to Azure service bus where those messages are consumed by another client.
In an effort to get more of our tasks into the cloud, I've done some research and it seems that an Azure function would be a good candidate to replace my console application. I spun up a new Azure function project in Visual Studio 2019 as a "timer" function and then dove into some reading where I got lost really fast.
The reading I've done talks about using "bindings" in my Run() method arguments decorated with attributes for connection strings etc but I'm not sure that is the direction I should be heading. It sounds like that would make it easier for authentication to my table storage, but I can't figure out how to use those "hooks" to query my table and then perform inserts. I haven't even gotten to the service bus stuff yet nor looked into making HTTP calls to our third party vendor's api.
I know this is a very broad question and I don't have any code to post because I'm having a tough time even getting out of the starting blocks with this. The MS documentation is all over the map and I can't find anything specific to my needs and I promise I've spent a fair bit of time trying.
Are Azure functions even the right path I should be travelling? If not, what other options are out there?
TIA
You should keep with Azure Functions with the Time Trigger to replace your console app.
The bindings (which can be used for input /output) are helpers to save you some lines of code, for example:
Rather than using the following code to insert data into azure table:
// Retrieve storage account information from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);
// Create a table client for interacting with the table service
CloudTableClient tableClient = storageAccount.CreateCloudTableClient(new TableClientConfiguration());
// Create a table client for interacting with the table service
CloudTable table = tableClient.GetTableReference("MyTable");
//some code to populate an entity
var entity = new { PartitionKey = "Http", RowKey = Guid.NewGuid().ToString(), Text = input.Text };
// Create the InsertOrReplace table operation
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
// Execute the operation.
TableResult result = await table.ExecuteAsync(insertOrMergeOperation);
you would use:
[FunctionName("TableOutput")]
[return: Table("MyTable")]
public static MyPoco TableOutput([HttpTrigger] dynamic input, ILogger log)
{
log.LogInformation($"C# http trigger function processed: {input.Text}");
return new MyPoco { PartitionKey = "Http", RowKey = Guid.NewGuid().ToString(), Text = input.Text };
}
PS: the input trigger in the previous code is a HTTP Trigger, but was only to explain how to use output binding.
you can find more information in here:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-triggers-bindings
https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-table
and you should watch: https://learn.microsoft.com/en-us/learn/modules/chain-azure-functions-data-using-bindings/

Syncing Azure Easy Table with WHERE clause

I'm developing a Xamarin.Forms app which uses an Azure app service with SQL database linked through EasyTables. I've run the samples and successfully tested querying tables etc on the server and enabled offline sync so as a localdb is created.
I've created the store, defined the table & sync'd it, however I want to be able to query it somehow with a where clause - is that possible? Can I add a where clause to the client.GetSyncTable line?
var store = new MobileServiceSQLiteStore("localstore.db");
store.DefineTable<Journey_Stages>();
client.SyncContext.InitializeAsync(store);
tbl_Stages = client.GetSyncTable<Journey_Stages>();
Some of the tables I'm pulling down will grow over time & are linked to individual user profiles, so I only want data which belongs to that user and I don't want to be bringing down masses of data each time, preferably let the server handle that and only bring down what I need on a user by user basis.
Thanks,
Steve
You should add this filtering logic on the server side, so that each user's data isn't exposed to all your other users. See for example this sample if you are using the Node.js backend -- line 17 adds a WHERE clause for the table read query. If you have the .Net backend, similar logic would go in your table controller.
// Configure specific code when the client does a request
// READ - only return records belonging to the authenticated user
table.read(function (context) {
context.query.where({ userId: context.user.id });
return context.execute();
});

ServiceStack OrmLite Is it a error to use UserAuthRepository.CreateUserAuth inside a transaction

I have a complex workflow where I want to create rows in several tables in one transaction.
One of the operations is to create a new UserAuth (from ServiceStack Authentication feature).
I assume that all the database operations in a transaction should operate on the same connection, and if that is true, then I think it may be a problem to call UserAuthRepository.CreateUserAuth inside a transaction because it looks as if it uses its own connection.
So my question is whether if the creation of a UserAuth will be part of the transaction or not when I have code like shown below. And if not, then how to go about creating new users as part of an transaction?
using (var db = Db.OpenDbConnection()) {
using (var trans = db.OpenTransaction()) {
... do some databae operations via. db ...
var userAuth = UserAuthRepository.CreateUserAuth(
new UserAuth{UserName = "blabla"},
"password"
);
... do some more databae operations via. db ...
trans.Commit();
}
}
Internally whenever ServiceStack requires accessing a database, e.g in the OrmLiteUserAuthRepository.CreateUserAuth it asks for and uses a new connection and immediately disposes of it once it's done.
There is currently no way to make it apart of a custom transaction.

Resources