Skip to content

Commit

Permalink
Adding cache.clear method
Browse files Browse the repository at this point in the history
  • Loading branch information
fishcharlie committed May 17, 2019
1 parent 7e781eb commit 5b237fa
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ This method allows you to remove an item from the cache. If the ID does not exis

This method returns a promise that will resolve when the item has been removed from the cache.

### cache.clear([settings])

This method allows you to clear all the items from the cache. The settings parameter is an optional object that you can pass in with a `internalCacheOnly` property that if set to true, won't call the clear method on the plugins.

This method returns a promise that will resolve when the items have been cleared from the cache.

### cache.fetch(id, retrieveFunction)

This method allows you to get an item from the cache then fall back to a retrieveFunction if the item is not in the cache. If you call `cache.fetch` multiple times before the `retrieveFunction` has completed, it will only call the `retrieveFunction` once, and resolve all the promises after that one `retrieveFunction` has completed.
Expand Down
15 changes: 15 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ debug.get = require("debug")("HeapStash:get");
debug.fetch = require("debug")("HeapStash:fetch");
debug.put = require("debug")("HeapStash:put");
debug.remove = require("debug")("HeapStash:remove");
debug.clear = require("debug")("HeapStash:clear");

class HeapStash {
constructor(settings = {}) {
Expand Down Expand Up @@ -191,6 +192,20 @@ class HeapStash {
debug.remove("Not removing item from plugins since internalCacheOnly is set to false.");
}
}
async clear(settings = {}) {
debug.clear("Clearing cache");

this._.internalcache = {};
this._.internalcachearray = [];

if (!settings.internalCacheOnly) {
debug.clear("Clearing cache from plugins");
await Promise.all(this.plugins.map((plugin) => plugin.run("clear")()));
debug.clear("Done clearing cache in plugins.");
} else {
debug.clear("Not clearing cache from plugins since internalCacheOnly is set to false.");
}
}
}

module.exports = HeapStash;
Expand Down
14 changes: 14 additions & 0 deletions lib/plugin/DynamoDB.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ module.exports = (settings) => {
"TableName": plugin._.tableName
}).promise();
};
plugin.tasks.clear = async () => {
let items = [], lastEvaluatedKey;
do {
const obj = {"TableName": plugin._.tableName};
if (lastEvaluatedKey) {
obj.ExclusiveStartKey = lastEvaluatedKey;
}
const res = await plugin._.dynamodb.scan(obj).promise();
items = [...items, ...res.Items];
lastEvaluatedKey = res.LastEvaluatedKey;
} while (lastEvaluatedKey);

await Promise.all(items.map((item) => item.id).map((id) => plugin._.dynamodb.deleteItem({"Key": {id}, "TableName": plugin._.tableName}).promise()));
};

return plugin;
};
31 changes: 31 additions & 0 deletions lib/plugin/FileSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ module.exports = (settings) => {
});
});
};
filesystem.tasks.clear = () => {
return new Promise((resolve, reject) => {
fs.readdir(path.join(settings.path), async (err, files) => {
/* istanbul ignore next */
if (err) {
reject();
}

function deleteFilePromise(file) {
return new Promise((resolve, reject) => {
fs.unlink(path.join(settings.path, file), (err) => {
/* istanbul ignore next */
if (err) {
reject();
} else {
resolve();
}
});
});
}

try {
await Promise.all(files.map((file) => deleteFilePromise(file)));
resolve();
} catch (e) {
/* istanbul ignore next */
reject();
}
});
});
};

return filesystem;
};
55 changes: 55 additions & 0 deletions test/07_clear.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const HeapStash = require("../");
const {expect} = require("chai");

describe("clear()", () => {
let cache;
beforeEach(() => cache = new HeapStash());

it("Should be a function", () => {
expect(cache.clear).to.be.a("function");
});

it("Should clear item from cache", async () => {
cache._.internalcache["test"] = {"data": 123};
cache._.internalcachearray.push("test");

await cache.clear();

expect(cache._.internalcache).to.eql({});
expect(cache._.internalcachearray).to.eql([]);
});

it("Should clear items from cache", async () => {
(new Array(50).fill("a").map((a, index) => index + 1)).forEach((item) => {
cache._.internalcache[`test${item}`] = {"data": item * 50};
cache._.internalcachearray.push(`test${item}`);
});

expect(cache._.internalcachearray.length).to.eql(50);

await cache.clear();

expect(cache._.internalcache).to.eql({});
expect(cache._.internalcachearray).to.eql([]);
});

it("Should fail silently if nothing in cache", async () => {
await cache.clear();

expect(cache._.internalcache).to.eql({});
expect(cache._.internalcachearray).to.eql([]);
});

it("Should not call any plugins if internalCacheOnly is set to true", async () => {
let called = false;
const plugin = new HeapStash.Plugin();
plugin.tasks.clear = () => {
called = true;
};
cache.plugins.push(plugin);

await cache.clear({"internalCacheOnly": true});

expect(called).to.be.false;
});
});
35 changes: 35 additions & 0 deletions test/plugins/02_FileSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,39 @@ describe("FileSystem", () => {
expect(error).to.not.exist;
});
});

describe("clear()", () => {
beforeEach(() => new Promise((resolve, reject) => fs.writeFile(path.join(__dirname, "tmp", "id"), JSON.stringify({"data": {"myitem": "Hello World"}}), (err) => {
if (err) {
reject(err);
} else {
resolve();
}
})));

it("Should clear item from file system cache", async () => {
await cache.clear();

let error, data;
try {
data = fs.readFileSync(path.join(__dirname, "tmp", "id"), "utf8");
} catch (e) {
error = e;
}

expect(error.message).to.include("ENOENT: no such file or directory");
expect(data).to.not.exist;
});

it("Should fail silently if no item in cache", async () => {
let error;
try {
await cache.clear();
} catch (e) {
error = e;
}

expect(error).to.not.exist;
});
});
});
46 changes: 46 additions & 0 deletions test/plugins/03_DynamoDB.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,50 @@ describe("DynamoDB", function() {
expect(error).to.not.exist;
});
});

describe("clear()", () => {
beforeEach(() => dynamodb.putItem({
"Item": AWS.DynamoDB.Converter.marshall({"id": "id", "data": {"myitem": "Hello World"}}),
"TableName": "TestTable"
}).promise());

it("Should clear item from DynamoDB cache", async () => {
await cache.clear();

const data = AWS.DynamoDB.Converter.unmarshall((await dynamodb.getItem({
"Key": {
"id": {
"S": "id"
}
},
"TableName": "TestTable"
}).promise()).Item);

expect(data).to.eql({});
});

it("Should clear items from DynamoDB cache", async () => {
const dataMap = {"myitem": "Hello World", "largestring": new Array(100000).fill("a").join("")};
await Promise.all((new Array(15).fill("a").map((a, index) => index + 1)).map((item) => dynamodb.putItem({
"Item": AWS.DynamoDB.Converter.marshall({"id": `id${item}`, dataMap}),
"TableName": "TestTable"
}).promise()));

await cache.clear();

const data = (await dynamodb.scan({"TableName": "TestTable"}).promise()).Items;
expect(data).to.eql([]);
});

it("Should fail silently if no item in cache", async () => {
let error;
try {
await cache.clear();
} catch (e) {
error = e;
}

expect(error).to.not.exist;
});
});
});

0 comments on commit 5b237fa

Please sign in to comment.