FIX: allows adapters to define a custom primaryKey (#9254)

This commit is contained in:
Joffrey JAFFEUX 2020-03-30 15:23:59 +02:00 committed by GitHub
parent 92e81d2ae5
commit 2b78bd01ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 24 deletions

View File

@ -27,6 +27,8 @@ function rethrow(error) {
}
export default EmberObject.extend({
primaryKey: "id",
storageKey(type, findArgs, options) {
if (options && options.cacheKey) {
return options.cacheKey;
@ -132,7 +134,7 @@ export default EmberObject.extend({
},
destroyRecord(store, type, record) {
return ajax(this.pathFor(store, type, record.get("id")), {
return ajax(this.pathFor(store, type, record.get(this.primaryKey)), {
type: "DELETE"
});
}

View File

@ -124,14 +124,14 @@ export default EmberObject.extend({
if (adapter.cache) {
const stale = adapter.findStale(this, type, findArgs, opts);
hydrated = this._updateStale(stale, hydrated);
hydrated = this._updateStale(stale, hydrated, adapter.primaryKey);
adapter.cacheFind(this, type, findArgs, opts, hydrated);
}
return hydrated;
});
},
_updateStale(stale, hydrated) {
_updateStale(stale, hydrated, primaryKey) {
if (!stale) {
return hydrated;
}
@ -139,7 +139,7 @@ export default EmberObject.extend({
hydrated.set(
"content",
hydrated.get("content").map(item => {
var staleItem = stale.content.findBy("id", item.get("id"));
var staleItem = stale.content.findBy(primaryKey, item.get(primaryKey));
if (staleItem) {
staleItem.setProperties(item);
} else {
@ -186,12 +186,11 @@ export default EmberObject.extend({
},
update(type, id, attrs) {
return this.adapterFor(type).update(this, type, id, attrs, function(
result
) {
if (result && result[type] && result[type].id) {
const adapter = this.adapterFor(type);
return adapter.update(this, type, id, attrs, function(result) {
if (result && result[type] && result[type][adapter.primaryKey]) {
const oldRecord = findAndRemoveMap(type, id);
storeMap(type, result[type].id, oldRecord);
storeMap(type, result[type][adapter.primaryKey], oldRecord);
}
return result;
});
@ -199,22 +198,25 @@ export default EmberObject.extend({
createRecord(type, attrs) {
attrs = attrs || {};
return !!attrs.id ? this._hydrate(type, attrs) : this._build(type, attrs);
const adapter = this.adapterFor(type);
return !!attrs[adapter.primaryKey]
? this._hydrate(type, attrs)
: this._build(type, attrs);
},
destroyRecord(type, record) {
const adapter = this.adapterFor(type);
// If the record is new, don't perform an Ajax call
if (record.get("isNew")) {
removeMap(type, record.get("id"));
removeMap(type, record.get(adapter.primaryKey));
return Promise.resolve(true);
}
return this.adapterFor(type)
.destroyRecord(this, type, record)
.then(function(result) {
removeMap(type, record.get("id"));
return result;
});
return adapter.destroyRecord(this, type, record).then(function(result) {
removeMap(type, record.get(adapter.primaryKey));
return result;
});
},
_resultSet(type, result, findArgs) {
@ -252,9 +254,10 @@ export default EmberObject.extend({
},
_build(type, obj) {
const adapter = this.adapterFor(type);
obj.store = this;
obj.__type = type;
obj.__state = obj.id ? "created" : "new";
obj.__state = obj[adapter.primaryKey] ? "created" : "new";
// TODO: Have injections be automatic
obj.topicTrackingState = this.register.lookup("topic-tracking-state:main");
@ -264,7 +267,7 @@ export default EmberObject.extend({
const klass = this.register.lookupFactory("model:" + type) || RestModel;
const model = klass.create(obj);
storeMap(type, obj.id, model);
storeMap(type, obj[adapter.primaryKey], model);
return model;
},
@ -288,6 +291,7 @@ export default EmberObject.extend({
subType = root.meta.types[subType] || subType;
}
const subTypeAdapter = this.adapterFor(subType);
const pluralType = this.pluralize(subType);
const collection = root[this.pluralize(subType)];
if (collection) {
@ -296,7 +300,7 @@ export default EmberObject.extend({
if (!hashedCollection) {
hashedCollection = {};
collection.forEach(function(it) {
hashedCollection[it.id] = it;
hashedCollection[it[subTypeAdapter.primaryKey]] = it;
});
root[hashedProp] = hashedCollection;
}
@ -311,7 +315,12 @@ export default EmberObject.extend({
},
_hydrateEmbedded(type, obj, root) {
const adapter = this.adapterFor(type);
Object.keys(obj).forEach(k => {
if (k === adapter.primaryKey) {
return;
}
const m = /(.+)\_id(s?)$/.exec(k);
if (m) {
const subType = m[1];
@ -340,9 +349,13 @@ export default EmberObject.extend({
throw new Error("Can't hydrate " + type + " of `null`");
}
const id = obj.id;
const adapter = this.adapterFor(type);
const id = obj[adapter.primaryKey];
if (!id) {
throw new Error("Can't hydrate " + type + " without an `id`");
throw new Error(
`Can't hydrate ${type} without primaryKey: \`${adapter.primaryKey}\``
);
}
root = root || obj;
@ -357,7 +370,7 @@ export default EmberObject.extend({
}
if (existing) {
delete obj.id;
delete obj[adapter.primaryKey];
let klass = this.register.lookupFactory("model:" + type);
if (klass && klass.class) {
@ -369,7 +382,7 @@ export default EmberObject.extend({
}
existing.setProperties(klass.munge(obj));
obj.id = id;
obj[adapter.primaryKey] = id;
return existing;
}

View File

@ -5,12 +5,21 @@ import TopicListAdapter from "discourse/adapters/topic-list";
import TopicTrackingState from "discourse/models/topic-tracking-state";
import { buildResolver } from "discourse-common/resolver";
const CatAdapter = RestAdapter.extend({
primaryKey: "cat_id"
});
export default function(customLookup = () => {}) {
const resolver = buildResolver("discourse").create();
return Store.create({
register: {
lookup(type) {
if (type === "adapter:cat") {
this._catAdapter =
this._catAdapter || CatAdapter.create({ owner: this });
return this._catAdapter;
}
if (type === "adapter:rest") {
if (!this._restAdapter) {
this._restAdapter = RestAdapter.create({ owner: this });

View File

@ -26,9 +26,23 @@ const colors = [
{ id: 3, name: "Yellow" }
];
const cats = [
{
cat_id: 1,
name: "souna"
}
];
export default function(helpers) {
const { response, success, parsePostData } = helpers;
this.get("/cats", function() {
return response({
__rest_serializer: "1",
cats
});
});
this.get("/fruits/:id", function(request) {
const fruit = fruits.find(f => f.id === parseInt(request.params.id, 10));
return response({ __rest_serializer: "1", fruit, farmers, colors });

View File

@ -182,3 +182,9 @@ QUnit.test("findAll embedded", async assert => {
assert.equal(fruits.objectAt(2).get("farmer.name"), "Luke Skywalker");
});
QUnit.test("custom primaryKey", async assert => {
const store = createStore();
const cats = await store.findAll("cat");
assert.equal(cats.objectAt(0).name, "souna");
});