mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-17 23:44:39 -05:00
Merge pull request #4721 from jaswilli/adapters
Make embedded-relation-adapter the default
This commit is contained in:
commit
75e081dbf0
5 changed files with 124 additions and 137 deletions
|
@ -1,44 +1,5 @@
|
||||||
import ghostPaths from 'ghost/utils/ghost-paths';
|
import EmbeddedRelationAdapter from 'ghost/adapters/embedded-relation-adapter';
|
||||||
|
|
||||||
var ApplicationAdapter = DS.RESTAdapter.extend({
|
var ApplicationAdapter = EmbeddedRelationAdapter.extend();
|
||||||
host: window.location.origin,
|
|
||||||
namespace: ghostPaths().apiRoot.slice(1),
|
|
||||||
|
|
||||||
findQuery: function (store, type, query) {
|
|
||||||
var id;
|
|
||||||
|
|
||||||
if (query.id) {
|
|
||||||
id = query.id;
|
|
||||||
delete query.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ajax(this.buildURL(type.typeKey, id), 'GET', {data: query});
|
|
||||||
},
|
|
||||||
|
|
||||||
buildURL: function (type, id) {
|
|
||||||
// Ensure trailing slashes
|
|
||||||
var url = this._super(type, id);
|
|
||||||
|
|
||||||
if (url.slice(-1) !== '/') {
|
|
||||||
url += '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override deleteRecord to disregard the response body on 2xx responses.
|
|
||||||
// This is currently needed because the API is returning status 200 along
|
|
||||||
// with the JSON object for the deleted entity and Ember expects an empty
|
|
||||||
// response body for successful DELETEs.
|
|
||||||
// Non-2xx (failure) responses will still work correctly as Ember will turn
|
|
||||||
// them into rejected promises.
|
|
||||||
deleteRecord: function () {
|
|
||||||
var response = this._super.apply(this, arguments);
|
|
||||||
|
|
||||||
return response.then(function () {
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ApplicationAdapter;
|
export default ApplicationAdapter;
|
||||||
|
|
44
core/client/adapters/base.js
Normal file
44
core/client/adapters/base.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import ghostPaths from 'ghost/utils/ghost-paths';
|
||||||
|
|
||||||
|
var BaseAdapter = DS.RESTAdapter.extend({
|
||||||
|
host: window.location.origin,
|
||||||
|
namespace: ghostPaths().apiRoot.slice(1),
|
||||||
|
|
||||||
|
findQuery: function (store, type, query) {
|
||||||
|
var id;
|
||||||
|
|
||||||
|
if (query.id) {
|
||||||
|
id = query.id;
|
||||||
|
delete query.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ajax(this.buildURL(type.typeKey, id), 'GET', {data: query});
|
||||||
|
},
|
||||||
|
|
||||||
|
buildURL: function (type, id) {
|
||||||
|
// Ensure trailing slashes
|
||||||
|
var url = this._super(type, id);
|
||||||
|
|
||||||
|
if (url.slice(-1) !== '/') {
|
||||||
|
url += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Override deleteRecord to disregard the response body on 2xx responses.
|
||||||
|
// This is currently needed because the API is returning status 200 along
|
||||||
|
// with the JSON object for the deleted entity and Ember expects an empty
|
||||||
|
// response body for successful DELETEs.
|
||||||
|
// Non-2xx (failure) responses will still work correctly as Ember will turn
|
||||||
|
// them into rejected promises.
|
||||||
|
deleteRecord: function () {
|
||||||
|
var response = this._super.apply(this, arguments);
|
||||||
|
|
||||||
|
return response.then(function () {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default BaseAdapter;
|
|
@ -1,16 +1,18 @@
|
||||||
import ApplicationAdapter from 'ghost/adapters/application';
|
import BaseAdapter from 'ghost/adapters/base';
|
||||||
|
|
||||||
// EmbeddedRelationAdapter will augment the query object in calls made to
|
// EmbeddedRelationAdapter will augment the query object in calls made to
|
||||||
// DS.Store#find, findQuery, and findAll with the correct "includes"
|
// DS.Store#find, findQuery, and findAll with the correct "includes"
|
||||||
// (?include=relatedType) by introspecting on the provided subclass of the DS.Model.
|
// (?include=relatedType) by introspecting on the provided subclass of the DS.Model.
|
||||||
|
// In cases where there is no query object (DS.Model#save, or simple finds) the URL
|
||||||
|
// that is built will be augmented with ?include=... where appropriate.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
// If a model has an embedded hasMany relation, the related type will be included:
|
// If a model has an embedded hasMany relation, the related type will be included:
|
||||||
// roles: DS.hasMany('role', { embedded: 'always' }) => ?include=roles
|
// roles: DS.hasMany('role', { embedded: 'always' }) => ?include=roles
|
||||||
|
|
||||||
var EmbeddedRelationAdapter = ApplicationAdapter.extend({
|
var EmbeddedRelationAdapter = BaseAdapter.extend({
|
||||||
find: function (store, type, id) {
|
find: function (store, type, id) {
|
||||||
return this.findQuery(store, type, this.buildQuery(store, type, id));
|
return this.ajax(this.buildIncludeURL(store, type, id), 'GET');
|
||||||
},
|
},
|
||||||
|
|
||||||
findQuery: function (store, type, query) {
|
findQuery: function (store, type, query) {
|
||||||
|
@ -18,31 +20,66 @@ var EmbeddedRelationAdapter = ApplicationAdapter.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
findAll: function (store, type, sinceToken) {
|
findAll: function (store, type, sinceToken) {
|
||||||
return this.findQuery(store, type, this.buildQuery(store, type, sinceToken));
|
var query = {};
|
||||||
|
|
||||||
|
if (sinceToken) {
|
||||||
|
query.since = sinceToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.findQuery(store, type, query);
|
||||||
|
},
|
||||||
|
|
||||||
|
createRecord: function (store, type, record) {
|
||||||
|
return this.saveRecord(store, type, record, {method: 'POST'});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRecord: function (store, type, record) {
|
||||||
|
var options = {
|
||||||
|
method: 'PUT',
|
||||||
|
id: Ember.get(record, 'id')
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.saveRecord(store, type, record, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
saveRecord: function (store, type, record, options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var url = this.buildIncludeURL(store, type, options.id),
|
||||||
|
payload = this.preparePayload(store, type, record);
|
||||||
|
|
||||||
|
return this.ajax(url, options.method, payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
preparePayload: function (store, type, record) {
|
||||||
|
var serializer = store.serializerFor(type.typeKey),
|
||||||
|
payload = {};
|
||||||
|
|
||||||
|
serializer.serializeIntoHash(payload, type, record);
|
||||||
|
|
||||||
|
return {data: payload};
|
||||||
|
},
|
||||||
|
|
||||||
|
buildIncludeURL: function (store, type, id) {
|
||||||
|
var url = this.buildURL(type.typeKey, id),
|
||||||
|
includes = this.getEmbeddedRelations(store, type);
|
||||||
|
|
||||||
|
if (includes.length) {
|
||||||
|
url += '?include=' + includes.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
},
|
},
|
||||||
|
|
||||||
buildQuery: function (store, type, options) {
|
buildQuery: function (store, type, options) {
|
||||||
var model,
|
var toInclude = this.getEmbeddedRelations(store, type),
|
||||||
toInclude = [],
|
query = options || {},
|
||||||
query = {},
|
|
||||||
deDupe = {};
|
deDupe = {};
|
||||||
|
|
||||||
// Get the class responsible for creating records of this type
|
|
||||||
model = store.modelFor(type);
|
|
||||||
|
|
||||||
// Iterate through the model's relationships and build a list
|
|
||||||
// of those that need to be pulled in via "include" from the API
|
|
||||||
model.eachRelationship(function (name, meta) {
|
|
||||||
if (meta.kind === 'hasMany' &&
|
|
||||||
Object.prototype.hasOwnProperty.call(meta.options, 'embedded') &&
|
|
||||||
meta.options.embedded === 'always') {
|
|
||||||
toInclude.push(name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (toInclude.length) {
|
if (toInclude.length) {
|
||||||
// If this is a find by id, build a query object and attach the includes
|
// If this is a find by id, build a query object and attach the includes
|
||||||
if (typeof options === 'string' || typeof options === 'number') {
|
if (typeof options === 'string' || typeof options === 'number') {
|
||||||
|
query = {};
|
||||||
query.id = options;
|
query.id = options;
|
||||||
query.include = toInclude.join(',');
|
query.include = toInclude.join(',');
|
||||||
} else if (typeof options === 'object' || Ember.isNone(options)) {
|
} else if (typeof options === 'object' || Ember.isNone(options)) {
|
||||||
|
@ -50,7 +87,7 @@ var EmbeddedRelationAdapter = ApplicationAdapter.extend({
|
||||||
// the includes.
|
// the includes.
|
||||||
// If this is a find with an existing query object then merge the includes
|
// If this is a find with an existing query object then merge the includes
|
||||||
// into the existing object. Existing properties and includes are preserved.
|
// into the existing object. Existing properties and includes are preserved.
|
||||||
query = options || query;
|
query = query || {};
|
||||||
toInclude = toInclude.concat(query.include ? query.include.split(',') : []);
|
toInclude = toInclude.concat(query.include ? query.include.split(',') : []);
|
||||||
|
|
||||||
toInclude.forEach(function (include) {
|
toInclude.forEach(function (include) {
|
||||||
|
@ -62,6 +99,23 @@ var EmbeddedRelationAdapter = ApplicationAdapter.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEmbeddedRelations: function (store, type) {
|
||||||
|
var model = store.modelFor(type),
|
||||||
|
ret = [];
|
||||||
|
|
||||||
|
// Iterate through the model's relationships and build a list
|
||||||
|
// of those that need to be pulled in via "include" from the API
|
||||||
|
model.eachRelationship(function (name, meta) {
|
||||||
|
if (meta.kind === 'hasMany' &&
|
||||||
|
Object.prototype.hasOwnProperty.call(meta.options, 'embedded') &&
|
||||||
|
meta.options.embedded === 'always') {
|
||||||
|
ret.push(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
import EmbeddedRelationAdapter from 'ghost/adapters/embedded-relation-adapter';
|
|
||||||
|
|
||||||
var PostAdapter = EmbeddedRelationAdapter.extend({
|
|
||||||
createRecord: function (store, type, record) {
|
|
||||||
var data = {},
|
|
||||||
serializer = store.serializerFor(type.typeKey),
|
|
||||||
url = this.buildURL(type.typeKey);
|
|
||||||
|
|
||||||
// make the server return with the tags embedded
|
|
||||||
url = url + '?include=tags';
|
|
||||||
|
|
||||||
// use the PostSerializer to transform the model back into
|
|
||||||
// an array with a post object like the API expects
|
|
||||||
serializer.serializeIntoHash(data, type, record);
|
|
||||||
|
|
||||||
return this.ajax(url, 'POST', {data: data});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateRecord: function (store, type, record) {
|
|
||||||
var data = {},
|
|
||||||
serializer = store.serializerFor(type.typeKey),
|
|
||||||
id = Ember.get(record, 'id'),
|
|
||||||
url = this.buildURL(type.typeKey, id);
|
|
||||||
|
|
||||||
// make the server return with the tags embedded
|
|
||||||
url = url + '?include=tags';
|
|
||||||
|
|
||||||
// use the PostSerializer to transform the model back into
|
|
||||||
// an array of posts objects like the API expects
|
|
||||||
serializer.serializeIntoHash(data, type, record);
|
|
||||||
|
|
||||||
// use the ApplicationAdapter's buildURL method
|
|
||||||
return this.ajax(url, 'PUT', {data: data});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default PostAdapter;
|
|
|
@ -1,43 +1,8 @@
|
||||||
import EmbeddedRelationAdapter from 'ghost/adapters/embedded-relation-adapter';
|
import ApplicationAdapter from 'ghost/adapters/application';
|
||||||
|
|
||||||
var UserAdapter = EmbeddedRelationAdapter.extend({
|
|
||||||
createRecord: function (store, type, record) {
|
|
||||||
var data = {},
|
|
||||||
serializer = store.serializerFor(type.typeKey),
|
|
||||||
url = this.buildURL(type.typeKey);
|
|
||||||
|
|
||||||
// Ask the API to include full role objects in its response
|
|
||||||
url += '?include=roles';
|
|
||||||
|
|
||||||
// Use the UserSerializer to transform the model back into
|
|
||||||
// an array of user objects like the API expects
|
|
||||||
serializer.serializeIntoHash(data, type, record);
|
|
||||||
|
|
||||||
// Use the url from the ApplicationAdapter's buildURL method
|
|
||||||
return this.ajax(url, 'POST', {data: data});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateRecord: function (store, type, record) {
|
|
||||||
var data = {},
|
|
||||||
serializer = store.serializerFor(type.typeKey),
|
|
||||||
id = Ember.get(record, 'id'),
|
|
||||||
url = this.buildURL(type.typeKey, id);
|
|
||||||
|
|
||||||
// Ask the API to include full role objects in its response
|
|
||||||
url += '?include=roles';
|
|
||||||
|
|
||||||
// Use the UserSerializer to transform the model back into
|
|
||||||
// an array of user objects like the API expects
|
|
||||||
serializer.serializeIntoHash(data, type, record);
|
|
||||||
|
|
||||||
// Use the url from the ApplicationAdapter's buildURL method
|
|
||||||
return this.ajax(url, 'PUT', {data: data});
|
|
||||||
},
|
|
||||||
|
|
||||||
|
var UserAdapter = ApplicationAdapter.extend({
|
||||||
find: function (store, type, id) {
|
find: function (store, type, id) {
|
||||||
var url = this.buildQuery(store, type, id);
|
return this.findQuery(store, type, {id: id, status: 'all'});
|
||||||
url.status = 'all';
|
|
||||||
return this.findQuery(store, type, url);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue