Repository: Yoctol/bottender
Pull request #633 (2020-01-17 11:03:03)

feat: add getSessionStore api
Added following API to get the session store that configured by `bottender.config.js`

```js
const { getSessionStore } = require('bottender');

const sessionStore = getSessionStore();
```

codecov-io: # [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/633?src=pr&el=h1) Report
> Merging [#633](https://codecov.io/gh/Yoctol/bottender/pull/633?src=pr&el=desc) into [release/1.2](https://codecov.io/gh/Yoctol/bottender/commit/562661c3404c766c089d59612f8dc26597070f74?src=pr&el=desc) will **increase** coverage by `0.42%`.
> The diff coverage is `65.78%`.

[![Impacted file tree graph](https://codecov.io/gh/Yoctol/bottender/pull/633/graphs/tree.svg?width=650&token=JX3EGtxYH3&height=150&src=pr)](https://codecov.io/gh/Yoctol/bottender/pull/633?src=pr&el=tree)

```diff
@@               Coverage Diff               @@
##           release/1.2     #633      +/-   ##
===============================================
+ Coverage        80.66%   81.08%   +0.42%     
===============================================
  Files               99      100       +1     
  Lines             4287     4293       +6     
  Branches          1061     1077      +16     
===============================================
+ Hits              3458     3481      +23     
+ Misses             789      764      -25     
- Partials            40       48       +8
```


| [Impacted Files](https://codecov.io/gh/Yoctol/bottender/pull/633?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/bottender/src/types.ts](https://codecov.io/gh/Yoctol/bottender/pull/633/diff?src=pr&el=tree#diff-cGFja2FnZXMvYm90dGVuZGVyL3NyYy90eXBlcy50cw==) | `100% <ø> (ø)` | :arrow_up: |
| [packages/bottender/src/index.ts](https://codecov.io/gh/Yoctol/bottender/pull/633/diff?src=pr&el=tree#diff-cGFja2FnZXMvYm90dGVuZGVyL3NyYy9pbmRleC50cw==) | `100% <100%> (ø)` | :arrow_up: |
| [...ackages/bottender/src/session/MongoSessionStore.ts](https://codecov.io/gh/Yoctol/bottender/pull/633/diff?src=pr&el=tree#diff-cGFja2FnZXMvYm90dGVuZGVyL3NyYy9zZXNzaW9uL01vbmdvU2Vzc2lvblN0b3JlLnRz) | `97.29% <100%> (ø)` | :arrow_up: |
| [packages/bottender/src/server/Server.ts](https://codecov.io/gh/Yoctol/bottender/pull/633/diff?src=pr&el=tree#diff-cGFja2FnZXMvYm90dGVuZGVyL3NyYy9zZXJ2ZXIvU2VydmVyLnRz) | `10.25% <33.33%> (+1.56%)` | :arrow_up: |
| [packages/bottender/src/initializeServer.ts](https://codecov.io/gh/Yoctol/bottender/pull/633/diff?src=pr&el=tree#diff-cGFja2FnZXMvYm90dGVuZGVyL3NyYy9pbml0aWFsaXplU2VydmVyLnRz) | `27.27% <33.33%> (+7.27%)` | :arrow_up: |
| [packages/bottender/src/getSessionStore.ts](https://codecov.io/gh/Yoctol/bottender/pull/633/diff?src=pr&el=tree#diff-cGFja2FnZXMvYm90dGVuZGVyL3NyYy9nZXRTZXNzaW9uU3RvcmUudHM=) | `70% <70%> (ø)` | |
| [packages/create-bottender-app/src/shared/log.ts](https://codecov.io/gh/Yoctol/bottender/pull/633/diff?src=pr&el=tree#diff-cGFja2FnZXMvY3JlYXRlLWJvdHRlbmRlci1hcHAvc3JjL3NoYXJlZC9sb2cudHM=) | `0% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Yoctol/bottender/pull/633?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Yoctol/bottender/pull/633?src=pr&el=footer). Last update [562661c...973034a](https://codecov.io/gh/Yoctol/bottender/pull/633?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/packages/bottender/src/getSessionStore.ts b/packages/bottender/src/getSessionStore.ts
new file mode 100644
index 00000000..8a9bd481
--- /dev/null
+++ b/packages/bottender/src/getSessionStore.ts
@@ -0,0 +1,64 @@
+import warning from 'warning';
+
+import getBottenderConfig from './shared/getBottenderConfig';
+
+function getSessionStore() {
+  const bottenderConfig = getBottenderConfig();
+
+  const { session } = bottenderConfig;
+
+  const sessionDriver = (session && session.driver) || 'memory';
+
+  const storesConfig = (session && session.stores) || {};
+
+  switch (sessionDriver) {
+    case 'memory': {
+      const MemorySessionStore = require('./session/MemorySessionStore')
+        .default;
+
+      return new MemorySessionStore(
+        storesConfig.memory || {},
+        session && session.expiresIn
+      );
+    }
+    case 'file': {
+      const FileSessionStore = require('./session/FileSessionStore').default;
+
+      return new FileSessionStore(
+        storesConfig.file || {},
+        session && session.expiresIn
+      );
+    }
+    case 'mongo': {
+      const MongoSessionStore = require('./session/MongoSessionStore').default;
+
+      return new MongoSessionStore(
+        storesConfig.mongo || {},
+        session && session.expiresIn
+      );
+    }
+    case 'redis': {
+      const RedisSessionStore = require('./session/RedisSessionStore').default;
+
+      return new RedisSessionStore(
+        storesConfig.redis || {},
+        session && session.expiresIn
+      );
+    }
+    default: {
+      warning(
+        false,
+        `Received unknown driver: ${sessionDriver}, so fallback it to \`memory\` driver.`
+      );
+      const MemorySessionStore = require('./session/MemorySessionStore')
+        .default;
+
+      return new MemorySessionStore(
+        storesConfig.memory || {},
+        session && session.expiresIn
+      );
+    }
+  }
+}
+
+export default getSessionStore;
diff --git a/packages/bottender/src/index.ts b/packages/bottender/src/index.ts
index 7a3a1bc7..d01cfea4 100644
--- a/packages/bottender/src/index.ts
+++ b/packages/bottender/src/index.ts
@@ -5,6 +5,7 @@ export { bottender };
 /* Core */
 export { default as Bot } from './bot/Bot';
 export { default as Context } from './context/Context';
+export { default as getSessionStore } from './getSessionStore';
 
 /* Action */
 export { default as chain } from './chain';
diff --git a/packages/bottender/src/initializeServer.ts b/packages/bottender/src/initializeServer.ts
index 234464df..8e72da37 100644
--- a/packages/bottender/src/initializeServer.ts
+++ b/packages/bottender/src/initializeServer.ts
@@ -11,6 +11,7 @@ import SlackBot from './slack/SlackBot';
 import TelegramBot from './telegram/TelegramBot';
 import ViberBot from './viber/ViberBot';
 import getBottenderConfig from './shared/getBottenderConfig';
+import getSessionStore from './getSessionStore';
 import { Action, BottenderConfig, Channel, Plugin } from './types';
 
 const BOT_MAP = {
@@ -30,35 +31,9 @@ function initializeServer({
 } = {}): express.Application | void {
   const bottenderConfig = getBottenderConfig();
 
-  const { initialState, plugins, session, channels } = merge(
-    bottenderConfig,
-    config
-  );
-
-  // session
-  const sessionDriver = (session && session.driver) || 'memory';
-  let SessionStore;
-  switch (sessionDriver) {
-    case 'file':
-      SessionStore = require('./session/FileSessionStore').default;
-      break;
-    case 'mongo':
-      SessionStore = require('./session/MongoSessionStore').default;
-      break;
-    case 'redis':
-      SessionStore = require('./session/RedisSessionStore').default;
-      break;
-    default:
-      SessionStore = require('./session/MemorySessionStore').default;
-  }
-
-  const sessionDriverConfig =
-    ((session && session.stores) || {})[sessionDriver] || {};
+  const { initialState, plugins, channels } = merge(bottenderConfig, config);
 
-  const sessionStore = new SessionStore(
-    sessionDriverConfig,
-    session && session.expiresIn
-  );
+  const sessionStore = getSessionStore();
 
   // TODO: refine handler entry, improve error message and hint
   // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires
diff --git a/packages/bottender/src/server/Server.ts b/packages/bottender/src/server/Server.ts
index 60731a5a..ec665b02 100644
--- a/packages/bottender/src/server/Server.ts
+++ b/packages/bottender/src/server/Server.ts
@@ -8,47 +8,14 @@ import { pascalcase } from 'messaging-api-common';
 
 import Bot from '../bot/Bot';
 import getBottenderConfig from '../shared/getBottenderConfig';
-import {
-  Action,
-  BottenderConfig,
-  Plugin,
-  SessionConfig,
-  SessionDriver,
-} from '../types';
+import getSessionStore from '../getSessionStore';
+import { Action, BottenderConfig, Plugin } from '../types';
 
 export type ServerOptions = {
   useConsole?: boolean;
   dev?: boolean;
 };
 
-function createSessionStore(
-  sessionConfig: SessionConfig = { driver: SessionDriver.Memory, stores: {} }
-) {
-  const sessionDriver = (sessionConfig && sessionConfig.driver) || 'memory';
-  let SessionStore;
-  switch (sessionDriver) {
-    case 'file':
-      SessionStore = require('../session/FileSessionStore').default;
-      break;
-    case 'mongo':
-      SessionStore = require('../session/MongoSessionStore').default;
-      break;
-    case 'redis':
-      SessionStore = require('../session/RedisSessionStore').default;
-      break;
-    default:
-      SessionStore = require('../session/MemorySessionStore').default;
-  }
-
-  const sessionDriverConfig =
-    ((sessionConfig && sessionConfig.stores) || {})[sessionDriver] || {};
-
-  return new SessionStore(
-    sessionDriverConfig,
-    sessionConfig && sessionConfig.expiresIn
-  );
-}
-
 class Server {
   _channelBots: { webhookPath: string; bot: Bot<any, any, any> }[] = [];
 
@@ -73,11 +40,11 @@ class Server {
   public async prepare(): Promise<void> {
     const bottenderConfig = getBottenderConfig();
 
-    const { initialState, plugins, session, channels } = merge(
+    const { initialState, plugins, channels } = merge(
       bottenderConfig /* , config */
     ) as BottenderConfig;
 
-    const sessionStore = createSessionStore(session);
+    const sessionStore = getSessionStore();
 
     // TODO: refine handler entry, improve error message and hint
     // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires
diff --git a/packages/bottender/src/session/MongoSessionStore.ts b/packages/bottender/src/session/MongoSessionStore.ts
index f12bd6be..939637e6 100644
--- a/packages/bottender/src/session/MongoSessionStore.ts
+++ b/packages/bottender/src/session/MongoSessionStore.ts
@@ -8,7 +8,7 @@ import SessionStore from './SessionStore';
 type MongoOption =
   | string
   | {
-      url: string;
+      url?: string;
       collectionName?: string;
     };
 
@@ -27,7 +27,7 @@ export default class MongoSessionStore implements SessionStore {
       this._url = options;
       this._collectionName = 'sessions';
     } else {
-      this._url = options.url;
+      this._url = options.url || 'mongodb://localhost:27017';
       this._collectionName = options.collectionName || 'sessions';
     }
     this._expiresIn = expiresIn || 0;
diff --git a/packages/bottender/src/types.ts b/packages/bottender/src/types.ts
index 2f8852a7..3f4cc2b0 100644
--- a/packages/bottender/src/types.ts
+++ b/packages/bottender/src/types.ts
@@ -86,10 +86,10 @@ export type SessionConfig = {
       port?: number;
       host?: string;
       password?: string;
-      db: number;
+      db?: number;
     };
     mongo?: {
-      url: string;
+      url?: string;
       collectionName?: string;
     };
   };


Test patch:
diff --git a/packages/bottender/src/__tests__/getSessionStore.spec.ts b/packages/bottender/src/__tests__/getSessionStore.spec.ts
new file mode 100644
index 00000000..3d28b3e3
--- /dev/null
+++ b/packages/bottender/src/__tests__/getSessionStore.spec.ts
@@ -0,0 +1,74 @@
+import getBottenderConfig from '../shared/getBottenderConfig';
+import getSessionStore from '../getSessionStore';
+import { SessionDriver } from '../types';
+
+jest.mock('../shared/getBottenderConfig');
+
+const getBottenderConfigMocked = getBottenderConfig as jest.MockedFunction<
+  typeof getBottenderConfig
+>;
+
+it('should get MemorySessionStore by default', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({});
+  expect(getSessionStore().constructor.name).toEqual('MemorySessionStore');
+});
+
+it('should get MemorySessionStore when assigning memory as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.Memory,
+      stores: {
+        memory: {
+          maxSize: 500,
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('MemorySessionStore');
+});
+
+it('should get FileSessionStore when assigning file as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.File,
+      stores: {
+        file: {
+          dirname: '.sessions',
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('FileSessionStore');
+});
+
+it('should get MongoSessionStore when assigning mongo as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.Mongo,
+      stores: {
+        mongo: {
+          url: 'mongodb://localhost:27017',
+          collectionName: 'sessions',
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('MongoSessionStore');
+});
+
+it('should get RedisSessionStore when assigning redis as driver', () => {
+  getBottenderConfigMocked.mockReturnValueOnce({
+    session: {
+      driver: SessionDriver.Redis,
+      stores: {
+        redis: {
+          port: 6379,
+          host: '127.0.0.1',
+          password: 'auth',
+          db: 0,
+        },
+      },
+    },
+  });
+  expect(getSessionStore().constructor.name).toEqual('RedisSessionStore');
+});
diff --git a/packages/bottender/src/__tests__/index.spec.ts b/packages/bottender/src/__tests__/index.spec.ts
index 9b726597..d46461d5 100644
--- a/packages/bottender/src/__tests__/index.spec.ts
+++ b/packages/bottender/src/__tests__/index.spec.ts
@@ -51,6 +51,10 @@ describe('core', () => {
     expect(core.createServer).toBeDefined();
   });
 
+  it('export getSessionStore', () => {
+    expect(core.getSessionStore).toBeDefined();
+  });
+
   it('export chain', () => {
     expect(core.chain).toBeDefined();
   });