diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 2daecc1cb..de6fef6ad 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog +##### 1.2.4 (2015-08-29) +- Feature: add `enforceLinks` option, when set as `false` it will ignore referential integrity errors. Useful for client-side use. + + ##### 1.2.3 (2015-08-28) - Polish: do not expose missing related records error, should be internal error. - Polish: use UTF-8 charset for ad hoc serializer. diff --git a/lib/browser.js b/lib/browser.js index 1c55cb5b9..4b24019b0 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -27,6 +27,9 @@ export default class Fortune extends FortuneCore { if (hasIndexedDB) options.adapter = { type: indexedDB } else if (hasWebStorage) options.adapter = { type: webStorage } + if (!('enforceLinks' in options)) + options.enforceLinks = false + super(options) } diff --git a/lib/core.js b/lib/core.js index cd4ffba7e..e38748985 100644 --- a/lib/core.js +++ b/lib/core.js @@ -64,7 +64,11 @@ export default class Fortune extends events.EventEmitter { * * // An options object that is specific to the serializer. Optional. * options: { ... } - * }] + * }], + * + * // Whether or not to enforce referential integrity. Default: `true` for + * // server, `false` for browser. + * enforceLinks: true * } * ``` * diff --git a/lib/dispatch/check_links.js b/lib/dispatch/check_links.js index fd57ce086..78f09611e 100644 --- a/lib/dispatch/check_links.js +++ b/lib/dispatch/check_links.js @@ -1,5 +1,4 @@ import * as keys from '../common/keys' -import { includes } from '../common/array_proxy' /** @@ -9,11 +8,12 @@ import { includes } from '../common/array_proxy' * @param {Object} fields * @param {Array} links - An array of strings indicating which fields are * links. Need to pass this so that it doesn't get computed each time. - * @param {Object} adapter * @param {Object} [meta] * @return {Promise} */ -export default function checkLinks (record, fields, links, adapter, meta) { +export default function checkLinks (record, fields, links, meta) { + const { adapter, options: { enforceLinks } } = this + return Promise.all(links.map(field => new Promise((resolve, reject) => { const ids = Array.isArray(record[field]) ? record[field] : !(field in record) || record[field] === null ? [] : [ record[field] ] @@ -25,10 +25,13 @@ export default function checkLinks (record, fields, links, adapter, meta) { }, meta) .then(records => { - for (let record of records) - if (!includes(ids, record[keys.primary])) + if (enforceLinks) { + const recordIds = new Set(records.map(record => record[keys.primary])) + + for (let id of ids) if (!recordIds.has(id)) return reject(new Error( `A related record for the field "${field}" was not found.`)) + } return resolve(records) }) diff --git a/lib/dispatch/create.js b/lib/dispatch/create.js index bd943c515..c0d911f08 100644 --- a/lib/dispatch/create.js +++ b/lib/dispatch/create.js @@ -44,7 +44,7 @@ export default function (context) { enforce(type, record, fields) // Ensure referential integrity. - return checkLinks(record, fields, links, adapter, meta) + return checkLinks.call(this, record, fields, links, meta) })) .then(() => adapter.beginTransaction()) .then(t => { diff --git a/lib/dispatch/update.js b/lib/dispatch/update.js index d2c03c76c..2be0ee637 100644 --- a/lib/dispatch/update.js +++ b/lib/dispatch/update.js @@ -27,7 +27,7 @@ export default function (context) { // Keyed by update, valued by hash of linked records. const linkedMap = new WeakMap() - const { type, meta } = context.request + const { request: { type, meta } } = context const fields = recordTypes[type] const transform = transforms[type] const links = Object.keys(fields) @@ -82,7 +82,7 @@ export default function (context) { enforce(type, record, fields) // Ensure referential integrity. - return checkLinks(record, fields, links, adapter, meta) + return checkLinks.call(this, record, fields, links, meta) .then(linked => { linkedMap.set(update, linked) return record diff --git a/lib/index.js b/lib/index.js index b1102c153..b846647fe 100644 --- a/lib/index.js +++ b/lib/index.js @@ -26,6 +26,9 @@ export default class Fortune extends FortuneCore { options.serializers = Object.keys(serializers).map(name => ({ type: serializers[name] })) + if (!('enforceLinks' in options)) + options.enforceLinks = true + super(options) } diff --git a/package.json b/package.json index 87091561d..4b56a940e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fortune", "description": "High-level I/O for web applications.", - "version": "1.2.3", + "version": "1.2.4", "license": "MIT", "author": { "email": "0x8890@airmail.cc", diff --git a/test/integration/serializers/ad_hoc.js b/test/integration/serializers/ad_hoc.js index 1d3769c69..ec8217e34 100644 --- a/test/integration/serializers/ad_hoc.js +++ b/test/integration/serializers/ad_hoc.js @@ -107,7 +107,8 @@ run(() => { headers: { 'Content-Type': mediaType }, body: [ { name: 'Ayy lmao', - nicknames: [ 'ayy', 'lmao' ] + nicknames: [ 'ayy', 'lmao' ], + owner: 1 } ] }, response => { equal(response.status, 201, 'status is correct')