botbuilder-storage-rethinkdb v0.0.2
Bot Builder RethinkDB Storage
Attention: Bot Framework State Service will cease operating on March 31st 2018, so you should switch your bot to another storage adapter - like this one - soon!
A module to use RethinkDB as storage adapter for Microsoft Bot Builder SDK for Node.js. Configure your bot to automatically save conversation state data in Rethinkdb.
Installation
npm install --save botbuilder-storage-rethinkdb
Usage
Instantiate a new storage, configure connection details to your RethinkDB and plug it into Bot Builder.
const RethinkDbStorage = require('botbuilder-storage-rethinkdb').RethinkDbStorage;
// BotBuilder setup code
const connector = new builder.ChatConnector({
...
});
const bot = new builder.UniversalBot(connector, {
...
});
const storage = new RethinkDbStorage({
// add your settings here
host: '127.0.0.1',
tablePrefix: 'botstorage_' + process.env.NODE_ENV
});
bot.set('storage', storage);
Configuration
The constructor take an "options" argument. All options for connecting to RethinkDb are supported, and some additional ones.
host
Default: 'localhost' Well, the hostname of your RethinkDB instance ...
port
Default: 28015 The port your RethinkDB instance is listening.
db
Default: 'botstorage' The name of the database to store the conversation state data. If this database doesn't exist, it will be created.
tablePrefix
Default: 'botstorage__' The table prefix for the conversation state tables, which are created automatically. In case you are sharing one RethinkDB instance among several environments or bots, you should change this table prefix.
There are 3 tables created automatically, one for each storage container:
- botstorage_userData
- botstorage_conversationData
- botstorageprivateConversationData (botstorage is the table prefix)
All 3 of these tables include:
- a secondary index for fast lookup
- a "created_at" column
- a "updated_at" column
Data Migration
Here are some hints how I migrated existing conversation state data from the Bot Framework State Service to my local RethinkDB.
First, I enabled a special "migration" mode by setting an environment variable. In migration mode, the old storage adapter is used, but the RethinkDB storage adapter is initialized and connected:
const storage = new RethinkDbStorage({
...
});
if (process.env.MIGRATE_STORAGE) {
require('./migrate')(bot, storage);
} else {
bot.set('storage', storage);
}
...
The migrate.js file loads all existing user addresses from my Redis session store, starts an empty dialog of each of them, making the conversation state data available in the session, and finally stores the conversation state data in RethinkDB.
const async = require('async');
const Redis = require('ioredis');
module.exports = (bot, storage) => {
var migratedCount = 0;
var errorCount = 0;
const redis = new Redis({
host: ...,
port: ...,
showFriendlyErrorStack: true
});
bot.dialog('/migrate', (session) => {
console.log('MIGRATING USER ' + session.message.address.user.name);
const context = {
persistUserData: true,
userId: session.message.address.user.id,
persistConversationData: true,
conversationId: session.message.address.conversation.id
};
const data = {
userData: session.userData,
conversationData: session.conversationData,
privateConversationData: session.privateConversationData
}
storage.saveData(context, data, (err) => {
if (err) {
console.log('SAVEDATA FAILED: ' + err)
errorCount++;
} else {
migratedCount++;
}
console.log('CURRENTLY MIGRATED: ' + migratedCount + ', ERRORS: ' + errorCount);
})
})
async.waterfall([
(getKeysDone) => {
redis.keys('bot:user:*', getKeysDone);
},
(keys, getValuesDone) => {
redis.mget(keys, getValuesDone);
},
(values, done) => {
console.log('MIGRATING ' + values.length + ' bot users');
values.forEach((address) => {
bot.beginDialog(JSON.parse(address), '/migrate');
})
done()
}
], (err) => {
if (err) {
console.log('MIGRATE FAILED: ' + err);
}
});
};
License
MIT