Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

connect-cloudant-store

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

connect-cloudant-store - npm Package Compare versions

Comparing version 0.0.4 to 0.1.0

71

lib/connect-cloudant-store.js

@@ -68,2 +68,5 @@ /**

this.disableTTLRefresh = options.disableTTLRefresh || false;
this.dbViewName = options.dbViewName || 'express_expired_sessions';
this.dbDesignName = options.dbDesignName || 'expired_sessions';
this.dbRemoveExpMax = options.dbRemoveExpMax || 100;
if (options.client) {

@@ -231,3 +234,71 @@ this.client = options.client;

CloudantStore.prototype.cleanupExpired = function() {
return new Promise(function(resolve, reject) {
self.initView().then(function() {
self.db.view(self.dbDesignName, self.dbViewName, {limit: self.dbRemoveExpMax})
.then(function(body) {
// bulk delete
if (body.total_rows > 0) {
var delRows = [];
body.rows.forEach(function(row) {
delRows.push(
{
_id: row.key,
_rev: row.value,
_deleted: true
}
);
});
debug('cleanupExpired - BULK delete %s', JSON.stringify(body.rows));
self.db.bulk({docs: delRows}, null)
.then(function() { resolve(); })
.catch(function(err) { reject(err); });
} else {
debug('cleanupExpired - Nothing to delete');
resolve();
}
}).catch(function(err) {
debug('cleanupExpired - ERROR reading expired sessions', JSON.stringify(err));
reject(err);
});
}).catch(function(err) {
debug('cleanupExpired - Failed to load/create views', JSON.stringify(err));
reject(err);
});
});
};
CloudantStore.prototype.initView = function() {
return new Promise(function(resolve, reject) {
self.db.view(self.dbDesignName, self.dbViewName, {limit: 0})
.then(function() { resolve(); })
.catch(function(err) {
if (err.statusCode === 404) {
// try to create DB
debug('View for expired session doesn\'t exists - Try to create');
var designDoc = {'views': {}};
designDoc['views'][self.dbViewName] = {
'map': function(doc) {
if (doc.session_ttl && doc.session_modified &&
Date.now() > (doc.session_ttl + doc.session_modified)) {
// eslint-disable-next-line
emit(doc._id, doc._rev);
}
}
};
self.db.insert(designDoc, '_design/' + self.dbDesignName)
.then(function() { resolve(); })
.catch(function(err) {
reject(err);
});
} else {
reject(err);
}
});
});
};
return CloudantStore;
};

2

package.json
{
"name": "connect-cloudant-store",
"version": "0.0.4",
"version": "0.1.0",
"description": "Node JS express-session storage connector for IBM Cloudant",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -32,2 +32,3 @@ # connect-cloudant-store

// Cloudant Session store is ready for use
// you can call store.cleanupExpired() here or set it to be executed at an interval
});

@@ -49,3 +50,3 @@

Standard usage for Bluemix environment/dev environment :
Standard usage for Bluemix (public) environment :

@@ -66,2 +67,12 @@ ```javascript

### Using the session auto-clean feature
The storage class has a auto-clean specific method that has to be called in your code. It could be called for example from a setIterval timer. It checks if there is already a view available for getting the expired sessions from store db, and if not, is trying to create it. There are related optional parameters to customize the name of the view/design, set a top limit for items deleted per cleanup call.
```javascript
store.on('connect', function() {
// Cloudant Session store is ready for use
setInterval(function() { store.cleanupExpired(); }, 3600 * 1000); here or set it to be executed at an interval
});
```
## Store Parameters

@@ -79,2 +90,5 @@

disableTTLRefresh: false,
dbViewName: 'express_expired_sessions';
dbDesignName: 'expired_sessions';
dbRemoveExpMax: 100,
// Cloudant() parameters used if 'client' is not provided

@@ -92,3 +106,3 @@ url: undefined,

### url
Allows to create the Cloudant client based on the url
Allows to create the Cloudant client based on the url (containing credentials)

@@ -108,3 +122,3 @@ ex:

Note: This will not work on *Bluemix Dedicated* because cloudant library is not searching by service name first, but instead by service type key first
**Note:** This will not work on *Bluemix Dedicated* because cloudant library is not searching by service name first, but instead by service type key first
and second by service name (instanceName);

@@ -141,2 +155,11 @@

### dbViewName
Name of the expired session view to be used for building the expired sessions list
### dbDesignName
Name of the couch db design name to be used for building the expired sessions list - if the design and the view is not found in the cloudant database, the first call to store.cleanupExpired() will try to create it.
### dbRemoveExpMax
Limits the maximum amount of sessions to be bulk deleted per each store.cleanupExpired() call.
## Debugging

@@ -170,3 +193,3 @@

PR code should be covered by UT
PR code should have UT associated with a good coverage

@@ -193,2 +216,2 @@ ```

[downloads-image]: https://img.shields.io/npm/dm/connect-cloudant-store.svg
[downloads-url]: https://npmjs.org/package/connect-cloudant-store
[downloads-url]: https://npmjs.org/package/connect-cloudant-store

@@ -29,9 +29,8 @@ 'use strict';

'insert': function() {},
'destroy': function() {}
'destroy': function() {},
'bulk': function() {},
'view': function() {}
};
var client = new Cloudant(goodClientParams);
// dbPutStub,
// dbDestroyStub,
// dbHeadStub;

@@ -46,3 +45,2 @@ describe('TestSuite - CloudantStore', function() {

clientGetDbStub = sinon.stub(client, 'use').returns(dbStub);
// dbDestroyStub = sinon.stub(Cloudant.prototype, 'destroy');
});

@@ -391,2 +389,225 @@

});
describe('TestSuite - CloudantStore - Cleanup Sessions', function() {
var dbViewStub,
dbBulkStub,
goodViewObj;
beforeEach(function() {
dbInfoStub = sinon.stub(dbStub, 'info');
dbInsertStub = sinon.stub(dbStub, 'insert');
dbViewStub = sinon.stub(dbStub, 'view');
dbBulkStub = sinon.stub(dbStub, 'bulk');
clientGetDbStub = sinon.stub(client, 'use').returns(dbStub);
goodViewObj = {
'total_rows': 1,
'rows': [
{key: 'sess:sess-id-1', value: 'rev-id-1'},
{key: 'sess:sess-id-2', value: 'rev-id-2'}
]
};
});
afterEach(function() {
clientGetDbStub.restore();
dbInfoStub.restore();
dbViewStub.restore();
dbBulkStub.restore();
dbInsertStub.restore();
});
it('Testcase - initView - existing view resolves', function(done) {
dbInfoStub.returns(Promise.resolve({}));
dbViewStub.returns(Promise.resolve({}));
var store = new CloudantStore({
client: client
});
store.initView();
setTimeout(function() {
expect(dbViewStub.called).to.be.true;
expect(dbInsertStub.called).to.be.false;
done();
}, 0);
});
it('Testcase - initView - no view non 404 - fail', function(done) {
dbInfoStub.returns(Promise.resolve({}));
var error = {statusCode: 409, message: 'Some message'};
dbViewStub.returns(Promise.reject(error));
var store = new CloudantStore({
client: client
});
store.initView()
.then(function() {
expect(false).to.be.true;
done();
})
.catch(function(err) {
expect(dbViewStub.called).to.be.true;
expect(dbInsertStub.called).to.be.false;
expect(err).to.be.ok;
expect(err).to.deep.equal(error);
done();
});
});
it('Testcase - initView - no view 404 - success to create', function(done) {
dbInfoStub.returns(Promise.resolve({}));
var error = {statusCode: 404, message: 'not found'};
dbViewStub.returns(Promise.reject(error));
dbInsertStub.returns(Promise.resolve());
var store = new CloudantStore({
client: client
});
store.initView()
.then(function() {
expect(dbViewStub.called).to.be.true;
expect(dbInsertStub.called).to.be.true;
done();
})
.catch(function() {
expect(false).to.be.true;
done();
});
});
it('Testcase - initView - no view 404 - failed to create', function(done) {
dbInfoStub.returns(Promise.resolve({}));
var error = {statusCode: 404, message: 'not found'};
var insertErr = {statusCode: 500, message: 'generic error'};
dbViewStub.returns(Promise.reject(error));
dbInsertStub.returns(Promise.reject(insertErr));
var store = new CloudantStore({
client: client
});
store.initView()
.then(function() {
expect(false).to.be.true;
done();
})
.catch(function(err) {
expect(dbViewStub.called).to.be.true;
expect(dbInsertStub.called).to.be.true;
expect(err).to.deep.equal(insertErr);
done();
});
});
it('Testcase - cleanupExpired - initView failure', function(done) {
dbInfoStub.returns(Promise.resolve({}));
var error = {statusCode: 500, message: 'generic message'};
var store = new CloudantStore({
client: client
});
sinon.stub(store, 'initView').returns(Promise.reject(error));
store.cleanupExpired()
.then(function() {
expect(false).to.be.true;
done();
})
.catch(function(err) {
expect(err).to.deep.equal(error);
done();
});
});
it('Testcase - cleanupExpired - db.view failure', function(done) {
dbInfoStub.returns(Promise.resolve({}));
var error = {statusCode: 429, message: 'generic 429 message'};
dbViewStub.returns(Promise.reject(error));
var store = new CloudantStore({
client: client
});
sinon.stub(store, 'initView').returns(Promise.resolve());
store.cleanupExpired()
.then(function() {
expect(false).to.be.true;
done();
})
.catch(function(err) {
expect(err).to.deep.equal(error);
done();
});
});
it('Testcase - cleanupExpired - nothing to delete', function(done) {
dbInfoStub.returns(Promise.resolve({}));
dbViewStub.returns(Promise.resolve({'total_rows': 0, 'rows': []}));
var store = new CloudantStore({
client: client
});
sinon.stub(store, 'initView').returns(Promise.resolve());
store.cleanupExpired()
.then(function() {
expect(dbViewStub.called).to.be.true;
done();
})
.catch(function() {
expect(false).to.be.true;
done();
});
});
it('Testcase - cleanupExpired - expired rows - delete fails', function(done) {
var error = {statusCode: 500, message: 'generic 500 message'};
dbInfoStub.returns(Promise.resolve({}));
dbViewStub.returns(Promise.resolve(goodViewObj));
dbBulkStub.returns(Promise.reject(error));
var store = new CloudantStore({
client: client
});
sinon.stub(store, 'initView').returns(Promise.resolve());
store.cleanupExpired()
.then(function() {
expect(false).to.be.true;
done();
})
.catch(function(err) {
expect(err).to.deep.equal(error);
expect(dbViewStub.called).to.be.true;
expect(dbBulkStub.calledWith({
docs: [
{_id: 'sess:sess-id-1', _rev: 'rev-id-1', _deleted: true},
{_id: 'sess:sess-id-2', _rev: 'rev-id-2', _deleted: true}
]
})).to.be.true;
done();
});
});
it('Testcase - cleanupExpired - expired rows - success delete', function(done) {
dbInfoStub.returns(Promise.resolve({}));
dbViewStub.returns(Promise.resolve(goodViewObj));
dbBulkStub.returns(Promise.resolve());
var store = new CloudantStore({
client: client
});
sinon.stub(store, 'initView').returns(Promise.resolve());
store.cleanupExpired()
.then(function() {
expect(dbViewStub.called).to.be.true;
expect(dbBulkStub.calledWith({
docs: [
{_id: 'sess:sess-id-1', _rev: 'rev-id-1', _deleted: true},
{_id: 'sess:sess-id-2', _rev: 'rev-id-2', _deleted: true}
]
})).to.be.true;
done();
})
.catch(function() {
expect(false).to.be.true;
done();
});
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc