New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

sequelize-simple-cache

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sequelize-simple-cache - npm Package Compare versions

Comparing version 1.0.0-beta.2 to 1.0.0-beta.3

3

package.json
{
"name": "sequelize-simple-cache",
"version": "1.0.0-beta.2",
"version": "1.0.0-beta.3",
"description": "A simple, transparent, client-side, in-memory cache for Sequelize",

@@ -42,2 +42,3 @@ "main": "src/SequelizeSimpleCache.js",

"nyc": "^13.1.0",
"sequelize": "^4.41.1",
"sinon": "^7.0.0",

@@ -44,0 +45,0 @@ "sinon-chai": "^3.2.0"

@@ -6,2 +6,3 @@ # sequelize-simple-cache

Selectively add your Sequelize models to the cache.
Works with a all storage engines supported by Sequelize.

@@ -76,3 +77,3 @@ [![Build Status](https://travis-ci.org/frankthelen/sequelize-simple-cache.svg?branch=master)](https://travis-ci.org/frankthelen/sequelize-simple-cache)

// if you don't want that to be cached, bypass the cache like this
Model.cacheNo().findOne({ where: { startDate: { [Op.lte]: fn('NOW') }, } });
Model.cacheBypass().findOne({ where: { startDate: { [Op.lte]: fn('NOW') }, } });
```

@@ -97,3 +98,3 @@

```javascript
User.cacheNo().findOne(...);
User.cacheBypass().findOne(...);
```

@@ -100,0 +101,0 @@

@@ -8,13 +8,41 @@ const Promise = require('bluebird');

constructor(config = {}, options = {}) {
this.config = config;
this.methods = [
'findById', 'findOne', 'findAll', 'findAndCountAll',
'count', 'min', 'max', 'sum',
];
this.defaults = {
ttl: 60 * 60, // 1 hour
methods: ['findById', 'findOne', 'findAll', 'findAndCountAll', 'count', 'min', 'max', 'sum'],
};
this.config = Object.entries(config)
.reduce((acc, [name, { ttl = this.defaults.ttl, methods = this.defaults.methods }]) => ({
...acc,
[name]: { ttl, methods },
}), {});
const { debug = false } = options;
this.debug = debug;
this.cache = new Map();
this.debug = (...args) => debug && console.debug(...args); // eslint-disable-line no-console
this.disabled = new Set();
}
log(tag, details) {
if (!this.debug) return;
const { args, data } = details;
const out = details;
if (args) {
out.args = SequelizeSimpleCache.stringify(args);
}
if (data) {
out.data = JSON.stringify(data);
}
console.debug(`>>> CACHE ${tag.toUpperCase()} >>>`, out); // eslint-disable-line no-console
}
static stringify(obj) {
// Unfortunately, there seam to be no stringifyers or object hashers that work correctly
// with ES6 symbols and function objects. But this is important for Sequelize queries.
// This is the only solution that seams to be working.
return inspect(obj, { depth: Infinity, maxArrayLength: Infinity, breakLength: Infinity });
}
static hash(obj) {
return md5(SequelizeSimpleCache.stringify(obj));
}
init(model) { // Sequelize model object

@@ -24,3 +52,3 @@ const { name } = model;

/* eslint-disable no-param-reassign */
model.cacheNo = () => model; // bypass
model.cacheBypass = () => model;
model.cacheClear = () => this.clear(name);

@@ -35,7 +63,7 @@ model.cacheClearAll = () => this.clear();

const config = this.config[name];
if (!config) return model; // no caching
const { ttl = 60 * 60, methods = this.methods } = config;
// setup Proxy for caching
this.debug('>>> CACHE INIT >>>', { name, config });
const interceptor = {
if (!config) return model; // no caching for this model
const { ttl, methods } = config;
this.log('init', { model: name, ttl, methods });
// proxy for intercepting Sequelize methods
return new Proxy(model, {
get: (target, prop) => {

@@ -46,4 +74,3 @@ if (this.disabled.has(name) || !methods.includes(prop)) {

const fn = (...args) => {
const key = `${name}.${prop}.${inspect(args, { depth: null })}`;
const hash = md5(key);
const hash = SequelizeSimpleCache.hash({ name, prop, args });
const item = this.cache.get(hash);

@@ -53,4 +80,4 @@ if (item) { // hit

if (expires > Date.now()) {
this.debug('>>> CACHE RESOLVE >>>', {
key, hash, data: JSON.stringify(data), expires, size: this.cache.size,
this.log('hit', {
model: name, method: prop, args, hash, data, expires, size: this.cache.size,
});

@@ -69,3 +96,4 @@ return Promise.resolve(data); // resolve from cache

};
return new Proxy(fn, { // support Sinon-decorated properties
// proxy for supporting Sinon-decorated properties on mocked model functions
return new Proxy(fn, {
get: (_, deco) => { // eslint-disable-line consistent-return

@@ -78,4 +106,3 @@ if (Reflect.has(target, prop) && Reflect.has(target[prop], deco)) {

},
};
return new Proxy(model, interceptor);
});
}

@@ -82,0 +109,0 @@

@@ -5,2 +5,3 @@ const chai = require('chai');

const sinonChai = require('sinon-chai');
const { Op, fn } = require('sequelize');
const SequelizeSimpleCache = require('..');

@@ -39,2 +40,32 @@

it('should generate unique hashes for Sequelize queries with ES6 symbols and functions', () => {
const queries = [{
where: {
config: '07d54b5c-78d0-4315-9ffc-581a4afa6f6d',
startDate: { [Op.lte]: fn('NOW') },
},
order: [['majorVersion', 'DESC'], ['minorVersion', 'DESC'], ['patchVersion', 'DESC']],
}, {
where: {
config: '07d54b5c-78d0-4315-9ffc-581a4afa6f6d',
startDate: { [Op.lte]: fn('NOW-XXX') },
},
order: [['majorVersion', 'DESC'], ['minorVersion', 'DESC'], ['patchVersion', 'DESC']],
}, {
where: {
config: '07d54b5c-78d0-4315-9ffc-581a4afa6f6d',
startDate: {},
},
order: [['majorVersion', 'DESC'], ['minorVersion', 'DESC'], ['patchVersion', 'DESC']],
}];
const hashes = new Set();
const hashes2 = new Set();
queries.forEach(q => hashes.add(SequelizeSimpleCache.hash(q)));
queries.forEach(q => hashes2.add(SequelizeSimpleCache.hash(q)));
const union = new Set([...hashes, ...hashes2]);
expect(hashes.size).to.be.equal(queries.length);
expect(hashes2.size).to.be.equal(queries.length);
expect(union.size).to.be.equal(queries.length);
});
it('should create decorations on model', async () => {

@@ -48,3 +79,3 @@ const stub = sinon.stub().resolves({ username: 'fred' });

const User = cache.init(model);
expect(User).to.have.property('cacheNo').which.is.a('function');
expect(User).to.have.property('cacheBypass').which.is.a('function');
expect(User).to.have.property('cacheClear').which.is.a('function');

@@ -264,3 +295,3 @@ expect(User).to.have.property('cacheClearAll').which.is.a('function');

const result2 = await User.findOne({ where: { username: 'fred' } });
const result3 = await User.cacheNo().findOne({ where: { username: 'fred' } });
const result3 = await User.cacheBypass().findOne({ where: { username: 'fred' } });
expect(stub.calledTwice).to.be.true;

@@ -294,3 +325,3 @@ expect(result1).to.be.deep.equal({ username: 'fred' });

it('should work to stub models using Sinon in unit tests / option 1', async () => {
it('should work to stub model using Sinon in unit tests / pattern 1', async () => {
const model = {

@@ -311,3 +342,3 @@ name: 'User',

it('should work to stub models using Sinon in unit tests / option 2', async () => {
it('should work to stub model using Sinon in unit tests / pattern 2', async () => {
const model = {

@@ -327,2 +358,14 @@ name: 'User',

});
it('should throw error if model is wrongly mocked', async () => {
const model = {
name: 'User',
findOne: async () => ({ username: 'fred' }),
};
const cache = new SequelizeSimpleCache({ User: {} });
const User = cache.init(model);
sinon.stub(User, 'findOne').returns({ username: 'foo' }); // should be `resolves`
expect(() => User.findOne({ where: { username: 'foo' } }))
.to.throw('User.findOne() did not return a promise but should');
});
});
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