How to store session data in custom storage in bot builder

I am using node js to create a chat.

I currently store session data in Microsoft's default store, which is limited to 64KB per user session. I want to use my own storage to store session data. Here, I got help from a Microsoft developer.

I can store in document DB and Azure table.

However, I am confused. How do we implement an interface IStorageClient

for storing in our own database?

Whenever I install session.UserData.name=""

it has to be stored in its own DB.

+4


source to share


4 answers


I wrote some code to store bot data in Mongo Db. Full code and details can be found in BotBuilder-MongoDB

IstorageClient interface implementation code:



"use strict";
var Consts = require('./Consts');
var mongodb_1 = require("mongodb");
var replaceDot_Atrate = require("./replaceDot");
var mongoDbConnection = require('./connection.js');
var conf = require('../config');
var conversational_collname = conf.db.conversationalCollection;

var IStorageClient = (function () {

    function IStorageClient(options) {
        this.options = options;
    }

    IStorageClient.prototype.retrieve = function (partitionKey, rowKey, callback) {
        var id = partitionKey + ',' + rowKey;
        if(rowKey!=="userData"){
            var query={"$and":[{"userid":id}]}
                mongoDbConnection(function(err,db) {
                var iterator= db.collection(conversational_collname).find(query);
                iterator.toArray(function (error, result, responseHeaders) {
                    if (error) {
                        console.log("Error",error)
                        callback(error, null, null);
                    }
                    else if (result.length == 0) {
                        callback(null, null, null);
                    }
                    else {
                        var document_1 = result[0];
                        var finaldoc=replaceDot_Atrate.substituteKeyDeep(document_1, /\@/g, '.');
                        finaldoc["id"]=id
                        callback(null, finaldoc, null);
                    }
                });
            }); 
        }
        else{
            var query={"$and":[{"userid":partitionKey}]}
            mongoDbConnection(function(err,db) { 

                var iterator= db.collection(conversational_collname).find(query);
                iterator.toArray(function (error, result, responseHeaders) {
                    if (error) {
                        callback(error, null, null);
                    }
                    else if (result.length == 0) {
                        //console.log("result length 0")
                        callback(null, null, null);
                    }
                    else {
                        var document_1 = result[0];
                        callback(null, document_1, null);
                    }
                });
            });
        }
    };

    IStorageClient.prototype.initialize = function (callback) {
        var _this = this;
        var client=mongodb_1.MongoClient;
        this.client = client;

        mongoDbConnection(function(err,database) {    
                _this.database = database;
                _this.collection = database.collection(conversational_collname);
                callback(null);
         });
    };

    IStorageClient.prototype.insertOrReplace = function (partitionKey, rowKey, entity, isCompressed, callback) {
        var id=partitionKey + ',' + rowKey
        var docDbEntity = { id: partitionKey + ',' + rowKey, data: entity, isCompressed: isCompressed };
        if(rowKey!=="userData"){
            var newEntitiy=replaceDot_Atrate.substituteKeyDeep(entity, /\./g, '@');
            var conditions1 = {
                'userid': id
            };
            var updateobj1 = {
                "$set": {"data":newEntitiy,"isCompressed":false}
            };   
            mongoDbConnection(function(error,db) {    
                db.collection(conversational_collname).update(conditions1,updateobj1,{upsert: true},function(err,res){
                callback(error, null,"");
            });
            });
        }
        else{
            var conditions = {
                'userid': partitionKey
            };
            var update = {
                "$set": {"data":entity}
            }
            mongoDbConnection(function(error,db) {    
                db.collection(conversational_collname).update(conditions,update,{upsert: true},function(err,res){
                callback(error, null,"");
           })
        });
        } 
    };


    IStorageClient.getError = function (error) {
        if (!error)
            return null;
        return new Error('Error Code: ' + error.code + ' Error Body: ' + error.body);
    };

    return IStorageClient;
}());
exports.IStorageClient = IStorageClient;

      

+2


source


I'm not sure what my problem is; I understand what you are trying to do and it seems like you have all the parts in place.

You must first implement the IStorageClient interface . In your implementation, you write logic to store things in your DB.

You can browse DocumentDB and Azure Tables to get an idea of ​​how this can be done.



Now the question is, if your custom storage is in Azure ... you can use AzureBotStorage with your custom storage client. You instantiate your client client, pass the link to AzureBotStorage

and set it as repository in the bot

// Azure DocumentDb State Store
var docDbClient = new azure.DocumentDbClient({
    host: process.env.DOCUMENT_DB_HOST,
    masterKey: process.env.DOCUMENT_DB_MASTER_KEY,
    database: process.env.DOCUMENT_DB_DATABASE,
    collection: process.env.DOCUMENT_DB_COLLECTION
});
var botStorage = new azure.AzureBotStorage({ gzipData: false }, docDbClient);

// Set Custom Store
bot.set('storage', botStorage);

      

If your custom storage is located anywhere other than Azure, then it AzureBotStorage

may not work for you. Please note that I am not sure about this, you will need to check the code for confirmation. From what I've seen it looks pretty versatile and so you can reuse it and just implement it IStorageClient

. If it is not, you will also need to implement the interface

+3


source


Here's another example of this library: mongo-bot-storage

import {MongoDbBotStorage, MongoDBStorageClient} from "mongo-bot-storage";

// use it like this 
bot.set("storage", new MongoDbBotStorage(new MongoDBStorageClient({
    url: "mongodb://localhost/mydb",
    mongoOptions: {}
})));

// or like this
MongoClient.connect("mongodb://localhost/mydb", (error, db) => {
    bot.set("storage", new MongoDbBotStorage(new MongoDBStorageClient({db})));
});

      

and then use the default session.userData

, etc.

Do not forget

// Enable Conversation Data persistence
bot.set('persistConversationData', true);

      

+1


source


I am posting this answer on a very, very old thread just because it seems to give better results for implementing custom storage options for the Bot Framework.

There is a much simpler solution that does not involve using npms that have not been updated for some time, which runs the risk of not being validated in corporate environments or can lead to a huge amount of log entries.

The easiest way - to create a custom data warehouse code for your bot using the most modern driver for the selected database by following the steps described in this undocumented instructions for the user data store to Microsoft Bot Framework.

It took me about an hour to implement MongoDB, including some learning curve for their latest driver.

In short, in case a link dies at some point, you can create middleware that exports two main features that the Bot Framework is looking for: getData

and saveData

. Each of them should have the following rough structure (by reference, using Redis):

var storageKey = 'conversationId';
module.exports = {
getData : (data, callback) => {
    redisClient.get(data[storageKey], (err, reply) => {
        if (err || !reply) {
            callback(err, null);
        } else {
            callback(null, JSON.parse(reply));
        }
    })
},
saveData : (data, value, callback) => {
        redisClient.set(data[storageKey], JSON.stringify(value), (err, reply) => {
            if (err) {
                callback(err);
            } else if (reply === 'OK') {
                callback(null);
            }
        })
    }
}

      

Then in your app.js

necessarily app.js

require()

your middleware and install it accordingly:

const dbMiddleware = require('./path/to/middleware')

bot.set('storage', dbMiddleware)

      

0


source







All Articles