Skip to content

Commit

Permalink
fix: support multiple createRecord calls (fixes #239)
Browse files Browse the repository at this point in the history
This commit changes how ember-pouch implements `Adapter.createRecord`,
which is invoked after calling `.save()` on a new record,
so that it does not create multiple records if `.save()` is called
more than once before the first operation has finished.

If `.save()` is only invoked once before the record has finished persisting
to the DB (i.e. the promise that it returns has resolved) then
the behavior is unchanged. However, subsequent calls will wait for
the previously returned promise to resolve and then, if changes have
been made to the record, as indicated by Snapshot#changedAttributes,
it will delegate the task to `updateRecord`.

To avoid a problem caused by ember-data changing the ID associated
with the internalModel/record when the record has finished persisting,
the `Adapter.generateIdForRecord` method has been implemented so
that the ID is available immediately. Previously ember-pouch had still
been generating this id during `createRecord`, but ember-data was
not being made aware of this until its returned promise resolved.

Also, rather than rely on `adapter.db.rel.uuid()` to generate
an RFC4122 v4 UUID (requiring initialization to have completed),
this has been replaced by the equivalent `uuid` module from npm,
and the ember-auto-import addon has been installed to make it
easy to access this from within ember-pouch.

Finally, the `engines` section of package.json has been updated
to align with ember-auto-import's minimum version of 6.x
BREAKING CHANGE: drop node 4.x support (and 6.x/7.x not tested by CI)
  • Loading branch information
jacobq committed Jan 7, 2020
1 parent 9bfde5c commit 7c238ff
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 20 deletions.
70 changes: 53 additions & 17 deletions addon/adapters/pouch.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Ember from 'ember';
import DS from 'ember-data';
import { pluralize } from 'ember-inflector';
import { v4 } from 'uuid';
//import BelongsToRelationship from 'ember-data/-private/system/relationships/state/belongs-to';

import {
Expand All @@ -9,6 +10,20 @@ import {
configFlagDisabled
} from '../utils';

function getRevFromSaveResult(records) {
let rev = null;
try {
rev = records[Object.keys(records)[0]][0].rev;
if (!rev || Object.keys(records).length > 1) {
// eslint-disable-next-line no-console
console.warn(`getRevFromSaveResult going to return ${rev}, but that may not be correct`);
}
} catch(e) {
throw Error(`Could not determine rev`);
}
return rev;
}

const {
getOwner,
run: {
Expand Down Expand Up @@ -153,7 +168,7 @@ export default DS.RESTAdapter.extend({
willDestroy: function() {
this._stopChangesListener();
},

_indexPromises: [],

_init: function (store, type) {
Expand Down Expand Up @@ -206,8 +221,9 @@ export default DS.RESTAdapter.extend({
relModel = (typeof rel.type === 'string' ? store.modelFor(rel.type) : rel.type);
if (relModel) {
let includeRel = true;
if (!('options' in rel)) rel.options = {};

if (!('options' in rel)) {
rel.options = {};
}
if (typeof(rel.options.async) === "undefined") {
rel.options.async = config.emberPouch && !Ember.isEmpty(config.emberPouch.async) ? config.emberPouch.async : true;//default true from https://github.com/emberjs/data/pull/3366
}
Expand Down Expand Up @@ -464,27 +480,47 @@ export default DS.RESTAdapter.extend({
});
},

generateIdForRecord: function(/* store, type, inputProperties */) {
return v4();
},

createdRecords: {},
createRecord: function(store, type, record) {
this._init(store, type);
var data = this._recordToData(store, type, record);
let rel = this.get('db').rel;

let id = data.id;
if (!id) {
id = data.id = rel.uuid();
createRecord: function(store, type, snapshot) {
const record = snapshot.record;
if (record._emberPouchSavePromise) {
const changes = record.changedAttributes();
record._emberPouchSavePromise = record._emberPouchSavePromise.then(records => {
// If there have been changes since the document was created then we should update the record now
if (Object.keys(changes).length > 0) {
// Include latest rev to indicate that we're aware that data has changed since original request
// (otherwise a document update conflict error would be thrown by the DB)
snapshot._attributes.rev = getRevFromSaveResult(records);
return this.updateRecord(store, type, snapshot);
}
return records;
});
return record._emberPouchSavePromise;
}

this._init(store, type);
var data = this._recordToData(store, type, snapshot);
const rel = this.get('db').rel;
const id = data.id;
this.createdRecords[id] = true;

return rel.save(this.getRecordTypeName(type), data).catch((e) => {
delete this.createdRecords[id];
throw e;
Object.defineProperty(record, '_emberPouchSavePromise', {
enumerable: false,
writable: true,
value: rel.save(this.getRecordTypeName(type), data).catch((e) => {
delete this.createdRecords[id];
throw e;
}),
});
return record._emberPouchSavePromise;
},

updateRecord: function (store, type, record) {
updateRecord: function (store, type, snapshot) {
this._init(store, type);
var data = this._recordToData(store, type, record);
var data = this._recordToData(store, type, snapshot);
return this.get('db').rel.save(this.getRecordTypeName(type), data);
},

Expand Down
10 changes: 9 additions & 1 deletion ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
let app = new EmberAddon(defaults, {
// Add options here
autoImport: {
webpack: {
node: {
global: true
}
},
// We could use ember-auto-import for these, but index.js is already handling them
exclude: ['pouchdb', 'pouchdb-find', 'relational-pouch']
}
});

/*
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"CouchDB"
],
"engines": {
"node": "^4.5 || 6.* || >= 7.*"
"node": ">= 6.*"
},
"author": "Nolan Lawson",
"license": "Apache-2.0",
Expand Down Expand Up @@ -62,9 +62,11 @@
"dependencies": {
"broccoli-file-creator": "^2.1.1",
"broccoli-stew": "^2.1.0",
"ember-auto-import": "^1.5.3",
"ember-cli-babel": "^7.7.3",
"pouchdb": "^7.1.1",
"relational-pouch": "^3.1.0",
"ember-cli-babel": "^7.7.3"
"uuid": "^3.3.3"
},
"ember-addon": {
"configPath": "tests/dummy/config"
Expand Down

0 comments on commit 7c238ff

Please sign in to comment.