|
import Adapter from 'ember-data/adapters/json-api'; |
|
import Ember from 'ember'; |
|
import RSVP from 'rsvp'; |
|
|
|
const { Promise } = RSVP; |
|
const { get } = Ember; |
|
|
|
class Transaction { |
|
constructor(store, ModelClass, primary, transactionMembers) { |
|
this.store = store; |
|
this.primary = primary; |
|
this.members = []; |
|
|
|
let relsByName = get(ModelClass, 'relationshipsByName'); |
|
let transaction = this; |
|
|
|
transactionMembers.forEach((name) => { |
|
let meta = relsByName.get(name); |
|
|
|
if (!meta) { |
|
throw new Error(`Unknown relationship ${name} was specified as a transactionMember while saving ${snapshot.record}`); |
|
} |
|
|
|
if (meta.kind === 'belongsTo') { |
|
transaction.add({ |
|
key:name, |
|
kind: meta.kind, |
|
data: primary.belongsTo(name) |
|
}); |
|
} else if (meta.kind === 'hasMany') { |
|
let snapshots = primary.hasMany(name); |
|
snapshots.forEach((snapshot, index) => { |
|
transaction.add({ |
|
key: name, |
|
kind: meta.kind, |
|
index, |
|
data: snapshot |
|
}) |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
add(member) { |
|
let promise = new Promise(resolve => { |
|
member.resolve = resolve; |
|
}); |
|
member.promise = promise; |
|
|
|
return this.members.push(member); |
|
} |
|
|
|
begin() { |
|
this.members.forEach(r => { |
|
r.data.record.save({ |
|
adapterOptions: { |
|
transactionPromise: r.promise |
|
} |
|
}); |
|
}); |
|
} |
|
|
|
complete(jsonApiDocument) { |
|
this.members.forEach(member => { |
|
this.extractMemberFromPayload(member, jsonApiDocument); |
|
}); |
|
} |
|
|
|
extractMemberFromPayload(member, doc) { |
|
let dataPath = `data.relationships.${member.key}.data`; |
|
let data = get(doc, dataPath); |
|
let ref; |
|
|
|
if (!doc.included) { |
|
throw new Error('No includes!'); |
|
} |
|
|
|
if (!data) { |
|
throw new Error('No relationship payload!'); |
|
} |
|
|
|
if (member.kind === 'belongsTo') { |
|
ref = data; |
|
} else { |
|
if (!Array.isArray(data)) { |
|
throw new Error('Expected array!'); |
|
} |
|
|
|
ref = data[member.index]; |
|
} |
|
|
|
let resolvePayload = doc.included.find(r => r.id === ref.id && r.type === ref.type); |
|
member.resolve({ data: resolvePayload }); |
|
} |
|
} |
|
|
|
export default Adapter.extend({ |
|
|
|
createRecord(store, ModelClass, snapshot) { |
|
let options = snapshot.adapterOptions; |
|
|
|
if (options && Array.isArray(options.transactionMembers) && !options.transactionInitialized) { |
|
return this.batchCreate(store, ModelClass, snapshot); |
|
} else if (options && options.transactionPromise) { |
|
return options.transactionPromise; |
|
} |
|
|
|
// super here in real life and ensure that |
|
// serializer.serializeIntoHash correctly adds relationships |
|
// return this._super(store, ModelClass, snapshot); |
|
return Promise.resolve(makeCreateResponse(snapshot)); |
|
}, |
|
|
|
batchCreate(store, ModelClass, snapshot) { |
|
let options = snapshot.adapterOptions; |
|
let { transactionMembers } = options; |
|
options.transactionInitialized = true; |
|
|
|
let transaction = new Transaction(store, ModelClass, snapshot, transactionMembers); |
|
|
|
transaction.begin(); |
|
let promise = this.createRecord(store, ModelClass, snapshot); |
|
|
|
return promise.then((rawPayload) => { |
|
let serializer = store.serializerFor(ModelClass.modelName); |
|
// short circuit for the example |
|
let normalizedResponse = rawPayload; |
|
// what you would do in real life |
|
// let normalizedResponse = serializer.normalizeResponse(store, ModelClass, rawPayload, null, 'createRecord'); |
|
transaction.complete(normalizedResponse); |
|
|
|
// TODO this is trolly, but rawPayload can't be trusted either |
|
return normalizedResponse; |
|
}); |
|
} |
|
}); |
|
|
|
let fakeId = 1; |
|
function makeCreateResponse(snapshot) { |
|
let response = {}; |
|
let data = response.data = {}; |
|
let included = response.included = []; |
|
|
|
data.type = snapshot.modelName; |
|
data.id = `fake-server-id-${fakeId++}`; |
|
data.attributes = snapshot.attributes(); |
|
let relationships = data.relationships = {}; |
|
|
|
let includes = snapshot.adapterOptions.transactionMembers; |
|
let rels = get(snapshot.type, 'relationshipsByName'); |
|
includes.forEach(name => { |
|
let meta = rels.get(name); |
|
if (meta.kind === 'belongsTo') { |
|
// to belongsTo things |
|
} else { |
|
let relationship = relationships[name] = {}; |
|
let rData = relationship.data = []; |
|
snapshot.hasMany(name).forEach((s, index) => { |
|
let type = s.modelName; |
|
let id = `fake-server-id-${fakeId++}`; |
|
rData.push({ type, id }); |
|
included.push({ |
|
type, |
|
id, |
|
attributes: s.attributes() |
|
}); |
|
}); |
|
} |
|
}); |
|
|
|
return response; |
|
} |