Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection and resource ID handling altered to be case-insensitive for issue #49. #51

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions lib/Collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ class Collection {
throw new TypeError('Provided class is not a function (constructor)');
}

// A Collection is case-insensitive by default.
// When a Collection is case-insensitive, all map keys are converted to lowercase before use.
// If being used with a case-sensitive app or router, caseSensitive will be set to true by setupRoutes.
this.caseSensitive = false;

this.map = {};
this.Class = Class;
this.lastModified = Date.now();
Expand Down Expand Up @@ -76,10 +81,16 @@ class Collection {
}

has(id) {
if (!this.caseSensitive) {
id = id.toLowerCase();
}
return this.map.hasOwnProperty(id);
}

get(id) {
if (!this.caseSensitive) {
id = id.toLowerCase();
}
return this.map[id];
}

Expand Down Expand Up @@ -108,6 +119,9 @@ class Collection {
}

set(id, resource, cb) {
if (!this.caseSensitive) {
id = id.toLowerCase();
}
const previous = this.map[id];
this.map[id] = resource;

Expand All @@ -133,6 +147,16 @@ class Collection {

setAll(resources, cb) {
const previous = this.map;
if (!this.caseSensitive) {
// Lowercase all keys in the new resource set.
const lowercasedResources = {};
for (const key in resources) {
if (resources.hasOwnProperty(key)) {
lowercasedResources[key.toLowerCase()] = resources[key];
}
}
resources = lowercasedResources;
}
this.map = resources;

this.save(this.getIds(), (error) => {
Expand All @@ -149,6 +173,9 @@ class Collection {
}

del(id, cb) {
if (!this.caseSensitive) {
id = id.toLowerCase();
}
const previous = this.get(id);

if (!previous) {
Expand Down Expand Up @@ -351,6 +378,15 @@ class Collection {
};
}
}

getCaseSensitive() {
return this.caseSensitive;
}

setCaseSensitive(caseSensitive) {
// caseSensitive is retained as a Boolean value with value = truthiness of parameter
this.caseSensitive = caseSensitive;
}
}


Expand Down
8 changes: 8 additions & 0 deletions lib/operations/collection/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ module.exports = function (collection, context, cb) {
const fn = context.createCustomFn('get', collection.Class);
const map = collection.getMapRef();

if (!collection.getCaseSensitive()) {
for (const clause in context.query) {
if (context.query.hasOwnProperty(clause)) {
context.query[clause] = context.query[clause].toLowerCase();
}
}
}

const properties = getProperties(context.query);
const propertyStatus = validateProperties(map, properties);
if (propertyStatus >= 400) {
Expand Down
23 changes: 20 additions & 3 deletions lib/setupRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,26 @@ function respond(context) {
// route handlers

module.exports = function setupRoutes(router, collection, path, rights) {
// Set up a regular expression for the given path and optionally allow extensions

const reCollection = new RegExp('^' + path + '(\\.[a-zA-Z]+)?/?$');
// When router is a Router, router.caseSensitive will yield true or false,
// but when router is a base Express application, it will always yield undefined.
const routerCaseSensitive = router.caseSensitive;

// When router is a Router, router.get('case sensitive routing') will yield a function,
// but when router is a base Express application, it will yield true, false or undefined.
const appCaseSensitiveRoutingSetting = router.get('case sensitive routing');
const appCaseSensitive = (typeof appCaseSensitiveRoutingSetting !== 'function' && appCaseSensitiveRoutingSetting);

// Set up a regular expression for the given path and optionally allow extensions.
// Also, set the collection's case sensitivity to match the app/router's case sensitivity,
// since this is one of the only places we currently have all this information together.
let reCollection;
if (appCaseSensitive || routerCaseSensitive) {
reCollection = new RegExp('^' + path + '(\\.[a-zA-Z]+)?/?$');
collection.setCaseSensitive(true);
} else {
reCollection = new RegExp('^' + path + '(\\.[a-z]+)?/?$', 'i');
collection.setCaseSensitive(false);
}

// GET all resources in the collection

Expand Down
6 changes: 3 additions & 3 deletions test/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ test('HTTP cache', function (t) {
collection = _collection;
http = _http;

collection.loadOne('Heineken', heineken);
heineken.id = 'Heineken';
collection.loadOne('heineken', heineken);
heineken.id = 'heineken';

t.end();
});
Expand All @@ -48,7 +48,7 @@ test('HTTP cache', function (t) {

t.equal(res.statusCode, 200, 'HTTP status 200 (OK)');
t.equal(res.headers['content-type'], 'application/json', 'JSON response');
t.deepEqual(data, { Heineken: heineken }, 'Heineken returned');
t.deepEqual(data, { heineken }, 'Heineken returned');
t.end();
});
});
Expand Down
Loading