Skip to content

Commit

Permalink
v0.0.6 (#20)
Browse files Browse the repository at this point in the history
* Improved detection of empty session data

* ✅ Improved tests (better coverage)

* ✅ Added new tests (better coverage)
* Tests for `format.serialize` and `format.deserialize` functions of lowdb storage
* Test for `isPromise()` function
* Exporting `isPromise()` function now

* 🔧 Ignoring file `sessions.json`, which comes after running tests

* ⬆️ Upgrading dependencies

* Added .npmrc (disable package-lock.json)

* 📝 Improve documentation & JSDoc config  [ci skip]

* 💥 Default storage type: storageFileAsync -> storageFileSync, see #16

* 👌 Possible fix for #16 and #13

* 🔧 Move ESLint config from package.json to .eslintrc [ci skip]

* Fixing tests (TravisCI failing builds for Node 6)
  • Loading branch information
TemaSM committed Aug 28, 2018
1 parent 8f32b4a commit 21bd956
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 54 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
docs/
19 changes: 19 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"env": {
"es6": true,
"node": true,
"mocha": true
},
"extends": "standard",
"plugins": ["mocha"],
"parserOptions": {
"ecmaVersion": 2016
},
"rules": {
"one-var": 0,
"comma-dangle": 0,
"new-cap": 0,
"padded-blocks": 0,
"brace-style": [0, "1tbs", { "allowSingleLine": true }]
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ coverage

# Databases
*_db.json
sessions.json

# Created by https://www.gitignore.io/api/node,linux,macos,windows,visualstudiocode

Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,20 @@
"DEBUG": "telegraf:*"
},
"console": "integratedTerminal"
},
{
"type": "node",
"request": "launch",
"name": "Tests",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"test"
],
"console": "integratedTerminal",
"env": {
"DEBUG": "telegraf:session-local:test"
},
"port": 9229
}]
}
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ const localSession = new LocalSession({
database: 'example_db.json',
// Name of session property object in Telegraf Context (default: 'session')
property: 'session',
// Type of lowdb storage (default: 'storagefileAsync')
storage: LocalSession.storagefileAsync,
// Type of lowdb storage (default: 'storageFileSync')
storage: LocalSession.storageFileAsync,
// Format of storage/database (default: JSON.stringify / JSON.parse)
format: {
serialize: (obj) => JSON.stringify(obj, null, 2), // null & 2 for pretty-formatted JSON
Expand All @@ -86,6 +86,13 @@ const localSession = new LocalSession({
state: { messages: [] }
})

// Wait for database async initialization finished (storageFileAsync or your own asynchronous storage adapter)
localSession.DB.then(DB => {
// Database now initialized, so now you can retrieve anything you want from it
console.log('Current LocalSession DB:', DB.value())
// console.log(DB.get('sessions').getById('1:1').value())
})

// Telegraf will use `telegraf-session-local` configured above middleware with overrided `property` name
Bot.use(localSession.middleware(property))

Expand Down
11 changes: 9 additions & 2 deletions examples/extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const localSession = new LocalSession({
database: 'example_db.json',
// Name of session property object in Telegraf Context (default: 'session')
property: 'session',
// Type of lowdb storage (default: 'storagefileAsync')
storage: LocalSession.storagefileAsync,
// Type of lowdb storage (default: 'storageFileSync')
storage: LocalSession.storageFileAsync,
// Format of storage/database (default: JSON.stringify / JSON.parse)
format: {
serialize: (obj) => JSON.stringify(obj, null, 2), // null & 2 for pretty-formatted JSON
Expand All @@ -23,6 +23,13 @@ const localSession = new LocalSession({
state: { messages: [] }
})

// Wait for database async initialization finished (storageFileAsync or your own asynchronous storage adapter)
localSession.DB.then(DB => {
// Database now initialized, so now you can retrieve anything you want from it
console.log('Current LocalSession DB:', DB.value())
// console.log(DB.get('sessions').getById('1:1').value())
})

// Telegraf will use `telegraf-session-local` configured above middleware with overrided `property` name
Bot.use(localSession.middleware(property))

Expand Down
36 changes: 34 additions & 2 deletions jsdoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,39 @@
}
},
"docdash": {
"static": false,
"sort": false
"static": true,
"sort": true,
"sectionOrder": [
"Modules",
"Namespaces",
"Classes",
"Externals",
"Events",
"Mixins",
"Tutorials",
"Interfaces"
],
"search": true,
"collapse": false,
"typedefs": true,
"meta": {
"title": "Telegraf Session local Documentation",
"keyword": "Telegram Telegraf Session Local Database JSON XML",
"description": "Documentation for Telegraf Session local"
},
"menu": {
"GitHub Repository ⧉": {
"href": "https://github.com/RealSpeaker/telegraf-session-local",
"target": "_blank",
"class": "menu-item",
"id": "link_repo"
},
"NPM Package ⧉": {
"href": "https://www.npmjs.com/package/telegraf-session-local",
"target": "_blank",
"class": "menu-item",
"id": "link_npm"
}
}
}
}
30 changes: 14 additions & 16 deletions lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const
* @param {String} [options.property] - Name of property in {@link https://telegraf.js.org/#/?id=context|Telegraf Context} where session object will be located `(default: 'session')`
* @param {Object} [options.state] - Initial state of database. You can use it to pre-init database Arrays/Objects to store your own data `(default: {})`
* @param {Function} [options.getSessionKey] - Function to get identifier for session from {@link https://telegraf.js.org/#/?id=context|Telegraf Context} (may implement it on your own)
* @param {Object} [options.storage] - lowdb storage option for implementing your own storage read/write operations `(default: {@link LocalSession.storageFileAsync|LocalSession.storageFileAsync})`
* @param {Object} [options.storage] - lowdb storage option for implementing your own storage read/write operations `(default: {@link LocalSession.storageFileSync|LocalSession.storageFileSync})`
* @param {Function} [options.storage.read] - lowdb storage read function, must return an object or a Promise
* @param {Function} [options.storage.write] - lowdb storage write function, must return undefined or a Promise
* @param {Object} [options.format] - lowdb storage format option for implementing your own database format for read/write operations
Expand All @@ -24,7 +24,8 @@ const
class LocalSession {
constructor (options = {}) {
this.options = Object.assign({
storage: LocalSession.storageFileAsync,
// TODO: Use storageFileAsync as default with support of Promise or Promise-like initialization, see: https://git.io/fA3ZN
storage: LocalSession.storageFileSync,
database: 'sessions.json',
property: 'session',
state: { },
Expand Down Expand Up @@ -54,12 +55,10 @@ class LocalSession {
if (this.options.storage === LocalSession.storageMemory) {
debug('Initiating: lowdb adapter: storageMemory')
this._adapter = new LocalSession.storageMemory(this.options.database, defaultAdaptersOptions)
// TODO: Remove back-compat conditional in next version due to deprecation storagefileAsync
} else if (this.options.storage === LocalSession.storageFileAsync || this.options.storage === LocalSession.storagefileAsync) {
} else if (this.options.storage === LocalSession.storageFileAsync) {
debug('Initiating: lowdb adapter: storageFileAsync')
this._adapter = new LocalSession.storageFileAsync(this.options.database, defaultAdaptersOptions)
// TODO: Remove back-compat conditional in next version due to deprecation of storagefileSync
} else if (this.options.storage === LocalSession.storageFileSync || this.options.storage === LocalSession.storagefileSync) {
} else if (this.options.storage === LocalSession.storageFileSync) {
debug('Initiating: lowdb adapter: storageFileSync')
this._adapter = new LocalSession.storageFileSync(this.options.database, defaultAdaptersOptions)
} else {
Expand All @@ -72,7 +71,9 @@ class LocalSession {
if (isPromise(DbInstance)) {
debug('DbInstance is Promise like')
// TODO: Split it from constructor, because this code will produce glitches if async initiating may take too long time
DbInstance.then((DB) => {
this.DB = DbInstance
this.DB.then((DB) => {
debug('DbInstance Promise resolved')
this.DB = DB
_initDB.call(this)
})
Expand Down Expand Up @@ -119,7 +120,7 @@ class LocalSession {
this._called(arguments)
if (!key) return
// If there's no data provided or it's empty, we should remove session record from database
if (!data || Object.keys(data).length === 0) {
if (this.DB._.isEmpty(data)) {
debug('Removing session #', key)
return this.DB.get('sessions').removeById(key).write()
}
Expand Down Expand Up @@ -190,12 +191,10 @@ class LocalSession {
*
* @memberof! LocalSession
* @name LocalSession.storagefileSync
* @deprecated since version 0.0.5 renamed to {@link LocalSession.storageFileSync|LocalSession.storageFileSync}
* @global
* @alias LocalSession.storageFileSync
* @readonly
*/
static get storagefileSync () {
console.warn('> DeprWarn: LocalSession.storagefileSync renamed to storageFileSync')
return storageFileSync
}

Expand All @@ -216,13 +215,10 @@ class LocalSession {
*
* @memberof! LocalSession
* @name LocalSession.storagefileAsync
* @deprecated since version 0.0.5 renamed to {@link LocalSession.storageFileAsync|LocalSession.storageFileAsync}
* @deprecated since version 0.0.5
* @global
* @alias LocalSession.storageFileAsync
* @readonly
*/
static get storagefileAsync () {
console.warn('> DeprWarn: LocalSession.storagefileAsync renamed to storageFileAsync')
return storageFileAsync
}

Expand Down Expand Up @@ -279,6 +275,7 @@ function _initDB () {
// If database is empty, fill it with empty Array of sessions and optionally with initial state
this.DB.defaults(Object.assign({ sessions: [] }, this.options.state)).write()
debug('Initiating finished')
return true
}

// Credits to `is-promise` package
Expand All @@ -287,7 +284,7 @@ function isPromise (obj) {
}

/**
* @overview {@link http://telegraf.js.org/|Telegraf} Session middleware for storing sessions locally (memory/fileSync/fileAsync/...)
* @overview {@link http://telegraf.js.org/|Telegraf} Session middleware for storing sessions locally (Memory/FileSync/FileAsync/...)
* @module telegraf-session-local
* @version 0.0.5
* @license MIT
Expand All @@ -298,3 +295,4 @@ function isPromise (obj) {
* @exports LocalSession
*/
module.exports = LocalSession
module.exports.isPromise = isPromise
31 changes: 5 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,6 @@
"coverage": "nyc report --reporter=text-lcov | coveralls",
"jsdoc": "node_modules/.bin/jsdoc -c jsdoc.json"
},
"eslintConfig": {
"extends": [
"standard"
],
"plugins": [
"mocha"
],
"env": {
"mocha": true,
"node": true
},
"rules": {
"one-var": 0,
"comma-dangle": 0,
"new-cap": 0,
"brace-style": [0, "1tbs", { "allowSingleLine": true }]
}
},
"eslintIgnore": [
"docs/*"
],
"dependencies": {
"debug": "^3.1.0",
"lodash-id": "^0.14.0",
Expand All @@ -74,17 +53,17 @@
"devDependencies": {
"coveralls": "^3.0.0",
"docdash": "^1.0.0",
"eslint": "^5.0.0",
"eslint": "^5.4.0",
"eslint-config-standard": "^11.0.0",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-mocha": "^5.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-mocha": "^5.2.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-standard": "^3.1.0",
"jsdoc": "^3.5.5",
"mocha": "^5.2.0",
"nyc": "^12.0.2",
"should": "^13.1.0",
"nyc": "^13.0.1",
"should": "^13.2.3",
"telegraf": "^2.0.0 || ^3.0.0"
}
}
71 changes: 71 additions & 0 deletions tests/general.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,43 @@ describe('Telegraf Session local : General', () => {
let bot = {}
let localSession = new LocalSession(options)

it('Should works without specifying any options for LocalSession', (done) => {
bot = new Telegraf()
let session = new LocalSession()
bot.on('text', session.middleware(), (ctx) => {
should.exist(ctx.session)
done()
})
// Temporary using setTimeout() because `telegraf-local-session` doesn't handle async adapters correctly yet
setTimeout(() => {
bot.handleUpdate({ message: { chat: { id: 1 }, from: { id: 1 }, text: 'hey' } })
}, 25)
})

it('Should use custom `format.serialize` and `format.deserialize` functions', (done) => {
bot = new Telegraf()
let session = new LocalSession({
database: 'test_sync_db.json',
storage: LocalSession.storageFileSync,
format: {
// By default lowdb uses pretty-printed JSON string: JSON.stringify(obj, null, 2)
// We will override that behaviour calling it `oneline`, making one-lined JSON string
serialize: function oneline (obj) {
return JSON.stringify(obj)
},
deserialize: JSON.parse
}
})
bot.on('text', session.middleware(), (ctx) => {
should.exist(ctx.session)
ctx.session.wow = true
// ctx.session.should.have.property('wow')
// ctx.session.foo.should.be.equal(true)
done()
})
bot.handleUpdate({ message: { chat: { id: 1 }, from: { id: 1 }, text: 'hey' } })
})

it('Should have access to lowdb instance via ctx.sessionDB', (done) => {
bot = new Telegraf()
bot.on('text', localSession.middleware(), (ctx) => {
Expand Down Expand Up @@ -39,4 +76,38 @@ describe('Telegraf Session local : General', () => {
})
bot.handleUpdate({ message: { chat: { id: 1 }, from: { id: 1 }, text: 'hey' } })
})

it('Should return `undefined` when context has no `from` field', (done) => {
bot = new Telegraf()
bot.on('text', localSession.middleware(), (ctx) => {
debug('Telegraf context `from` field: %o', ctx.from)
should.not.exists(localSession.getSessionKey(ctx))
done()
})
bot.handleUpdate({ message: { chat: { id: 1 }, text: 'hey' } })
})

it('Should return `undefined` when no key provided for session to be saved', (done) => {
bot = new Telegraf()
bot.on('text', localSession.middleware(), (ctx) => {
let sessionKey = localSession.getSessionKey(ctx)
debug('Real session key calculated by LocalSession: %s', sessionKey)
should.not.exists(localSession.saveSession(undefined, { authenticated: false }))
done()
})
bot.handleUpdate({ message: { chat: { id: 1 }, from: { id: 1 }, text: 'hey' } })
})

it('Should detect if object is Promise/like or not', (done) => {
const isPromise = require('../lib/session').isPromise
function notPromise () { return null }
function promise () { return new Promise((resolve, reject) => resolve(null)) }
function promiseLike () { return { then: cb => cb(null) } }
isPromise(undefined).should.be.equal(false)
isPromise(true).should.be.equal(false)
isPromise(notPromise()).should.be.equal(false)
isPromise(promise()).should.be.equal(true)
isPromise(promiseLike()).should.be.equal(true)
done()
})
})
Loading

0 comments on commit 21bd956

Please sign in to comment.