FIX: allows adapters to define a custom primaryKey ()

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
app/assets/javascripts/discourse
adapters
models
test/javascripts

@ -27,6 +27,8 @@ function rethrow(error) {
} }
export default EmberObject.extend({ export default EmberObject.extend({
primaryKey: "id",
storageKey(type, findArgs, options) { storageKey(type, findArgs, options) {
if (options && options.cacheKey) { if (options && options.cacheKey) {
return options.cacheKey; return options.cacheKey;
@ -132,7 +134,7 @@ export default EmberObject.extend({
}, },
destroyRecord(store, type, record) { 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" type: "DELETE"
}); });
} }

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

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

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

@ -182,3 +182,9 @@ QUnit.test("findAll embedded", async assert => {
assert.equal(fruits.objectAt(2).get("farmer.name"), "Luke Skywalker"); 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");
});