├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build.yaml │ ├── bump-master-version.yaml │ └── publish-npm.yaml ├── .gitignore ├── .vscode └── launch.json ├── CODE_OF_CONDUCT.md ├── Contributing.md ├── LICENSE ├── README.md ├── example ├── README.md ├── app.js ├── bot.js ├── mongodb_sample.bot ├── package-lock.json ├── package.json └── readme_assets │ ├── mongo_data.png │ └── sample_conversation.png ├── package-lock.json ├── package.json ├── pipelines └── azure-pipelines.yml ├── src ├── MongoDbStorage.ts ├── MongoDbStorageError.ts ├── MongoDocumentStoreItem.ts └── index.ts ├── test ├── integration │ ├── async-dump.js │ ├── mocha.opts │ └── mongoDbStorage.integration.test.js └── unit │ ├── mocha.opts │ └── mongoDbStorage.unit.test.js └── tsconfig.json /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ## This issue is : (mark with an `x`) 8 | 9 | ``` 10 | - [ ] bug report -> please search issues before submitting 11 | - [ ] feature request 12 | - [ ] documentation issue or request 13 | - [ ] regression (a behavior that used to work and stopped in a new release) 14 | ``` 15 | 16 | ### Minimal steps to reproduce 17 | > 18 | 19 | ### Any log messages given by the failure 20 | > 21 | 22 | ### Expected/desired behavior 23 | > 24 | 25 | ### OS and Version? 26 | > Windows 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 27 | 28 | ### Version(s) 29 | > 30 | 31 | ### Mention any other details that might be useful 32 | 33 | > --------------------------------------------------------------- 34 | > Thanks! We'll be in touch soon. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | 4 | * ... 5 | 6 | ## Does this introduce a breaking change? 7 | 8 | 9 | 10 | ``` 11 | [ ] Yes 12 | [ ] No 13 | ``` 14 | 15 | ## Pull Request Type 16 | 17 | What kind of change does this Pull Request introduce? 18 | 19 | 20 | ``` 21 | [ ] Bugfix 22 | [ ] Feature 23 | [ ] Code style update (formatting, local variables) 24 | [ ] Refactoring (no functional changes, no api changes) 25 | [ ] Documentation content changes 26 | [ ] Other - Please describe: 27 | _______________ 28 | ``` 29 | 30 | ## How to Test 31 | 32 | * Get the code 33 | 34 | ``` 35 | git clone [repo-address] 36 | cd [repo-name] 37 | git checkout [branch-name] 38 | npm install 39 | ``` 40 | 41 | * Test the code 42 | 43 | 44 | 45 | ``` 46 | ``` 47 | 48 | ## What to Check 49 | 50 | Verify that the following are valid 51 | 52 | * ... 53 | 54 | ## Other Information 55 | 56 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | build_and_test: 10 | runs-on: ubuntu-latest 11 | defaults: 12 | run: 13 | shell: bash 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Cache node modules 18 | uses: actions/cache@v2 19 | env: 20 | cache-name: cache-node-modules 21 | with: 22 | # npm cache files are stored in `~/.npm` on Linux/macOS 23 | path: ~/.npm 24 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 25 | restore-keys: | 26 | ${{ runner.os }}-build-${{ env.cache-name }}- 27 | ${{ runner.os }}-build- 28 | ${{ runner.os }}- 29 | 30 | - name: Install Dependencies 31 | run: npm install 32 | 33 | - name: Build 34 | run: npm run build 35 | 36 | - name: Unit Test 37 | run: npm run test:unit 38 | 39 | - name: Integration tests 40 | run: npm run test:integration 41 | 42 | services: 43 | # Label used to access the service container 44 | mongodb-service: 45 | # Docker Hub image 46 | image: mongo:latest 47 | # Docker port mapping 48 | ports: 49 | - 27017:27017 50 | -------------------------------------------------------------------------------- /.github/workflows/bump-master-version.yaml: -------------------------------------------------------------------------------- 1 | name: Bump master version 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | # Refrain from triggering on file change to package.json to prevent recursive trigger loop 9 | - 'package.json' 10 | # Refrain from triggering on workflows themselves - not funcitonal code 11 | - '.github/workflows/**' 12 | 13 | jobs: 14 | bump-and-tag: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/setup-node@v1 18 | with: 19 | node-version: '12.x' 20 | - uses: actions/checkout@master 21 | with: 22 | fetch-depth: '0' 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - name: Setup Git 26 | run: | 27 | git config user.email ${{ secrets.GIT_USER_EMAIL }} 28 | git config user.name ${{ secrets.GIT_USER_NAME }} 29 | 30 | - name: Bump version in package.json 31 | run: npm version patch -m 'Bump version and set tag' 32 | 33 | - name: Push code 34 | run: git push 35 | 36 | - name: Push tags 37 | run: git push --tags 38 | -------------------------------------------------------------------------------- /.github/workflows/publish-npm.yaml: -------------------------------------------------------------------------------- 1 | name: Publish Node.js Package 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | prepare-package: 10 | runs-on: ubuntu-latest 11 | defaults: 12 | run: 13 | shell: bash 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | ref: ${{ github.ref }} 18 | 19 | - uses: actions/setup-node@v1 20 | with: 21 | node-version: '12.x' 22 | registry-url: 'https://registry.npmjs.org' 23 | 24 | - name: Install Dependencies 25 | run: npm install 26 | 27 | # TODO: version bump, check-in? 28 | 29 | - name: build 30 | run: npm run build 31 | 32 | - name: Copy lib to package 33 | run: cp -r ./lib/ ./package/ 34 | 35 | - name: Copy core files to package 36 | run: cp ./{CODE_OF_CONDUCT.md,Contributing.md,LICENSE,README.md,package.json} ./package/ 37 | 38 | - name: Publish package to NPM 39 | run: npm publish package 40 | env: 41 | NODE_AUTH_TOKEN: ${{ secrets.NPMJS_ACCESS_TOKEN }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .nyc_output 4 | lib 5 | coverage 6 | *.tgz -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [{ 7 | "type": "node", 8 | "request": "launch", 9 | "name": "Launch Program", 10 | "program": "${workspaceFolder}/app.js" 11 | }, 12 | { 13 | "type": "node", 14 | "request": "launch", 15 | "name": "Mocha Current File", 16 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 17 | "args": [ 18 | "--timeout", 19 | "999999", 20 | "--colors", 21 | "${file}" 22 | ], 23 | "console": "integratedTerminal", 24 | "internalConsoleOptions": "neverOpen" 25 | } 26 | 27 | ] 28 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contribution 2 | 3 | ## Code of Conduct 4 | 5 | Contributors must follow [Code of Conduct](CODE_OF_CONDUCT.md). 6 | 7 | ## Contributing to this project 8 | 9 | Contributing to this project can be done through pull requests or raising issues. If you are contributing code, please follow the steps below to ensure that: 10 | 11 | 1. Target packages and dependent components are compatible with the versions supported by this project. 12 | 1. All unit tests and integration tests pass. 13 | 1. Contribution is aligned with the function and spirit of the current project. 14 | 15 | 16 | ## Local Development Setup 17 | 18 | ### Project setup 19 | 20 | Pull the code from the repo: 21 | 22 | ```bash 23 | git clone https://github.com/botbuilder-contrib/botbuilder-storage-mongodb.git 24 | ``` 25 | 26 | Install packages 27 | 28 | ```bash 29 | npm install 30 | ``` 31 | 32 | > Note: You do not need TypeScript installed globally. The project installs the TypeScript compiler locally and uses it in the build process. 33 | 34 | ### Unit Test 35 | 36 | Run all unit tests 37 | 38 | ```bash 39 | npm run test:unit 40 | ``` 41 | 42 | Unit tests are written in pure JavaScript. The npm `test:unit` script executes `tsc` then invokes [`nyc`](https://github.com/istanbuljs/nyc) 43 | 44 | ### Code Coverage 45 | 46 | ```bash 47 | npm run cover 48 | ``` 49 | 50 | There is no need to execute 'npm run test:unit' before code coverage. The cover command performs the following: 51 | 52 | * Builds via the TypeScript compiler (`tsc`). 53 | * Runs run unit tests. 54 | * Creates an html report in the cover folder. 55 | * Opens a browser window with html report. 56 | 57 | ### Integration Testing 58 | 59 | Run a local MongoDB instance in order to perform integration tests. 60 | There are many ways to run MongoDB locally, and you may already be running Mongo on your computer. 61 | 62 | > Note that the integration tests are hard-coded to run with the following settings: 63 | > ```javascript 64 | > const settings = { 65 | > url: "mongodb://localhost:27017/", 66 | > database: "botFrameworkStorage_TestDB", 67 | > collection: "botFrameworkStorage_TestCollection" 68 | > }; 69 | > ``` 70 | > Please ensure that the database and collection names do not overwrite your data! This is up to you!!! 71 | 72 | If using docker, you can run a local instance by issuing the following command 73 | 74 | ```bash 75 | docker run --name mongo-local-v40 -d -p 27017:27017 mongo:4.0 76 | ``` 77 | 78 | The command above will name your container instance **mongo-local-v40** and use the `4.0` MongoDB version. 79 | 80 | If your computer is already running a MongoDB server instance, you would need to stop it or run it on different TCP port. 81 | 82 | > Please don't modify the `settings` object in the integration tests. Our automated builds will fail and cause your PR to be rejected. 83 | 84 | Once you have MongoDB running, ensure all integration tests pass: 85 | 86 | ```bash 87 | npm run test:integration 88 | ``` 89 | 90 | ## Pull Request Preparation 91 | 92 | [TBD] 93 | 94 | Use the [TBD] template and ensure the PR complies with all requested information. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 BotBuilder Contrib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # State Storage for Bot Framework using MongoDB 2 | 3 | This project provides a MongoDb storage mechanism for [Bot Framework-JS SDK V4.](https://github.com/Microsoft/botbuilder-js). 4 | 5 | It allows you to store bot state in MongoDB, so that you can scale out your bot, and be more resilient to bot server failures. 6 | 7 | 8 | ![Build and test](https://github.com/botbuilder-contrib/botbuilder-storage-mongodb/workflows/Build%20and%20test/badge.svg) 9 | 10 | ## Requirements 11 | 12 | * [NodeJS](https://nodejs.org/en/) 13.x was used to create and test this library. 13 | * MongoDB database, or a database exposing basic MongoDB syntax. 14 | 15 | ## Installation 16 | 17 | ```bash 18 | npm install botbuilder-storage-mongodb 19 | ``` 20 | 21 | ## Sample Usage 22 | 23 | ```JavaScript 24 | (async () => { 25 | const mongoClient = new MongoClient("mongodb://localhost:27017/", { useUnifiedTopology: true }); 26 | await mongoClient.connect(); 27 | const collection = MongoDbStorage.getCollection(mongoClient); 28 | const mongoStorage = new MongoDbStorage(collection); 29 | 30 | // now we have a storage component instantiated: 31 | const conversationState = new ConversationState(mongoStorage); 32 | 33 | //... rest of your code 34 | })(); 35 | 36 | ``` 37 | 38 | See [example code](example/app.js) for more details. 39 | 40 | ## Specifying Mongo Collection 41 | 42 | The convenience method `MongoDbStorage.getCollection(mongoClient)` returns a MongoDB Collection object. It leverages default values `MongoDbStorage.DEFAULT_DB_NAME` and `MongoDbStorage.DEFAULT_COLLECTION_NAME` for the database and collection names respectively, resulting in the default collection `BotFreamework.BotFrameworkState`. 43 | 44 | You may alternatively call the method and supply `dbName` and `collectionName` to suit your needs: 45 | 46 | ```javascript 47 | MongoDbStorage.getCollection(mongoClient, 'MyDbName','MyCollectionName') 48 | // collection "MyCollectionName" in the database "MyDbName" 49 | ``` 50 | 51 | > If using the method `MongoDbStorage.getCollection(mongoClient)` to obtain a collection, the `mongoClient` object must already be instantiated and connected! 52 | 53 | 54 | You can also skip using the convenience method and supply a [`Collection`](https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html) instance to the constructor by obtaining a collection reference from your application configured to your liking. One reason you may want or need to do so is if you want to provide collection specific options not exposed by the convenience method. 55 | 56 | 57 | > ⚠ Caution: you **should not store Mongo URL in code!** Get the `url` from a configuration such as environment variable or a secrets store in your environment. It may contain sensitive password in the clear and should __never be stored in code__! 58 | 59 | See [MongoDB Connection URI format](https://docs.mongodb.com/manual/reference/connection-string/) in the official documentation to learn more about the connection `url` parameter value. 60 | 61 | 62 | ## Stale State 63 | 64 | Persisted state is stored indefinitely by Mongo, as the BotFramework on its own does not clean up persisted state. 65 | 66 | You can leverage MongoDB's [TTL index](https://docs.mongodb.com/manual/core/index-ttl/) to automatically delete state records a certain time after their creation. The field `dt` on the state items is updated each time a state is saved. It is therefore a good candidate for _expire-after-x_ type cleanup. 67 | 68 | Using the MongoDB Shell, the commands below creates a TTL index on the `dt` field, causing MongoDB compliant engines to automatically delete documents older than 30 days: 69 | 70 | ```javascript 71 | 72 | use BotFramework 73 | db.BotFrameworkState.createIndex({dt: 1}, {expireAfterSeconds: (60 * 60 * 24 * 30)}); 74 | 75 | ``` 76 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | ## Example bot using MongoDb Storage for Bot Framework. 2 | 3 | ### Getting Started 4 | You need to acces to a MongoDb server and the url/credentials. MongoDb can also be ran locally. 5 | 6 | Alternatively, you can use Docker. See the section below for instructions on how to run this example via docker (does not require MongoDb locally and will run a mongo instance in a container.) 7 | 8 | ### Running localy (requires a MongoDb server to be running) 9 | * #### Build 10 | Please ensure that the root project is built by running the following commands at the root of this repo. 11 | 12 | Install Project Dependencies 13 | ``` npm install``` 14 | 15 | Compile and build the TypeScript code 16 | ``` npm run build``` 17 | 18 | Change directory to the example folder 19 | ``` cd example``` 20 | 21 | Install Sample bot dependencies 22 | ``` npm install``` 23 | 24 | Start the bot 25 | ``` node app``` 26 | 27 | ### Running via Docker (will run a MongoDb container) 28 | (coming soon!) 29 | 30 | ### Interacting with the Bot via the Emulator 31 | * Install the [Bot Framework Emulator](https://github.com/Microsoft/BotFramework-Emulator) 32 | * Once installed, run the program and navigate to 33 | ``` File --> Open Bot Configuration``` 34 | Select the bot file in this project. 35 | * Enter any text and notice the counter increments on the repsonse. 36 | * ![](readme_assets/sample_conversation.png) 37 | 38 | 39 | 40 | * Start a mongo shell session and execute the following commands. 41 | 42 | * ``` use botframework``` 43 | 44 | * ``` show collections ``` 45 | 46 | * ``` db.botframeworkstate.find() ``` 47 | 48 | * You should see two entries one for conversation state another for user state. 49 | 50 | * ![](readme_assets/mongo_data.png) 51 | 52 | #### Documentation 53 | * [BotBuilder State](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-state?view=azure-bot-service-4.0) 54 | 55 | * [Using the Emulator](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-debug-emulator?view=azure-bot-service-4.0) 56 | 57 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { BotFrameworkAdapter } = require('botbuilder'); 4 | const { MongoDbStorage } = require('../lib/MongoDbStorage'); 5 | const BotGreeting = require('botbuilder-greeting'); 6 | const { MongoClient } = require('mongodb'); 7 | const { Bot } = require('./bot'); 8 | 9 | let server = require('restify').createServer(); 10 | 11 | const adapter = new BotFrameworkAdapter(); 12 | adapter.use(new BotGreeting(context => { 13 | return `Hi I'm your friendly bot. What's your name?`; 14 | })); 15 | 16 | server.listen(process.env.port || process.env.PORT || 3978, function () { 17 | console.log(`${server.name} listening to ${server.url}`); 18 | }); 19 | 20 | 21 | (async () => { 22 | 23 | try { 24 | // Create a MongoClient and connect it. 25 | const mongoClient = new MongoClient("mongodb://localhost:27017/?connectTimeoutMS=4000", { useUnifiedTopology: true }); 26 | await mongoClient.connect(); 27 | 28 | // Grab a collection handle off the connected client 29 | const collection = MongoDbStorage.getCollection(mongoClient); 30 | 31 | // Create a MongoDbStorage, supplying the collection to it. 32 | const mongoStorage = new MongoDbStorage(collection); 33 | 34 | 35 | /*---------------------------------- 36 | At this point the storage is all set up. 37 | Everything below here is demo of BotFramework. 38 | */ 39 | 40 | const bot = new Bot(mongoStorage); 41 | 42 | 43 | server.post('/api/messages', async (req, res, next) => { 44 | adapter.processActivity(req, res, bot.processBotActivity.bind(bot)).catch(e => { 45 | console.error(e); 46 | next(e) 47 | }); 48 | }); 49 | 50 | 51 | } catch (ex) { 52 | console.error(ex); 53 | } 54 | })(); 55 | -------------------------------------------------------------------------------- /example/bot.js: -------------------------------------------------------------------------------- 1 | const { ActivityTypes, ConversationState, UserState, TurnContext } = require('botbuilder'); 2 | 3 | class Bot { 4 | constructor(storage) { 5 | // Use the mongoStorage instance for ConversationState and UserState 6 | this.conversationState = new ConversationState(storage); 7 | this.userState = new UserState(storage); 8 | 9 | // Conversation State Property 10 | this.conversationProperty = this.conversationState.createProperty("Convo"); 11 | 12 | // User State properties 13 | this.countProperty = this.userState.createProperty("CountProperty"); 14 | this.nameProperty = this.userState.createProperty("NameProperty"); 15 | } 16 | 17 | /** 18 | * Process BotFramework's TurnContext 19 | * @param {TurnContext} context 20 | */ 21 | async processBotActivity(context) { 22 | try { 23 | if (context.activity.type == ActivityTypes.Message) { 24 | 25 | //Get all storage items 26 | let conversation = await this.conversationProperty.get(context, { MessageCount: 0, UserReplies: [] }); 27 | let count = await this.countProperty.get(context, 0); 28 | let name = await this.nameProperty.get(context, context.activity.text); 29 | 30 | // Change the data in some way 31 | count++; 32 | conversation.MessageCount = count; 33 | conversation.UserReplies.push(context.activity.text); 34 | 35 | // Respond back 36 | await context.sendActivity(`${count} - Hi ${name}! You said ${context.activity.text}`); 37 | 38 | // Set User State Properties 39 | await this.nameProperty.set(context, name); 40 | await this.countProperty.set(context, count); 41 | 42 | // Save UserState changes to MondogD 43 | await this.userState.saveChanges(context); 44 | 45 | //Set Conversation State property 46 | await this.conversationProperty.set(context, conversation); 47 | 48 | //Save Conversation State to MongoDb 49 | await this.conversationState.saveChanges(context); 50 | 51 | } 52 | // If activity type is DeleteUserData, invoke clean out userState 53 | else if (context.activity.type === ActivityTypes.DeleteUserData) { 54 | await this.userState.delete(context); 55 | await this.userState.saveChanges(context); 56 | } 57 | } catch (ex) { 58 | console.error(ex); 59 | } 60 | } 61 | } 62 | 63 | module.exports = { Bot }; 64 | -------------------------------------------------------------------------------- /example/mongodb_sample.bot: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mongodb_sample_bot", 3 | "description": "A sample bot using MongoDb as state storage.", 4 | "services": [ 5 | { 6 | "type": "endpoint", 7 | "appId": "", 8 | "appPassword": "", 9 | "endpoint": "http://localhost:3978/api/messages", 10 | "id": "09c02ab0-b7e6-11e8-8c7c-7be38873cc64", 11 | "name": "http://localhost:3978/api/messages" 12 | } 13 | ], 14 | "secretKey": "", 15 | "version": "2.0", 16 | "overrides": null 17 | } 18 | -------------------------------------------------------------------------------- /example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-mongo-storage-sample", 3 | "requires": true, 4 | "lockfileVersion": 1, 5 | "dependencies": { 6 | "@azure/ms-rest-js": { 7 | "version": "1.2.6", 8 | "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.2.6.tgz", 9 | "integrity": "sha512-8cmDpxsQjVdveJwYKtNnkJorxEORLYJu9UHaUvLZA6yHExzDeISHAcSVWE0J05+VkJtqheVHF17M+2ro18Cdnw==", 10 | "requires": { 11 | "axios": "^0.18.0", 12 | "form-data": "^2.3.2", 13 | "tough-cookie": "^2.4.3", 14 | "tslib": "^1.9.2", 15 | "uuid": "^3.2.1", 16 | "xml2js": "^0.4.19" 17 | }, 18 | "dependencies": { 19 | "axios": { 20 | "version": "0.18.1", 21 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", 22 | "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", 23 | "requires": { 24 | "follow-redirects": "1.5.10", 25 | "is-buffer": "^2.0.2" 26 | } 27 | } 28 | } 29 | }, 30 | "@netflix/nerror": { 31 | "version": "1.1.3", 32 | "resolved": "https://registry.npmjs.org/@netflix/nerror/-/nerror-1.1.3.tgz", 33 | "integrity": "sha512-b+MGNyP9/LXkapreJzNUzcvuzZslj/RGgdVVJ16P2wSlYatfLycPObImqVJSmNAdyeShvNeM/pl3sVZsObFueg==", 34 | "requires": { 35 | "assert-plus": "^1.0.0", 36 | "extsprintf": "^1.4.0", 37 | "lodash": "^4.17.15" 38 | }, 39 | "dependencies": { 40 | "extsprintf": { 41 | "version": "1.4.0", 42 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", 43 | "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=" 44 | } 45 | } 46 | }, 47 | "@types/bson": { 48 | "version": "4.0.2", 49 | "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz", 50 | "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==", 51 | "dev": true, 52 | "requires": { 53 | "@types/node": "*" 54 | } 55 | }, 56 | "@types/bunyan": { 57 | "version": "1.8.6", 58 | "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.6.tgz", 59 | "integrity": "sha512-YiozPOOsS6bIuz31ilYqR5SlLif4TBWsousN2aCWLi5233nZSX19tFbcQUPdR7xJ8ypPyxkCGNxg0CIV5n9qxQ==", 60 | "dev": true, 61 | "requires": { 62 | "@types/node": "*" 63 | } 64 | }, 65 | "@types/events": { 66 | "version": "3.0.0", 67 | "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", 68 | "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", 69 | "dev": true 70 | }, 71 | "@types/formidable": { 72 | "version": "1.0.31", 73 | "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-1.0.31.tgz", 74 | "integrity": "sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==", 75 | "dev": true, 76 | "requires": { 77 | "@types/events": "*", 78 | "@types/node": "*" 79 | } 80 | }, 81 | "@types/jsonwebtoken": { 82 | "version": "7.2.8", 83 | "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz", 84 | "integrity": "sha512-XENN3YzEB8D6TiUww0O8SRznzy1v+77lH7UmuN54xq/IHIsyWjWOzZuFFTtoiRuaE782uAoRwBe/wwow+vQXZw==", 85 | "requires": { 86 | "@types/node": "*" 87 | } 88 | }, 89 | "@types/mongodb": { 90 | "version": "3.5.25", 91 | "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.25.tgz", 92 | "integrity": "sha512-2H/Owt+pHCl9YmBOYnXc3VdnxejJEjVdH+QCWL5ZAfPehEn3evygKBX3/vKRv7aTwfNbUd0E5vjJdQklH/9a6w==", 93 | "dev": true, 94 | "requires": { 95 | "@types/bson": "*", 96 | "@types/node": "*" 97 | } 98 | }, 99 | "@types/node": { 100 | "version": "14.0.27", 101 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", 102 | "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==" 103 | }, 104 | "@types/restify": { 105 | "version": "8.4.2", 106 | "resolved": "https://registry.npmjs.org/@types/restify/-/restify-8.4.2.tgz", 107 | "integrity": "sha512-jdXB0IrsigqMccBMs3a2kBUUAlYTbjLCbfC63sI00rwTXc+B4UQniGkAJCGS27CAxwkJFAFXzpk0msOQtQ1RXA==", 108 | "dev": true, 109 | "requires": { 110 | "@types/bunyan": "*", 111 | "@types/formidable": "*", 112 | "@types/node": "*", 113 | "@types/spdy": "*" 114 | } 115 | }, 116 | "@types/spdy": { 117 | "version": "3.4.4", 118 | "resolved": "https://registry.npmjs.org/@types/spdy/-/spdy-3.4.4.tgz", 119 | "integrity": "sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==", 120 | "dev": true, 121 | "requires": { 122 | "@types/node": "*" 123 | } 124 | }, 125 | "@types/ws": { 126 | "version": "6.0.4", 127 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", 128 | "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", 129 | "requires": { 130 | "@types/node": "*" 131 | } 132 | }, 133 | "adal-node": { 134 | "version": "0.2.1", 135 | "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.2.1.tgz", 136 | "integrity": "sha512-C/oasZuTy0NIqh5wPWjG/09XaG+zS7elC8upf1ZVExt9lSRncme4Ejbx8CKYk+wsGgj609y84txtRAXQVvqApg==", 137 | "requires": { 138 | "@types/node": "^8.0.47", 139 | "async": "^2.6.3", 140 | "date-utils": "*", 141 | "jws": "3.x.x", 142 | "request": "^2.88.0", 143 | "underscore": ">= 1.3.1", 144 | "uuid": "^3.1.0", 145 | "xmldom": ">= 0.1.x", 146 | "xpath.js": "~1.1.0" 147 | }, 148 | "dependencies": { 149 | "@types/node": { 150 | "version": "8.10.62", 151 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.62.tgz", 152 | "integrity": "sha512-76fupxOYVxk36kb7O/6KtrAPZ9jnSK3+qisAX4tQMEuGNdlvl7ycwatlHqjoE6jHfVtXFM3pCrCixZOidc5cuw==" 153 | } 154 | } 155 | }, 156 | "ajv": { 157 | "version": "6.12.3", 158 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", 159 | "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", 160 | "requires": { 161 | "fast-deep-equal": "^3.1.1", 162 | "fast-json-stable-stringify": "^2.0.0", 163 | "json-schema-traverse": "^0.4.1", 164 | "uri-js": "^4.2.2" 165 | } 166 | }, 167 | "asn1": { 168 | "version": "0.2.4", 169 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 170 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 171 | "requires": { 172 | "safer-buffer": "~2.1.0" 173 | } 174 | }, 175 | "assert": { 176 | "version": "1.5.0", 177 | "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", 178 | "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", 179 | "requires": { 180 | "object-assign": "^4.1.1", 181 | "util": "0.10.3" 182 | } 183 | }, 184 | "assert-plus": { 185 | "version": "1.0.0", 186 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 187 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 188 | }, 189 | "async": { 190 | "version": "2.6.3", 191 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", 192 | "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", 193 | "requires": { 194 | "lodash": "^4.17.14" 195 | } 196 | }, 197 | "asynckit": { 198 | "version": "0.4.0", 199 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 200 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 201 | }, 202 | "aws-sign2": { 203 | "version": "0.7.0", 204 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 205 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 206 | }, 207 | "aws4": { 208 | "version": "1.10.0", 209 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", 210 | "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" 211 | }, 212 | "axios": { 213 | "version": "0.19.2", 214 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", 215 | "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", 216 | "requires": { 217 | "follow-redirects": "1.5.10" 218 | } 219 | }, 220 | "balanced-match": { 221 | "version": "1.0.0", 222 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 223 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 224 | "optional": true 225 | }, 226 | "base64url": { 227 | "version": "3.0.1", 228 | "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", 229 | "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" 230 | }, 231 | "bcrypt-pbkdf": { 232 | "version": "1.0.2", 233 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 234 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 235 | "requires": { 236 | "tweetnacl": "^0.14.3" 237 | } 238 | }, 239 | "bl": { 240 | "version": "2.2.1", 241 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", 242 | "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", 243 | "requires": { 244 | "readable-stream": "^2.3.5", 245 | "safe-buffer": "^5.1.1" 246 | } 247 | }, 248 | "botbuilder": { 249 | "version": "4.9.3", 250 | "resolved": "https://registry.npmjs.org/botbuilder/-/botbuilder-4.9.3.tgz", 251 | "integrity": "sha512-t5nBBRu3/Shw0TJVlmAwu1xBsslBOc8PsleiGkwjs2nM8tmbNS48Kkjyn1BKWpNaLI0cRMJGBE9+7iVfxkN/hw==", 252 | "requires": { 253 | "@azure/ms-rest-js": "1.2.6", 254 | "@types/node": "^10.12.18", 255 | "axios": "^0.19.0", 256 | "botbuilder-core": "4.9.3", 257 | "botframework-connector": "4.9.3", 258 | "botframework-streaming": "4.9.3", 259 | "filenamify": "^4.1.0", 260 | "fs-extra": "^7.0.1", 261 | "moment-timezone": "^0.5.28" 262 | }, 263 | "dependencies": { 264 | "@types/node": { 265 | "version": "10.17.28", 266 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", 267 | "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==" 268 | } 269 | } 270 | }, 271 | "botbuilder-core": { 272 | "version": "4.9.3", 273 | "resolved": "https://registry.npmjs.org/botbuilder-core/-/botbuilder-core-4.9.3.tgz", 274 | "integrity": "sha512-UKMmi+znm3u/UzMbJbmPQtqnVMP+3g4iQPucEbeVMmtj7NWsbZlRa/psXzfdMey7poxCMewUrJR6m69AFXXzcA==", 275 | "requires": { 276 | "assert": "^1.4.1", 277 | "botframework-schema": "4.9.3" 278 | } 279 | }, 280 | "botbuilder-greeting": { 281 | "version": "1.0.1", 282 | "resolved": "https://registry.npmjs.org/botbuilder-greeting/-/botbuilder-greeting-1.0.1.tgz", 283 | "integrity": "sha512-+1gCGL90alNyCrCxMx2ao8YhaRIbrPnvUOnUZP6X9L0MXHZSqUcJ7hUksoSlFXxTqY4/dvmvFjFvB1jeQOOsAQ==" 284 | }, 285 | "botframework-connector": { 286 | "version": "4.9.3", 287 | "resolved": "https://registry.npmjs.org/botframework-connector/-/botframework-connector-4.9.3.tgz", 288 | "integrity": "sha512-W94vcqbFHl7mAfwRGTqiVCMJ8BWF88SrHQtumoCFYmpHOU/VZ6LUGdNITB3ioFVtvV9a6UJ1DvdfxOl4tOq46g==", 289 | "requires": { 290 | "@azure/ms-rest-js": "1.2.6", 291 | "@types/jsonwebtoken": "7.2.8", 292 | "@types/node": "^10.12.18", 293 | "adal-node": "0.2.1", 294 | "base64url": "^3.0.0", 295 | "botframework-schema": "4.9.3", 296 | "form-data": "^2.3.3", 297 | "jsonwebtoken": "8.0.1", 298 | "node-fetch": "^2.2.1", 299 | "rsa-pem-from-mod-exp": "^0.8.4" 300 | }, 301 | "dependencies": { 302 | "@types/node": { 303 | "version": "10.17.28", 304 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", 305 | "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==" 306 | } 307 | } 308 | }, 309 | "botframework-schema": { 310 | "version": "4.9.3", 311 | "resolved": "https://registry.npmjs.org/botframework-schema/-/botframework-schema-4.9.3.tgz", 312 | "integrity": "sha512-gox7Yp9aOhBW6ajyHWRiFCkIW8iRevQRqPN/Ty9zVV1kvHO6a3sqgpX/P0QgzoV0w/N4XDPCbC31lkWHrqXjWQ==" 313 | }, 314 | "botframework-streaming": { 315 | "version": "4.9.3", 316 | "resolved": "https://registry.npmjs.org/botframework-streaming/-/botframework-streaming-4.9.3.tgz", 317 | "integrity": "sha512-BxXdxgInkr6LampLU5Iyn85N683hJREMg8RhhwzJpovP5Td4C/OTUIx42d/51S0WAhsp8+5rfXJhG11NQBRSgg==", 318 | "requires": { 319 | "@types/ws": "^6.0.3", 320 | "uuid": "^3.3.2", 321 | "ws": "^7.1.2" 322 | } 323 | }, 324 | "brace-expansion": { 325 | "version": "1.1.11", 326 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 327 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 328 | "optional": true, 329 | "requires": { 330 | "balanced-match": "^1.0.0", 331 | "concat-map": "0.0.1" 332 | } 333 | }, 334 | "bson": { 335 | "version": "1.1.4", 336 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", 337 | "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" 338 | }, 339 | "buffer-equal-constant-time": { 340 | "version": "1.0.1", 341 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 342 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 343 | }, 344 | "bunyan": { 345 | "version": "1.8.12", 346 | "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", 347 | "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", 348 | "requires": { 349 | "dtrace-provider": "~0.8", 350 | "moment": "^2.10.6", 351 | "mv": "~2", 352 | "safe-json-stringify": "~1" 353 | } 354 | }, 355 | "caseless": { 356 | "version": "0.12.0", 357 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 358 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 359 | }, 360 | "combined-stream": { 361 | "version": "1.0.8", 362 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 363 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 364 | "requires": { 365 | "delayed-stream": "~1.0.0" 366 | } 367 | }, 368 | "concat-map": { 369 | "version": "0.0.1", 370 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 371 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 372 | "optional": true 373 | }, 374 | "core-util-is": { 375 | "version": "1.0.2", 376 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 377 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 378 | }, 379 | "csv": { 380 | "version": "5.3.2", 381 | "resolved": "https://registry.npmjs.org/csv/-/csv-5.3.2.tgz", 382 | "integrity": "sha512-odDyucr9OgJTdGM2wrMbJXbOkJx3nnUX3Pt8SFOwlAMOpsUQlz1dywvLMXJWX/4Ib0rjfOsaawuuwfI5ucqBGQ==", 383 | "requires": { 384 | "csv-generate": "^3.2.4", 385 | "csv-parse": "^4.8.8", 386 | "csv-stringify": "^5.3.6", 387 | "stream-transform": "^2.0.1" 388 | } 389 | }, 390 | "csv-generate": { 391 | "version": "3.2.4", 392 | "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-3.2.4.tgz", 393 | "integrity": "sha512-qNM9eqlxd53TWJeGtY1IQPj90b563Zx49eZs8e0uMyEvPgvNVmX1uZDtdzAcflB3PniuH9creAzcFOdyJ9YGvA==" 394 | }, 395 | "csv-parse": { 396 | "version": "4.8.9", 397 | "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.8.9.tgz", 398 | "integrity": "sha512-uDxIDIDLb89gxqixSgGqDj3EA5A8D0pgUeyp9Qut8u+eCIC8IXkTtaxJEnnWDb6N2HqBY64suSlcOGg5ZBtsAQ==" 399 | }, 400 | "csv-stringify": { 401 | "version": "5.4.3", 402 | "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.4.3.tgz", 403 | "integrity": "sha512-WJLgRJQcVjPK45jXS1xfnkwVbw9bOjg2F2BQRa9OkG7Di2W/geclPZNlcQTwxbzn1nEDI2ane2AubTdTd6gCvw==" 404 | }, 405 | "dashdash": { 406 | "version": "1.14.1", 407 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 408 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 409 | "requires": { 410 | "assert-plus": "^1.0.0" 411 | } 412 | }, 413 | "date-utils": { 414 | "version": "1.2.21", 415 | "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", 416 | "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" 417 | }, 418 | "debug": { 419 | "version": "3.1.0", 420 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 421 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 422 | "requires": { 423 | "ms": "2.0.0" 424 | } 425 | }, 426 | "delayed-stream": { 427 | "version": "1.0.0", 428 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 429 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 430 | }, 431 | "denque": { 432 | "version": "1.4.1", 433 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", 434 | "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" 435 | }, 436 | "depd": { 437 | "version": "1.1.2", 438 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 439 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 440 | }, 441 | "destroy": { 442 | "version": "1.0.4", 443 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 444 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 445 | }, 446 | "detect-node": { 447 | "version": "2.0.4", 448 | "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", 449 | "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" 450 | }, 451 | "dtrace-provider": { 452 | "version": "0.8.8", 453 | "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", 454 | "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", 455 | "optional": true, 456 | "requires": { 457 | "nan": "^2.14.0" 458 | } 459 | }, 460 | "ecc-jsbn": { 461 | "version": "0.1.2", 462 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 463 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 464 | "requires": { 465 | "jsbn": "~0.1.0", 466 | "safer-buffer": "^2.1.0" 467 | } 468 | }, 469 | "ecdsa-sig-formatter": { 470 | "version": "1.0.11", 471 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 472 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 473 | "requires": { 474 | "safe-buffer": "^5.0.1" 475 | } 476 | }, 477 | "ee-first": { 478 | "version": "1.1.1", 479 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 480 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 481 | }, 482 | "encodeurl": { 483 | "version": "1.0.2", 484 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 485 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 486 | }, 487 | "escape-html": { 488 | "version": "1.0.3", 489 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 490 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 491 | }, 492 | "escape-regexp-component": { 493 | "version": "1.0.2", 494 | "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", 495 | "integrity": "sha1-nGO20LJf8qiMOtvRjFthrMO5+qI=" 496 | }, 497 | "escape-string-regexp": { 498 | "version": "1.0.5", 499 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 500 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 501 | }, 502 | "etag": { 503 | "version": "1.8.1", 504 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 505 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 506 | }, 507 | "ewma": { 508 | "version": "2.0.1", 509 | "resolved": "https://registry.npmjs.org/ewma/-/ewma-2.0.1.tgz", 510 | "integrity": "sha512-MYYK17A76cuuyvkR7MnqLW4iFYPEi5Isl2qb8rXiWpLiwFS9dxW/rncuNnjjgSENuVqZQkIuR4+DChVL4g1lnw==", 511 | "requires": { 512 | "assert-plus": "^1.0.0" 513 | } 514 | }, 515 | "extend": { 516 | "version": "3.0.2", 517 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 518 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 519 | }, 520 | "extsprintf": { 521 | "version": "1.3.0", 522 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 523 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 524 | }, 525 | "fast-decode-uri-component": { 526 | "version": "1.0.1", 527 | "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", 528 | "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" 529 | }, 530 | "fast-deep-equal": { 531 | "version": "3.1.3", 532 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 533 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 534 | }, 535 | "fast-json-stable-stringify": { 536 | "version": "2.1.0", 537 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 538 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 539 | }, 540 | "filename-reserved-regex": { 541 | "version": "2.0.0", 542 | "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", 543 | "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=" 544 | }, 545 | "filenamify": { 546 | "version": "4.1.0", 547 | "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.1.0.tgz", 548 | "integrity": "sha512-KQV/uJDI9VQgN7sHH1Zbk6+42cD6mnQ2HONzkXUfPJ+K2FC8GZ1dpewbbHw0Sz8Tf5k3EVdHVayM4DoAwWlmtg==", 549 | "requires": { 550 | "filename-reserved-regex": "^2.0.0", 551 | "strip-outer": "^1.0.1", 552 | "trim-repeated": "^1.0.0" 553 | } 554 | }, 555 | "find-my-way": { 556 | "version": "2.2.5", 557 | "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-2.2.5.tgz", 558 | "integrity": "sha512-GjRZZlGcGmTh9t+6Xrj5K0YprpoAFCAiCPgmAH9Kb09O4oX6hYuckDfnDipYj+Q7B1GtYWSzDI5HEecNYscLQg==", 559 | "requires": { 560 | "fast-decode-uri-component": "^1.0.0", 561 | "safe-regex2": "^2.0.0", 562 | "semver-store": "^0.3.0" 563 | } 564 | }, 565 | "follow-redirects": { 566 | "version": "1.5.10", 567 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", 568 | "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", 569 | "requires": { 570 | "debug": "=3.1.0" 571 | } 572 | }, 573 | "forever-agent": { 574 | "version": "0.6.1", 575 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 576 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 577 | }, 578 | "form-data": { 579 | "version": "2.5.1", 580 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 581 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 582 | "requires": { 583 | "asynckit": "^0.4.0", 584 | "combined-stream": "^1.0.6", 585 | "mime-types": "^2.1.12" 586 | } 587 | }, 588 | "formidable": { 589 | "version": "1.2.2", 590 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", 591 | "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" 592 | }, 593 | "fresh": { 594 | "version": "0.5.2", 595 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 596 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 597 | }, 598 | "fs-extra": { 599 | "version": "7.0.1", 600 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", 601 | "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", 602 | "requires": { 603 | "graceful-fs": "^4.1.2", 604 | "jsonfile": "^4.0.0", 605 | "universalify": "^0.1.0" 606 | } 607 | }, 608 | "getpass": { 609 | "version": "0.1.7", 610 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 611 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 612 | "requires": { 613 | "assert-plus": "^1.0.0" 614 | } 615 | }, 616 | "graceful-fs": { 617 | "version": "4.2.4", 618 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 619 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 620 | }, 621 | "handle-thing": { 622 | "version": "2.0.1", 623 | "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", 624 | "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" 625 | }, 626 | "har-schema": { 627 | "version": "2.0.0", 628 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 629 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 630 | }, 631 | "har-validator": { 632 | "version": "5.1.5", 633 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", 634 | "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", 635 | "requires": { 636 | "ajv": "^6.12.3", 637 | "har-schema": "^2.0.0" 638 | } 639 | }, 640 | "hpack.js": { 641 | "version": "2.1.6", 642 | "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", 643 | "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", 644 | "requires": { 645 | "inherits": "^2.0.1", 646 | "obuf": "^1.0.0", 647 | "readable-stream": "^2.0.1", 648 | "wbuf": "^1.1.0" 649 | } 650 | }, 651 | "http-deceiver": { 652 | "version": "1.2.7", 653 | "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", 654 | "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" 655 | }, 656 | "http-errors": { 657 | "version": "1.6.3", 658 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 659 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 660 | "requires": { 661 | "depd": "~1.1.2", 662 | "inherits": "2.0.3", 663 | "setprototypeof": "1.1.0", 664 | "statuses": ">= 1.4.0 < 2" 665 | } 666 | }, 667 | "http-signature": { 668 | "version": "1.3.4", 669 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.4.tgz", 670 | "integrity": "sha512-CbG3io8gUSIxNNSgq+XMjgpTMzAeVRipxVXjuGrDhH5M1a2kZ03w20s8FCLR1NjnnJj10KbvabvckmtQcYNb9g==", 671 | "requires": { 672 | "assert-plus": "^1.0.0", 673 | "jsprim": "^1.2.2", 674 | "sshpk": "^1.14.1" 675 | } 676 | }, 677 | "inflight": { 678 | "version": "1.0.6", 679 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 680 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 681 | "optional": true, 682 | "requires": { 683 | "once": "^1.3.0", 684 | "wrappy": "1" 685 | } 686 | }, 687 | "inherits": { 688 | "version": "2.0.3", 689 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 690 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 691 | }, 692 | "is-buffer": { 693 | "version": "2.0.4", 694 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", 695 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" 696 | }, 697 | "is-typedarray": { 698 | "version": "1.0.0", 699 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 700 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 701 | }, 702 | "isarray": { 703 | "version": "1.0.0", 704 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 705 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 706 | }, 707 | "isstream": { 708 | "version": "0.1.2", 709 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 710 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 711 | }, 712 | "jsbn": { 713 | "version": "0.1.1", 714 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 715 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 716 | }, 717 | "json-schema": { 718 | "version": "0.2.3", 719 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 720 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 721 | }, 722 | "json-schema-traverse": { 723 | "version": "0.4.1", 724 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 725 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 726 | }, 727 | "json-stringify-safe": { 728 | "version": "5.0.1", 729 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 730 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 731 | }, 732 | "jsonfile": { 733 | "version": "4.0.0", 734 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 735 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 736 | "requires": { 737 | "graceful-fs": "^4.1.6" 738 | } 739 | }, 740 | "jsonwebtoken": { 741 | "version": "8.0.1", 742 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.0.1.tgz", 743 | "integrity": "sha1-UNrvjQqMfeLNBrwQE7dbBMzz8M8=", 744 | "requires": { 745 | "jws": "^3.1.4", 746 | "lodash.includes": "^4.3.0", 747 | "lodash.isboolean": "^3.0.3", 748 | "lodash.isinteger": "^4.0.4", 749 | "lodash.isnumber": "^3.0.3", 750 | "lodash.isplainobject": "^4.0.6", 751 | "lodash.isstring": "^4.0.1", 752 | "lodash.once": "^4.0.0", 753 | "ms": "^2.0.0", 754 | "xtend": "^4.0.1" 755 | } 756 | }, 757 | "jsprim": { 758 | "version": "1.4.1", 759 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 760 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 761 | "requires": { 762 | "assert-plus": "1.0.0", 763 | "extsprintf": "1.3.0", 764 | "json-schema": "0.2.3", 765 | "verror": "1.10.0" 766 | } 767 | }, 768 | "jwa": { 769 | "version": "1.4.1", 770 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 771 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 772 | "requires": { 773 | "buffer-equal-constant-time": "1.0.1", 774 | "ecdsa-sig-formatter": "1.0.11", 775 | "safe-buffer": "^5.0.1" 776 | } 777 | }, 778 | "jws": { 779 | "version": "3.2.2", 780 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 781 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 782 | "requires": { 783 | "jwa": "^1.4.1", 784 | "safe-buffer": "^5.0.1" 785 | } 786 | }, 787 | "lodash": { 788 | "version": "4.17.21", 789 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 790 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 791 | }, 792 | "lodash.includes": { 793 | "version": "4.3.0", 794 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 795 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 796 | }, 797 | "lodash.isboolean": { 798 | "version": "3.0.3", 799 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 800 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 801 | }, 802 | "lodash.isinteger": { 803 | "version": "4.0.4", 804 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 805 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 806 | }, 807 | "lodash.isnumber": { 808 | "version": "3.0.3", 809 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 810 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 811 | }, 812 | "lodash.isplainobject": { 813 | "version": "4.0.6", 814 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 815 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 816 | }, 817 | "lodash.isstring": { 818 | "version": "4.0.1", 819 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 820 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 821 | }, 822 | "lodash.once": { 823 | "version": "4.1.1", 824 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 825 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 826 | }, 827 | "lru-cache": { 828 | "version": "5.1.1", 829 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 830 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 831 | "requires": { 832 | "yallist": "^3.0.2" 833 | } 834 | }, 835 | "memory-pager": { 836 | "version": "1.5.0", 837 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 838 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 839 | "optional": true 840 | }, 841 | "mime": { 842 | "version": "2.4.4", 843 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", 844 | "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" 845 | }, 846 | "mime-db": { 847 | "version": "1.44.0", 848 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 849 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 850 | }, 851 | "mime-types": { 852 | "version": "2.1.27", 853 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 854 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 855 | "requires": { 856 | "mime-db": "1.44.0" 857 | } 858 | }, 859 | "minimalistic-assert": { 860 | "version": "1.0.1", 861 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 862 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 863 | }, 864 | "minimatch": { 865 | "version": "3.0.4", 866 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 867 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 868 | "optional": true, 869 | "requires": { 870 | "brace-expansion": "^1.1.7" 871 | } 872 | }, 873 | "minimist": { 874 | "version": "1.2.5", 875 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 876 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 877 | "optional": true 878 | }, 879 | "mixme": { 880 | "version": "0.3.5", 881 | "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.3.5.tgz", 882 | "integrity": "sha512-SyV9uPETRig5ZmYev0ANfiGeB+g6N2EnqqEfBbCGmmJ6MgZ3E4qv5aPbnHVdZ60KAHHXV+T3sXopdrnIXQdmjQ==" 883 | }, 884 | "mkdirp": { 885 | "version": "0.5.5", 886 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 887 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 888 | "optional": true, 889 | "requires": { 890 | "minimist": "^1.2.5" 891 | } 892 | }, 893 | "moment": { 894 | "version": "2.24.0", 895 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", 896 | "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" 897 | }, 898 | "moment-timezone": { 899 | "version": "0.5.31", 900 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", 901 | "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", 902 | "requires": { 903 | "moment": ">= 2.9.0" 904 | } 905 | }, 906 | "mongodb": { 907 | "version": "3.6.0", 908 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.0.tgz", 909 | "integrity": "sha512-/XWWub1mHZVoqEsUppE0GV7u9kanLvHxho6EvBxQbShXTKYF9trhZC2NzbulRGeG7xMJHD8IOWRcdKx5LPjAjQ==", 910 | "requires": { 911 | "bl": "^2.2.0", 912 | "bson": "^1.1.4", 913 | "denque": "^1.4.1", 914 | "require_optional": "^1.0.1", 915 | "safe-buffer": "^5.1.2", 916 | "saslprep": "^1.0.0" 917 | } 918 | }, 919 | "ms": { 920 | "version": "2.0.0", 921 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 922 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 923 | }, 924 | "mv": { 925 | "version": "2.1.1", 926 | "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", 927 | "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", 928 | "optional": true, 929 | "requires": { 930 | "mkdirp": "~0.5.1", 931 | "ncp": "~2.0.0", 932 | "rimraf": "~2.4.0" 933 | }, 934 | "dependencies": { 935 | "glob": { 936 | "version": "6.0.4", 937 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 938 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 939 | "optional": true, 940 | "requires": { 941 | "inflight": "^1.0.4", 942 | "inherits": "2", 943 | "minimatch": "2 || 3", 944 | "once": "^1.3.0", 945 | "path-is-absolute": "^1.0.0" 946 | } 947 | }, 948 | "rimraf": { 949 | "version": "2.4.5", 950 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", 951 | "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", 952 | "optional": true, 953 | "requires": { 954 | "glob": "^6.0.1" 955 | } 956 | } 957 | } 958 | }, 959 | "nan": { 960 | "version": "2.14.1", 961 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", 962 | "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", 963 | "optional": true 964 | }, 965 | "ncp": { 966 | "version": "2.0.0", 967 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 968 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", 969 | "optional": true 970 | }, 971 | "negotiator": { 972 | "version": "0.6.2", 973 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 974 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 975 | }, 976 | "node-fetch": { 977 | "version": "2.6.1", 978 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 979 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 980 | }, 981 | "oauth-sign": { 982 | "version": "0.9.0", 983 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 984 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 985 | }, 986 | "object-assign": { 987 | "version": "4.1.1", 988 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 989 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 990 | }, 991 | "obuf": { 992 | "version": "1.1.2", 993 | "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", 994 | "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" 995 | }, 996 | "on-finished": { 997 | "version": "2.3.0", 998 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 999 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1000 | "requires": { 1001 | "ee-first": "1.1.1" 1002 | } 1003 | }, 1004 | "once": { 1005 | "version": "1.4.0", 1006 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1007 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1008 | "requires": { 1009 | "wrappy": "1" 1010 | } 1011 | }, 1012 | "path-is-absolute": { 1013 | "version": "1.0.1", 1014 | "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1015 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1016 | "optional": true 1017 | }, 1018 | "performance-now": { 1019 | "version": "2.1.0", 1020 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 1021 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 1022 | }, 1023 | "pidusage": { 1024 | "version": "2.0.18", 1025 | "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.18.tgz", 1026 | "integrity": "sha512-Y/VfKfh3poHjMEINxU+gJTeVOBjiThQeFAmzR7z56HSNiMx+etl+yBhk42nRPciPYt/VZl8DQLVXNC6P5vH11A==", 1027 | "requires": { 1028 | "safe-buffer": "^5.1.2" 1029 | } 1030 | }, 1031 | "process-nextick-args": { 1032 | "version": "2.0.1", 1033 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1034 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1035 | }, 1036 | "psl": { 1037 | "version": "1.8.0", 1038 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 1039 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" 1040 | }, 1041 | "punycode": { 1042 | "version": "2.1.1", 1043 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1044 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1045 | }, 1046 | "qs": { 1047 | "version": "6.5.2", 1048 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1049 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1050 | }, 1051 | "range-parser": { 1052 | "version": "1.2.1", 1053 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1054 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1055 | }, 1056 | "readable-stream": { 1057 | "version": "2.3.7", 1058 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 1059 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 1060 | "requires": { 1061 | "core-util-is": "~1.0.0", 1062 | "inherits": "~2.0.3", 1063 | "isarray": "~1.0.0", 1064 | "process-nextick-args": "~2.0.0", 1065 | "safe-buffer": "~5.1.1", 1066 | "string_decoder": "~1.1.1", 1067 | "util-deprecate": "~1.0.1" 1068 | } 1069 | }, 1070 | "request": { 1071 | "version": "2.88.2", 1072 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", 1073 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", 1074 | "requires": { 1075 | "aws-sign2": "~0.7.0", 1076 | "aws4": "^1.8.0", 1077 | "caseless": "~0.12.0", 1078 | "combined-stream": "~1.0.6", 1079 | "extend": "~3.0.2", 1080 | "forever-agent": "~0.6.1", 1081 | "form-data": "~2.3.2", 1082 | "har-validator": "~5.1.3", 1083 | "http-signature": "~1.2.0", 1084 | "is-typedarray": "~1.0.0", 1085 | "isstream": "~0.1.2", 1086 | "json-stringify-safe": "~5.0.1", 1087 | "mime-types": "~2.1.19", 1088 | "oauth-sign": "~0.9.0", 1089 | "performance-now": "^2.1.0", 1090 | "qs": "~6.5.2", 1091 | "safe-buffer": "^5.1.2", 1092 | "tough-cookie": "~2.5.0", 1093 | "tunnel-agent": "^0.6.0", 1094 | "uuid": "^3.3.2" 1095 | }, 1096 | "dependencies": { 1097 | "form-data": { 1098 | "version": "2.3.3", 1099 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 1100 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 1101 | "requires": { 1102 | "asynckit": "^0.4.0", 1103 | "combined-stream": "^1.0.6", 1104 | "mime-types": "^2.1.12" 1105 | } 1106 | }, 1107 | "http-signature": { 1108 | "version": "1.2.0", 1109 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 1110 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 1111 | "requires": { 1112 | "assert-plus": "^1.0.0", 1113 | "jsprim": "^1.2.2", 1114 | "sshpk": "^1.7.0" 1115 | } 1116 | } 1117 | } 1118 | }, 1119 | "require_optional": { 1120 | "version": "1.0.1", 1121 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 1122 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 1123 | "requires": { 1124 | "resolve-from": "^2.0.0", 1125 | "semver": "^5.1.0" 1126 | } 1127 | }, 1128 | "resolve-from": { 1129 | "version": "2.0.0", 1130 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 1131 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 1132 | }, 1133 | "restify": { 1134 | "version": "8.5.1", 1135 | "resolved": "https://registry.npmjs.org/restify/-/restify-8.5.1.tgz", 1136 | "integrity": "sha512-g+xciouvSDg2vmCZuCinztt2mvQynCfnGIE1y8vMjfcUrjBo4AP8DJ9RNheu0mdGpiI0cMoCHYA/GdZ3TEW+DA==", 1137 | "requires": { 1138 | "assert-plus": "^1.0.0", 1139 | "bunyan": "^1.8.12", 1140 | "csv": "^5.1.1", 1141 | "dtrace-provider": "^0.8.1", 1142 | "escape-regexp-component": "^1.0.2", 1143 | "ewma": "^2.0.1", 1144 | "find-my-way": "^2.0.1", 1145 | "formidable": "^1.2.1", 1146 | "http-signature": "^1.2.0", 1147 | "lodash": "^4.17.11", 1148 | "lru-cache": "^5.1.1", 1149 | "mime": "^2.4.3", 1150 | "negotiator": "^0.6.2", 1151 | "once": "^1.4.0", 1152 | "pidusage": "^2.0.17", 1153 | "qs": "^6.7.0", 1154 | "restify-errors": "^8.0.2", 1155 | "semver": "^6.1.1", 1156 | "send": "^0.16.2", 1157 | "spdy": "^4.0.0", 1158 | "uuid": "^3.3.2", 1159 | "vasync": "^2.2.0" 1160 | }, 1161 | "dependencies": { 1162 | "qs": { 1163 | "version": "6.9.3", 1164 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", 1165 | "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" 1166 | }, 1167 | "semver": { 1168 | "version": "6.3.0", 1169 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1170 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 1171 | } 1172 | } 1173 | }, 1174 | "restify-errors": { 1175 | "version": "8.0.2", 1176 | "resolved": "https://registry.npmjs.org/restify-errors/-/restify-errors-8.0.2.tgz", 1177 | "integrity": "sha512-UsXUVQo7M26xoQzeUcZQ0+H8L2t9DGzrXcAgR3WB/1vnbl+UdI4tZ1PqYsN+sS5WnqHKZ0Xy9w0CKf83bbrwYA==", 1178 | "requires": { 1179 | "@netflix/nerror": "^1.0.0", 1180 | "assert-plus": "^1.0.0", 1181 | "lodash": "^4.17.15", 1182 | "safe-json-stringify": "^1.0.4" 1183 | } 1184 | }, 1185 | "ret": { 1186 | "version": "0.2.2", 1187 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", 1188 | "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" 1189 | }, 1190 | "rsa-pem-from-mod-exp": { 1191 | "version": "0.8.4", 1192 | "resolved": "https://registry.npmjs.org/rsa-pem-from-mod-exp/-/rsa-pem-from-mod-exp-0.8.4.tgz", 1193 | "integrity": "sha1-NipCxtMEBW1JOz8SvOq7LGV2ptQ=" 1194 | }, 1195 | "safe-buffer": { 1196 | "version": "5.1.2", 1197 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1198 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1199 | }, 1200 | "safe-json-stringify": { 1201 | "version": "1.2.0", 1202 | "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", 1203 | "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", 1204 | "optional": true 1205 | }, 1206 | "safe-regex2": { 1207 | "version": "2.0.0", 1208 | "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", 1209 | "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", 1210 | "requires": { 1211 | "ret": "~0.2.0" 1212 | } 1213 | }, 1214 | "safer-buffer": { 1215 | "version": "2.1.2", 1216 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1217 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1218 | }, 1219 | "saslprep": { 1220 | "version": "1.0.3", 1221 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 1222 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 1223 | "optional": true, 1224 | "requires": { 1225 | "sparse-bitfield": "^3.0.3" 1226 | } 1227 | }, 1228 | "sax": { 1229 | "version": "1.2.4", 1230 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 1231 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 1232 | }, 1233 | "select-hose": { 1234 | "version": "2.0.0", 1235 | "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", 1236 | "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" 1237 | }, 1238 | "semver": { 1239 | "version": "5.7.1", 1240 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1241 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 1242 | }, 1243 | "semver-store": { 1244 | "version": "0.3.0", 1245 | "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz", 1246 | "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==" 1247 | }, 1248 | "send": { 1249 | "version": "0.16.2", 1250 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 1251 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 1252 | "requires": { 1253 | "debug": "2.6.9", 1254 | "depd": "~1.1.2", 1255 | "destroy": "~1.0.4", 1256 | "encodeurl": "~1.0.2", 1257 | "escape-html": "~1.0.3", 1258 | "etag": "~1.8.1", 1259 | "fresh": "0.5.2", 1260 | "http-errors": "~1.6.2", 1261 | "mime": "1.4.1", 1262 | "ms": "2.0.0", 1263 | "on-finished": "~2.3.0", 1264 | "range-parser": "~1.2.0", 1265 | "statuses": "~1.4.0" 1266 | }, 1267 | "dependencies": { 1268 | "debug": { 1269 | "version": "2.6.9", 1270 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1271 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1272 | "requires": { 1273 | "ms": "2.0.0" 1274 | } 1275 | }, 1276 | "mime": { 1277 | "version": "1.4.1", 1278 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 1279 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 1280 | } 1281 | } 1282 | }, 1283 | "setprototypeof": { 1284 | "version": "1.1.0", 1285 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 1286 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 1287 | }, 1288 | "sparse-bitfield": { 1289 | "version": "3.0.3", 1290 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1291 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 1292 | "optional": true, 1293 | "requires": { 1294 | "memory-pager": "^1.0.2" 1295 | } 1296 | }, 1297 | "spdy": { 1298 | "version": "4.0.2", 1299 | "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", 1300 | "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", 1301 | "requires": { 1302 | "debug": "^4.1.0", 1303 | "handle-thing": "^2.0.0", 1304 | "http-deceiver": "^1.2.7", 1305 | "select-hose": "^2.0.0", 1306 | "spdy-transport": "^3.0.0" 1307 | }, 1308 | "dependencies": { 1309 | "debug": { 1310 | "version": "4.1.1", 1311 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 1312 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 1313 | "requires": { 1314 | "ms": "^2.1.1" 1315 | } 1316 | }, 1317 | "ms": { 1318 | "version": "2.1.2", 1319 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1320 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1321 | } 1322 | } 1323 | }, 1324 | "spdy-transport": { 1325 | "version": "3.0.0", 1326 | "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", 1327 | "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", 1328 | "requires": { 1329 | "debug": "^4.1.0", 1330 | "detect-node": "^2.0.4", 1331 | "hpack.js": "^2.1.6", 1332 | "obuf": "^1.1.2", 1333 | "readable-stream": "^3.0.6", 1334 | "wbuf": "^1.7.3" 1335 | }, 1336 | "dependencies": { 1337 | "debug": { 1338 | "version": "4.1.1", 1339 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 1340 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 1341 | "requires": { 1342 | "ms": "^2.1.1" 1343 | } 1344 | }, 1345 | "ms": { 1346 | "version": "2.1.2", 1347 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1348 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1349 | }, 1350 | "readable-stream": { 1351 | "version": "3.6.0", 1352 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1353 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1354 | "requires": { 1355 | "inherits": "^2.0.3", 1356 | "string_decoder": "^1.1.1", 1357 | "util-deprecate": "^1.0.1" 1358 | } 1359 | } 1360 | } 1361 | }, 1362 | "sshpk": { 1363 | "version": "1.16.1", 1364 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 1365 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 1366 | "requires": { 1367 | "asn1": "~0.2.3", 1368 | "assert-plus": "^1.0.0", 1369 | "bcrypt-pbkdf": "^1.0.0", 1370 | "dashdash": "^1.12.0", 1371 | "ecc-jsbn": "~0.1.1", 1372 | "getpass": "^0.1.1", 1373 | "jsbn": "~0.1.0", 1374 | "safer-buffer": "^2.0.2", 1375 | "tweetnacl": "~0.14.0" 1376 | } 1377 | }, 1378 | "statuses": { 1379 | "version": "1.4.0", 1380 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1381 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 1382 | }, 1383 | "stream-transform": { 1384 | "version": "2.0.1", 1385 | "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.1.tgz", 1386 | "integrity": "sha512-GiTcO/rRvZP2R8WPwxmxCFP+Of1yIATuFAmYkvSLDfcD93X2WHiPwdgIqeFT2CvL1gyAsjQvu1nB6RDNQ5b2jw==", 1387 | "requires": { 1388 | "mixme": "^0.3.1" 1389 | } 1390 | }, 1391 | "string_decoder": { 1392 | "version": "1.1.1", 1393 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1394 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1395 | "requires": { 1396 | "safe-buffer": "~5.1.0" 1397 | } 1398 | }, 1399 | "strip-outer": { 1400 | "version": "1.0.1", 1401 | "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", 1402 | "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", 1403 | "requires": { 1404 | "escape-string-regexp": "^1.0.2" 1405 | } 1406 | }, 1407 | "tough-cookie": { 1408 | "version": "2.5.0", 1409 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 1410 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 1411 | "requires": { 1412 | "psl": "^1.1.28", 1413 | "punycode": "^2.1.1" 1414 | } 1415 | }, 1416 | "trim-repeated": { 1417 | "version": "1.0.0", 1418 | "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", 1419 | "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", 1420 | "requires": { 1421 | "escape-string-regexp": "^1.0.2" 1422 | } 1423 | }, 1424 | "tslib": { 1425 | "version": "1.13.0", 1426 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 1427 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" 1428 | }, 1429 | "tunnel-agent": { 1430 | "version": "0.6.0", 1431 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1432 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1433 | "requires": { 1434 | "safe-buffer": "^5.0.1" 1435 | } 1436 | }, 1437 | "tweetnacl": { 1438 | "version": "0.14.5", 1439 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1440 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1441 | }, 1442 | "underscore": { 1443 | "version": "1.10.2", 1444 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", 1445 | "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==" 1446 | }, 1447 | "universalify": { 1448 | "version": "0.1.2", 1449 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 1450 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" 1451 | }, 1452 | "uri-js": { 1453 | "version": "4.2.2", 1454 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1455 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1456 | "requires": { 1457 | "punycode": "^2.1.0" 1458 | } 1459 | }, 1460 | "util": { 1461 | "version": "0.10.3", 1462 | "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", 1463 | "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", 1464 | "requires": { 1465 | "inherits": "2.0.1" 1466 | }, 1467 | "dependencies": { 1468 | "inherits": { 1469 | "version": "2.0.1", 1470 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", 1471 | "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" 1472 | } 1473 | } 1474 | }, 1475 | "util-deprecate": { 1476 | "version": "1.0.2", 1477 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1478 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1479 | }, 1480 | "uuid": { 1481 | "version": "3.3.2", 1482 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1483 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1484 | }, 1485 | "vasync": { 1486 | "version": "2.2.0", 1487 | "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.0.tgz", 1488 | "integrity": "sha1-z951GGChWCLbOxMrxZsRakra8Bs=", 1489 | "requires": { 1490 | "verror": "1.10.0" 1491 | } 1492 | }, 1493 | "verror": { 1494 | "version": "1.10.0", 1495 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1496 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1497 | "requires": { 1498 | "assert-plus": "^1.0.0", 1499 | "core-util-is": "1.0.2", 1500 | "extsprintf": "^1.2.0" 1501 | } 1502 | }, 1503 | "wbuf": { 1504 | "version": "1.7.3", 1505 | "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", 1506 | "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", 1507 | "requires": { 1508 | "minimalistic-assert": "^1.0.0" 1509 | } 1510 | }, 1511 | "wrappy": { 1512 | "version": "1.0.2", 1513 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1514 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1515 | }, 1516 | "ws": { 1517 | "version": "7.3.1", 1518 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", 1519 | "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" 1520 | }, 1521 | "xml2js": { 1522 | "version": "0.4.23", 1523 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", 1524 | "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", 1525 | "requires": { 1526 | "sax": ">=0.6.0", 1527 | "xmlbuilder": "~11.0.0" 1528 | } 1529 | }, 1530 | "xmlbuilder": { 1531 | "version": "11.0.1", 1532 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 1533 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" 1534 | }, 1535 | "xmldom": { 1536 | "version": "0.3.0", 1537 | "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz", 1538 | "integrity": "sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==" 1539 | }, 1540 | "xpath.js": { 1541 | "version": "1.1.0", 1542 | "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", 1543 | "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==" 1544 | }, 1545 | "xtend": { 1546 | "version": "4.0.2", 1547 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1548 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 1549 | }, 1550 | "yallist": { 1551 | "version": "3.1.1", 1552 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1553 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 1554 | } 1555 | } 1556 | } 1557 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-mongo-storage-sample", 3 | "dependencies": { 4 | "botbuilder": "^4.9.3", 5 | "botbuilder-greeting": "^1.0.1", 6 | "mongodb": "^3.6.0", 7 | "restify": "^8.5.1" 8 | }, 9 | "scripts": { 10 | "start": " node app.js" 11 | }, 12 | "devDependencies": { 13 | "@types/mongodb": "3.5.25", 14 | "@types/node": "^14.0.27", 15 | "@types/restify": "^8.4.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/readme_assets/mongo_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbuilder-contrib/botbuilder-storage-mongodb/625dd35b53d760bf1498b9266b696d8a1a777ab7/example/readme_assets/mongo_data.png -------------------------------------------------------------------------------- /example/readme_assets/sample_conversation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/botbuilder-contrib/botbuilder-storage-mongodb/625dd35b53d760bf1498b9266b696d8a1a777ab7/example/readme_assets/sample_conversation.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "botbuilder-storage-mongodb", 3 | "version": "1.0.17", 4 | "license": "MIT", 5 | "contributors": [ 6 | { 7 | "name": "Hattan Shobokshi", 8 | "url": "https://github.com/hattan" 9 | }, 10 | { 11 | "name": "Daniel Egan", 12 | "url": "https://github.com/DanielEgan" 13 | }, 14 | { 15 | "name": "Nuri Halperin", 16 | "url": "https://github.com/nurih" 17 | } 18 | ], 19 | "homepage": "https://github.com/botbuilder-contrib/botbuilder-storage-mongodb#readme", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/botbuilder-contrib/botbuilder-storage-mongodb.git" 23 | }, 24 | "dependencies": { 25 | "@types/mongodb": "^4.0.7", 26 | "botbuilder": "^4.19.3", 27 | "mongodb": "^5.1.0", 28 | "request": "^2.88.2" 29 | }, 30 | "scripts": { 31 | "test:unit": "npm run build && mocha test/unit", 32 | "test:integration": "npm run build && mocha test/integration", 33 | "build": "node node_modules/typescript/bin/tsc" 34 | }, 35 | "devDependencies": { 36 | "@types/mocha": "^10.0.1", 37 | "@types/node": "^18.15.8", 38 | "mocha": "^10.2", 39 | "nyc": "^15.1.0", 40 | "sinon": "^15.0.2", 41 | "source-map-support": "^0.5.21", 42 | "typescript": "^5.0.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pipelines/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | pr: 2 | - master 3 | 4 | queue: 5 | name: Hosted Ubuntu 1604 6 | demands: npm 7 | 8 | steps: 9 | 10 | # Add npm 11 | - task: Npm@1 12 | displayName: 'npm install' 13 | inputs: 14 | verbose: false 15 | 16 | # Ensure version was bumped 17 | - task: PowerShell@2 18 | displayName: 'Package Version Bump Check' 19 | condition: eq(variables['Build.SourceBranch'], 'refs/heads/master') 20 | inputs: 21 | failOnStderr: true 22 | targetType: inline 23 | script: | 24 | Write-Host "Checking published NPM version vs. the code package.json::version" 25 | $publishedVersion = npm show botbuilder-storage-mongodb version 26 | $thisVersion = node -e "console.log(require('./package.json').version)" 27 | Write-Host $publishedVersion ' vs. ' $thisVersion 28 | if ($publishedVersion -eq $thisVersion) { 29 | $message = "Version $thisVersion is the same as published version $publishedVersion on NPM. Please bump source version in package.json." 30 | write-error $message 31 | } 32 | 33 | # Unit tests only 34 | - task: Npm@1 35 | displayName: 'unit tests' 36 | inputs: 37 | command: custom 38 | verbose: false 39 | customCommand: 'run test:unit' 40 | 41 | # Stand a dockerized MongoDB instance 42 | - task: Docker@1 43 | displayName: 'Run MongoDB on Docker' 44 | inputs: 45 | containerregistrytype: 'Container Registry' 46 | command: 'Run an image' 47 | imageName: 'mongo:4.0' 48 | qualifyImageName: false 49 | containerName: 'mongo_4_0' 50 | ports: '27017:27017' 51 | memoryLimit: 1GB 52 | 53 | # Integration tests only 54 | - task: Npm@1 55 | displayName: 'integration tests' 56 | inputs: 57 | command: custom 58 | verbose: false 59 | customCommand: 'run test:integration' 60 | 61 | # Copy package.json into lib 62 | - task: CopyFiles@1 63 | displayName: 'Copy package.json to lib' 64 | inputs: 65 | contents: 'lib/**' 66 | targetFolder: 'package' 67 | 68 | # Copy documents, index.js and package.json into lib 69 | - task: CopyFiles@2 70 | displayName: 'Copy documents into lib' 71 | inputs: 72 | Contents: | 73 | CODE_OF_COUNDUCT.md 74 | Contributing.md 75 | LICENSE 76 | README.md 77 | index.js 78 | package.json 79 | TargetFolder: 'package' 80 | 81 | # Publish 82 | - task: PublishBuildArtifacts@1 83 | displayName: 'Publish Artifact: MongoDbStorage' 84 | inputs: 85 | PathtoPublish: package 86 | ArtifactName: MongoDbStoragePackage 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/MongoDbStorage.ts: -------------------------------------------------------------------------------- 1 | import { Storage, StoreItems } from 'botbuilder'; 2 | import { Collection, ObjectId, MongoClient, BulkWriteResult } from 'mongodb'; 3 | import { MongoDbStorageError } from './MongoDbStorageError'; 4 | import { MongoDocumentStoreItem } from './MongoDocumentStoreItem'; 5 | 6 | export class MongoDbStorage implements Storage { 7 | static readonly DEFAULT_COLLECTION_NAME: string = "BotFrameworkState"; 8 | static readonly DEFAULT_DB_NAME: string = "BotFramework"; 9 | 10 | protected readonly targetCollection: Collection; 11 | 12 | constructor(collection: Collection) { 13 | this.targetCollection = collection; 14 | } 15 | 16 | public async read(stateKeys: string[]): Promise { 17 | if (!stateKeys || stateKeys.length == 0) { 18 | return {}; 19 | } 20 | 21 | const docs = await (await this.targetCollection.find(MongoDbStorage.createQuery(stateKeys))).toArray(); 22 | 23 | const storeItems: StoreItems = MongoDbStorage.packStoreItems(docs); 24 | 25 | return storeItems; 26 | } 27 | 28 | public async write(changes: StoreItems): Promise { 29 | if (!changes || Object.keys(changes).length === 0) { 30 | return; 31 | } 32 | 33 | const operations = MongoDbStorage.createBulkOperations(changes); 34 | 35 | await this.targetCollection.bulkWrite(operations); 36 | } 37 | 38 | public async delete(keys: string[]): Promise { 39 | if (!keys || keys.length == 0) { 40 | return; 41 | } 42 | await this.targetCollection.deleteMany(MongoDbStorage.createQuery(keys)); 43 | } 44 | 45 | public static packStoreItems(items: MongoDocumentStoreItem[]): StoreItems { 46 | return items.reduce((accum, item) => { 47 | accum[item._id] = item.state; 48 | return accum; 49 | }, {}); 50 | } 51 | 52 | public static createQuery(stateKeys: string[]) { 53 | return { _id: { $in: stateKeys } }; 54 | } 55 | 56 | public static createBulkOperations(changes: StoreItems) { 57 | const operations = []; 58 | 59 | Object.keys(changes).forEach(key => { 60 | const state = changes[key]; 61 | const shouldSlam = MongoDbStorage.shouldSlam(state.eTag); 62 | const oldETag = state.eTag; 63 | state.eTag = new ObjectId().toHexString(); 64 | operations.push({ 65 | updateOne: { 66 | filter: MongoDbStorage.createFilter(key, oldETag), 67 | update: { 68 | $set: { 69 | state: state 70 | }, 71 | $currentDate: { 72 | dt: { $type: 'date' } 73 | } 74 | }, 75 | upsert: shouldSlam 76 | } 77 | }); 78 | }); 79 | return operations; 80 | } 81 | 82 | 83 | public static shouldSlam(etag: any) { 84 | return (etag === '*' || !etag); 85 | } 86 | 87 | public static createFilter(key: string, etag: any) { 88 | if (this.shouldSlam(etag)) { 89 | return { _id: key }; 90 | } 91 | return { _id: key, 'state.eTag': etag }; 92 | } 93 | 94 | public static getCollection(client: MongoClient, dbName: string = MongoDbStorage.DEFAULT_DB_NAME, collectionName: string = MongoDbStorage.DEFAULT_COLLECTION_NAME): Collection { 95 | return client.db(dbName).collection(collectionName) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/MongoDbStorageError.ts: -------------------------------------------------------------------------------- 1 | export class MongoDbStorageError extends Error { 2 | public static readonly NO_CONFIG_ERROR: MongoDbStorageError = new MongoDbStorageError('MongoDbStorageConfig is required.'); 3 | public static readonly NO_URL_ERROR: MongoDbStorageError = new MongoDbStorageError('MongoDbStorageConfig.url is required.'); 4 | } 5 | -------------------------------------------------------------------------------- /src/MongoDocumentStoreItem.ts: -------------------------------------------------------------------------------- 1 | export interface MongoDocumentStoreItem { 2 | _id: string; 3 | state: any; 4 | } 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MongoDbStorage'; 2 | 3 | -------------------------------------------------------------------------------- /test/integration/async-dump.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {createHook} = require('async_hooks'); 4 | const {stackTraceFilter} = require('mocha/lib/utils'); 5 | const allResources = new Map(); 6 | 7 | // this will pull Mocha internals out of the stacks 8 | const filterStack = stackTraceFilter(); 9 | 10 | const hook = createHook({ 11 | init(asyncId, type, triggerAsyncId) { 12 | allResources.set(asyncId, {type, triggerAsyncId, stack: (new Error()).stack}); 13 | }, 14 | destroy(asyncId) { 15 | allResources.delete(asyncId); 16 | } 17 | }).enable(); 18 | 19 | global.asyncDump = module.exports = () => { 20 | hook.disable(); 21 | console.error(` 22 | STUFF STILL IN THE EVENT LOOP:`) 23 | allResources.forEach(value=> { 24 | console.error(`Type: ${value.type}`); 25 | console.error(filterStack(value.stack)); 26 | console.error('\n'); 27 | }); 28 | }; -------------------------------------------------------------------------------- /test/integration/mocha.opts: -------------------------------------------------------------------------------- 1 | --require source-map-support/register 2 | --require ./test/integration/async-dump 3 | --recursive 4 | **/*.integration.test.js -------------------------------------------------------------------------------- /test/integration/mongoDbStorage.integration.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const request = require('request'); 3 | 4 | const { 5 | MongoDbStorage, 6 | } = require('../../lib/MongoDbStorage'); 7 | 8 | //require MongoClient to set up fakes and stubs, not actual database connectivity 9 | const { 10 | ObjectId, MongoClient 11 | } = require('mongodb'); 12 | 13 | const settings = { 14 | url: "mongodb://127.0.0.1:27017/", 15 | database: "botFrameworkStorage_TestDB", 16 | collection: "botFrameworkStorage_TestCollection" 17 | }; 18 | 19 | describe('mongoDbStorage integration tests', async function () { 20 | let storage; 21 | let client; 22 | 23 | beforeEach(async function () { 24 | client = new MongoClient(settings.url, { useUnifiedTopology: true }); 25 | await client.connect(); 26 | const collection = client.db(settings.database).collection(settings.collection); 27 | storage = new MongoDbStorage(collection); 28 | }); 29 | 30 | afterEach(async function () { 31 | await client.close(); 32 | }) 33 | 34 | function uniqueChange() { 35 | return { 36 | [new ObjectId().toHexString()]: { 37 | field1: Math.random() * 1000000 38 | } 39 | }; 40 | }; 41 | 42 | function idOf(changesObject) { 43 | return Object.keys(changesObject)[0]; 44 | } 45 | 46 | function contentOf(changesObject) { 47 | return changesObject[idOf(changesObject)]; 48 | } 49 | 50 | describe('write', async function () { 51 | it('should create a document', async function () { 52 | 53 | const subject = uniqueChange(); 54 | await storage.write(subject); 55 | 56 | const actual = await storage.targetCollection.findOne({ 57 | _id: idOf(subject) 58 | }); 59 | 60 | assert.equal(actual.state.field1, contentOf(subject).field1); 61 | 62 | }); 63 | }); 64 | 65 | 66 | describe('read', function () { 67 | it('should return empty for non existent key', async function () { 68 | 69 | let actual = await storage.read(['__non_existent_key__']); 70 | 71 | assert.deepEqual(actual, {}, `unexpected non-empty object`); 72 | }); 73 | 74 | it('should return existing document', async function () { 75 | 76 | const subject = uniqueChange(); 77 | await storage.write(subject); 78 | 79 | const actual = await storage.read([idOf(subject)]); 80 | 81 | assert.equal(idOf(actual), idOf(subject)); 82 | 83 | }); 84 | 85 | }); 86 | 87 | 88 | describe('delete', function (done) { 89 | it('should remove an existing document', async function () { 90 | 91 | const subject = uniqueChange(); 92 | const testId = idOf(subject); 93 | await storage.write(subject); 94 | 95 | await storage.delete([testId]); 96 | 97 | const actual = await storage.targetCollection.findOne({ 98 | _id: testId 99 | }); 100 | 101 | assert.equal(actual, null, `Unexpected document found which should have been deleted. _id ${testId}`); 102 | 103 | }); 104 | }); 105 | 106 | }); 107 | -------------------------------------------------------------------------------- /test/unit/mocha.opts: -------------------------------------------------------------------------------- 1 | --require source-map-support/register 2 | --recursive 3 | **/*.test.js -------------------------------------------------------------------------------- /test/unit/mongoDbStorage.unit.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { MongoDbStorage } = require('../../lib/MongoDbStorage'); 3 | const { MongoDbStorageError } = require('../../lib/MongoDbStorageError'); 4 | const { MongoDocumentStoreItem } = require('../../lib/MongoDocumentStoreItem'); 5 | const sinon = require('sinon'); 6 | 7 | //require MongoClient to set up fakes and stubs, not actual database connectivity 8 | const { 9 | MongoClient 10 | } = require('mongodb'); 11 | 12 | 13 | const getSettings = () => ({ 14 | url: "mongodb://localhost:27017/", 15 | database: "botframework", 16 | collection: "botframeworkstate" 17 | }); 18 | 19 | describe('MongoDbStorage ', function () { 20 | describe('input verification', function () { 21 | it('should return empty object when null is passed in to read()', async function () { 22 | const storage = new MongoDbStorage(getSettings()); 23 | const storeItems = await storage.read(null); 24 | assert.deepEqual(storeItems, {}, `did not receive empty object, instead received ${JSON.stringify(storeItems)}`); 25 | }); 26 | 27 | it('should return empty object when no keys are passed in to read()', async function () { 28 | const storage = new MongoDbStorage(getSettings()); 29 | const storeItems = await storage.read([]); 30 | assert.deepEqual(storeItems, {}, `did not receive empty object, instead received ${JSON.stringify(storeItems)}`); 31 | }); 32 | 33 | it('should not blow up when no changes are passed in to write()', async function () { 34 | const storage = new MongoDbStorage(getSettings()); 35 | const storeItems = await storage.write({}); 36 | }); 37 | 38 | it('should not blow up when null is passed in to write()', async function () { 39 | const storage = new MongoDbStorage(getSettings()); 40 | const storeItems = await storage.write(null); 41 | }); 42 | 43 | it('should not blow up when no keys are passed in to delete()', async function () { 44 | const storage = new MongoDbStorage(getSettings()); 45 | const storeItems = await storage.delete([]); 46 | }); 47 | 48 | it('should not blow up when null is passed in to delete()', async function () { 49 | const storage = new MongoDbStorage(getSettings()); 50 | const storeItems = await storage.delete(null); 51 | }); 52 | }); 53 | 54 | describe('createFilter', function () { 55 | it('omits etag if etag is *', async function () { 56 | const actual = MongoDbStorage.createFilter('k', '*'); 57 | assert.deepEqual(actual, { 58 | _id: 'k' 59 | }); 60 | }); 61 | 62 | it('omits etag if etag is null', async function () { 63 | const actual = MongoDbStorage.createFilter('k', null); 64 | assert.deepEqual(actual, { 65 | _id: 'k' 66 | }); 67 | }); 68 | 69 | it('includes etag if etag is valid', async function () { 70 | const actual = MongoDbStorage.createFilter('k', '0xff44'); 71 | assert.deepEqual(actual, { 72 | _id: 'k', 73 | 'state.eTag': '0xff44' 74 | }); 75 | }); 76 | }); 77 | 78 | describe('shouldSlam', function () { 79 | it('returns truthy if etag is *', async function () { 80 | const actual = MongoDbStorage.shouldSlam('*'); 81 | assert.ok(actual); 82 | }); 83 | 84 | it('returns truthy if etag is omitted', async function () { 85 | const actual = MongoDbStorage.shouldSlam(); 86 | assert.ok(actual); 87 | }); 88 | 89 | it('returns falsy if etag exists', async function () { 90 | const actual = MongoDbStorage.shouldSlam("a_fake_etag"); 91 | assert.ok(actual === false); 92 | }); 93 | }); 94 | 95 | describe('createQuery', function () { 96 | it('composes query with items in array', function () { 97 | const actual = MongoDbStorage.createQuery(['a', 'b']) 98 | assert.deepStrictEqual(actual, { _id: { '$in': ['a', 'b'] } }) 99 | 100 | }) 101 | }) 102 | 103 | describe('packStorageItems', function () { 104 | it('should return storeItems as a dictionary', function () { 105 | //arrange 106 | const subject = [{ 107 | _id: 'abc', 108 | state: 'some_state' 109 | }, 110 | { 111 | _id: '123', 112 | state: { 113 | foo: 'bar' 114 | } 115 | } 116 | ]; 117 | 118 | const expected = { 119 | 'abc': 'some_state', 120 | '123': { 121 | foo: 'bar' 122 | } 123 | }; 124 | //act 125 | 126 | let storeItems = MongoDbStorage.packStoreItems(subject); 127 | 128 | //assert 129 | assert.deepEqual(storeItems, expected); 130 | }); 131 | }); 132 | 133 | describe('read', async function (done) { 134 | it('should return storeItems as a dictionary', async function () { 135 | //arrange 136 | 137 | 138 | const collection = { 139 | find: function (q) { 140 | return { 141 | toArray: function () { 142 | return [{ 143 | _id: 'abc', 144 | state: 'some_state' 145 | }, 146 | { 147 | _id: '123', 148 | state: { 149 | foo: 'bar' 150 | } 151 | }, 152 | { 153 | _id: '456', 154 | state: 1234 155 | } 156 | ]; 157 | } 158 | }; 159 | } 160 | }; 161 | 162 | const storage = new MongoDbStorage(collection); 163 | 164 | const keys = ['abc', '123', '456']; 165 | 166 | const expected = { 167 | 'abc': 'some_state', 168 | '123': { 169 | foo: 'bar' 170 | }, 171 | '456': 1234 172 | }; 173 | 174 | //act 175 | let storeItems = await storage.read(keys); 176 | 177 | //assert 178 | assert.deepEqual(storeItems, expected); 179 | }); 180 | }); 181 | 182 | describe('write', async function (done) { 183 | it('calls bulkWrite', async function () { 184 | //arrange 185 | const collection = { bulkWrite: sinon.fake() } 186 | const target = new MongoDbStorage(collection) 187 | //act 188 | await target.write({ 189 | 'key_one': { 190 | item: "foo", 191 | eTag: "*" 192 | } 193 | }); 194 | //assert 195 | assert.ok(collection.bulkWrite.called); 196 | }) 197 | }) 198 | describe('createBulkOperations', function () { 199 | it('creates options with updateOne for each key in changes object.', function () { 200 | //arrange 201 | const changes = { 202 | 'key_one': { 203 | item: "foo", 204 | eTag: "*" 205 | }, 206 | 'key_two': { 207 | item: "foo", 208 | eTag: "*" 209 | } 210 | }; 211 | 212 | const actual = MongoDbStorage.createBulkOperations(changes); 213 | 214 | //assert 215 | assert.equal(actual[0].updateOne.filter._id, "key_one"); 216 | assert.equal(actual[1].updateOne.filter._id, "key_two"); 217 | }); 218 | 219 | it('sets upsert property to true if shouldSlam.', async function () { 220 | //arrange 221 | 222 | const changes = { 223 | 'key_one': { 224 | item: "foo", 225 | eTag: "*" 226 | } 227 | }; 228 | 229 | //act 230 | const actual = MongoDbStorage.createBulkOperations(changes); 231 | 232 | //assert 233 | assert.ok(actual[0].updateOne.upsert); 234 | }); 235 | 236 | it('sets upsert property to false if !shouldSlam.', async function () { 237 | //arrange 238 | const changes = { 239 | 'key_one': { 240 | item: "foo", 241 | eTag: "123456" 242 | } 243 | }; 244 | 245 | //act 246 | const actual = MongoDbStorage.createBulkOperations(changes); 247 | 248 | //assert 249 | assert.ok(actual[0].updateOne.upsert == false); 250 | }); 251 | 252 | it('creates $set property with item value', async function () { 253 | //arrange 254 | const changes = { 255 | 'key_one': { 256 | item: "foo", 257 | eTag: "*" 258 | } 259 | }; 260 | 261 | //act 262 | const actual = MongoDbStorage.createBulkOperations(changes); 263 | 264 | //assert 265 | assert.deepEqual(actual[0].updateOne.update.$set.state.item, changes.key_one.item); 266 | }); 267 | 268 | it('updates dt field to current date', async function () { 269 | //arrange 270 | const changes = { 271 | 'key_one': { 272 | item: "foo", 273 | eTag: "*" 274 | } 275 | }; 276 | 277 | //act 278 | const actual = MongoDbStorage.createBulkOperations(changes); 279 | 280 | //assert 281 | assert.deepStrictEqual(actual[0].updateOne.update.$currentDate, {dt: {$type: 'date'}}); 282 | }); 283 | }); 284 | 285 | describe('delete', async function (done) { 286 | it('calls delete with keys query', async function () { 287 | //arrange 288 | let query = null; 289 | const storage = new MongoDbStorage({ deleteMany: function (q) { query = q } }); 290 | const keys = ['123', 'aaa', 'bbb']; 291 | 292 | //act 293 | await storage.delete(keys); 294 | 295 | //assert 296 | assert.deepStrictEqual(query, { 297 | _id: { 298 | $in: keys 299 | } 300 | }); 301 | }); 302 | }); 303 | 304 | describe('getCollection', function(){ 305 | it('uses default DB and Collection names', function(){ 306 | //arrange 307 | collectionFake = sinon.fake(); 308 | dbFake = sinon.fake.returns({collection: collectionFake}); 309 | 310 | //act 311 | MongoDbStorage.getCollection({db: dbFake}); 312 | 313 | //assert 314 | assert.ok(dbFake.calledWith(MongoDbStorage.DEFAULT_DB_NAME)); 315 | assert.ok(collectionFake.calledWith(MongoDbStorage.DEFAULT_COLLECTION_NAME)); 316 | }) 317 | 318 | it('overrides default DB and Collection names when supplied', function(){ 319 | //arrange 320 | collectionFake = sinon.fake(); 321 | dbFake = sinon.fake.returns({collection: collectionFake}); 322 | 323 | //act 324 | MongoDbStorage.getCollection({db: dbFake},dbName='db1', collectionName = 'collection1'); 325 | 326 | //assert 327 | assert.ok(dbFake.calledWith('db1')); 328 | assert.ok(collectionFake.calledWith('collection1')); 329 | }) 330 | }) 331 | 332 | }); 333 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "outDir": "./lib", 8 | "rootDir": "./src", 9 | "types" : ["node"] 10 | }, 11 | "include": [ 12 | "src/**/*" 13 | ] 14 | } --------------------------------------------------------------------------------