Skip to content

Commit

Permalink
Replace serializer with data mapper (#10)
Browse files Browse the repository at this point in the history
Resolves #7: Support custom mappings of session object to a database record.
  • Loading branch information
jhecking committed Oct 6, 2017
1 parent ba52668 commit b4853d2
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 27 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ parameters:
Aerospike db. If not specified, the ttl will be determined based on the
`maxAge` of the session cookie, if any. Set `ttl` to zero to disable usage of
ttl. However, note that a default ttl at the namespace level might still apply.
* `serializer` - A custom serializer to convert session objects to/from a
format suitable for storage in an Aerospike record. By default the `JSON`
* `mapper` - A custom data mapper to convert session objects to/from a
format suitable for storage in an Aerospike record. By default, the `JSON`
module is used to serialize session objects to/from JSON format.

Additional options are passed on to the Aerospike client when creating a new
Expand Down
30 changes: 15 additions & 15 deletions lib/aerospike_store.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
'use strict'

const Aerospike = require('aerospike')
const DataMapper = require('./data_mapper')

const debug = require('debug')('session:aerospike')
const util = require('util')
Expand All @@ -42,7 +43,11 @@ module.exports = function (session) {
this.as_namespace = options.namespace || 'test'
this.as_set = options.set || 'express-session'
this.ttl = options.ttl
this.serializer = options.serializer || JSON
this.mapper = options.mapper || new DataMapper()

if (options.serializer) {
throw new Error('The `serializer` option is no longer supported - supply a custom data mapper instead.')
}

if (options.client) {
this.client = options.client
Expand Down Expand Up @@ -120,27 +125,22 @@ module.exports = function (session) {
AerospikeStore.prototype.get = function (sid, callback) {
const key = this._key(sid)
debug('GET "%s"', sid)
this.client.get(key, (err, record) => {
if (err) {
switch (err.code) {
this.client.get(key, (error, record) => {
if (error) {
switch (error.code) {
case Aerospike.status.AEROSPIKE_ERR_RECORD_NOT_FOUND:
return callback()
default:
return callback(err)
return callback(error)
}
}

const bins = record.bins
const sessionBin = bins.session
if (!sessionBin) {
return callback()
}

try {
const session = this.serializer.parse(sessionBin)
const bins = record.bins
const session = this.mapper.fromRecord(bins)
return callback(null, session)
} catch (err) {
return callback(err)
} catch (error) {
return callback(error)
}
})
}
Expand Down Expand Up @@ -169,7 +169,7 @@ module.exports = function (session) {
AerospikeStore.prototype.set = function (sid, session, callback) {
try {
const key = this._key(sid)
const bins = { session: this.serializer.stringify(session) }
const bins = this.mapper.toRecord(session)
const meta = { ttl: this._ttl(session) }
debug('PUT "%s", ttl: %s', sid, meta.ttl)
this.client.put(key, bins, meta, error => callback(error))
Expand Down
45 changes: 45 additions & 0 deletions lib/data_mapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// *****************************************************************************
// Copyright 2016-2017 Aerospike, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// *****************************************************************************

'use strict'

class DataMapper {
/**
* Converts a session object into a database record.
*
* @param {Object} session - session object
* @returns {Object} Aerospike database record
*/
toRecord (session) {
return {
session: JSON.stringify(session)
}
}

/**
* Converts a database record into a session object.
*
* @param {Object} record - Aerospike record bins
* @param {Object} session object
*/
fromRecord (record) {
const sessionBin = record.session
if (!sessionBin) return null
return JSON.parse(sessionBin)
}
}

module.exports = DataMapper
27 changes: 17 additions & 10 deletions test/aerospike_store_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,28 @@ test('failed connection', function (t) {
})

test('serializer', function (t) {
const serializer = {
stringify: (value, replacer, space) => 'XXX' + JSON.stringify(value, replacer, space),
parse: str => {
t.ok(str.match(/^XXX/))
return JSON.parse(str.substring(3))
}
const fn = function () {
const options = { serializer: 'foo' }
const store = new AerospikeStore(options)
store.close(false)
}
t.throws(fn, 'serializer', 'Trying to set `serializer` should raise an error')
t.end()
})

test('mapper', function (t) {
const mapper = {
toRecord: session => session,
fromRecord: bins => bins
}
t.equal(serializer.stringify('UnitTest'), 'XXX"UnitTest"')
t.equal(serializer.parse(serializer.stringify('UnitTest')), 'UnitTest')
t.deepEqual(mapper.toRecord({ name: 'jan' }), { name: 'jan' })
t.deepEqual(mapper.fromRecord({ name: 'jan' }), { name: 'jan' })

const store = new AerospikeStore({ serializer: serializer })
const store = new AerospikeStore({ mapper: mapper })
return lifecycleTest(store, t)
})

test('serializer error', function (t) {
test('mapper error', function (t) {
const a = {}
const b = {}
// create circular reference to cause serialization error
Expand Down

0 comments on commit b4853d2

Please sign in to comment.