mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-27 18:52:14 +03:00
Sorted out the mixed usages of include
and withRelated
(#9425)
no issue - this commit cleans up the usages of `include` and `withRelated`. ### API layer (`include`) - as request parameter e.g. `?include=roles,tags` - as theme API parameter e.g. `{{get .... include="author"}}` - as internal API access e.g. `api.posts.browse({include: 'author,tags'})` - the `include` notation is more readable than `withRelated` - and it allows us to use a different easier format (comma separated list) - the API utility transforms these more readable properties into model style (or into Ghost style) ### Model access (`withRelated`) - e.g. `models.Post.findPage({withRelated: ['tags']})` - driven by bookshelf --- Commits explained. * Reorder the usage of `convertOptions` - 1. validation - 2. options convertion - 3. permissions - the reason is simple, the permission layer access the model layer - we have to prepare the options before talking to the model layer - added `convertOptions` where it was missed (not required, but for consistency reasons) * Use `withRelated` when accessing the model layer and use `include` when accessing the API layer * Change `convertOptions` API utiliy - API Usage - ghost.api(..., {include: 'tags,authors'}) - `include` should only be used when calling the API (either via request or via manual usage) - `include` is only for readability and easier format - Ghost (Model Layer Usage) - models.Post.findOne(..., {withRelated: ['tags', 'authors']}) - should only use `withRelated` - model layer cannot read 'tags,authors` - model layer has no idea what `include` means, speaks a different language - `withRelated` is bookshelf - internal usage * include-count plugin: use `withRelated` instead of `include` - imagine you outsource this plugin to git and publish it to npm - `include` is an unknown option in bookshelf * Updated `permittedOptions` in base model - `include` is no longer a known option * Remove all occurances of `include` in the model layer * Extend `filterOptions` base function - this function should be called as first action - we clone the unfiltered options - check if you are using `include` (this is a protection which could help us in the beginning) - check for permitted and (later on default `withRelated`) options - the usage is coming in next commit * Ensure we call `filterOptions` as first action - use `ghostBookshelf.Model.filterOptions` as first action - consistent naming pattern for incoming options: `unfilteredOptions` - re-added allowed options for `toJSON` - one unsolved architecture problem: - if you override a function e.g. `edit` - then you should call `filterOptions` as first action - the base implementation of e.g. `edit` will call it again - future improvement * Removed `findOne` from Invite model - no longer needed, the base implementation is the same
This commit is contained in:
parent
d87fe38019
commit
c6a95c6478
@ -52,6 +52,7 @@ clients = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {attrs: attrs}),
|
||||
localUtils.convertOptions(),
|
||||
// TODO: add permissions
|
||||
// utils.handlePublicPermissions(docName, 'read'),
|
||||
doQuery
|
||||
|
@ -23,8 +23,8 @@ invites = {
|
||||
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.browseDefaultOptions}),
|
||||
localUtils.handlePublicPermissions(docName, 'browse'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePublicPermissions(docName, 'browse'),
|
||||
modelQuery
|
||||
];
|
||||
|
||||
@ -52,8 +52,8 @@ invites = {
|
||||
|
||||
tasks = [
|
||||
localUtils.validate(docName, {attrs: attrs}),
|
||||
localUtils.handlePublicPermissions(docName, 'read'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePublicPermissions(docName, 'read'),
|
||||
modelQuery
|
||||
];
|
||||
|
||||
@ -76,8 +76,8 @@ invites = {
|
||||
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions}),
|
||||
localUtils.handlePermissions(docName, 'destroy'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'destroy'),
|
||||
modelQuery
|
||||
];
|
||||
|
||||
@ -221,7 +221,7 @@ invites = {
|
||||
}
|
||||
|
||||
function fetchLoggedInUser(options) {
|
||||
return models.User.findOne({id: loggedInUser}, _.merge({}, _.omit(options, 'data'), {include: ['roles']}))
|
||||
return models.User.findOne({id: loggedInUser}, _.merge({}, _.omit(options, 'data'), {withRelated: ['roles']}))
|
||||
.then(function (user) {
|
||||
if (!user) {
|
||||
return Promise.reject(new common.errors.NotFoundError({message: common.i18n.t('errors.api.users.userNotFound')}));
|
||||
@ -234,8 +234,8 @@ invites = {
|
||||
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: ['email']}),
|
||||
localUtils.handlePermissions(docName, 'add'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'add'),
|
||||
fetchLoggedInUser,
|
||||
validation,
|
||||
checkIfUserExists,
|
||||
|
@ -59,8 +59,8 @@ posts = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: permittedOptions}),
|
||||
localUtils.handlePublicPermissions(docName, 'browse', unsafeAttrs),
|
||||
localUtils.convertOptions(allowedIncludes, models.Post.allowedFormats),
|
||||
localUtils.handlePublicPermissions(docName, 'browse', unsafeAttrs),
|
||||
modelQuery
|
||||
];
|
||||
|
||||
@ -106,8 +106,8 @@ posts = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {attrs: attrs, opts: extraAllowedOptions}),
|
||||
localUtils.handlePublicPermissions(docName, 'read', unsafeAttrs),
|
||||
localUtils.convertOptions(allowedIncludes, models.Post.allowedFormats),
|
||||
localUtils.handlePublicPermissions(docName, 'read', unsafeAttrs),
|
||||
modelQuery
|
||||
];
|
||||
|
||||
@ -162,8 +162,8 @@ posts = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions.concat(extraAllowedOptions)}),
|
||||
localUtils.handlePermissions(docName, 'edit', unsafeAttrs),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'edit', unsafeAttrs),
|
||||
modelQuery
|
||||
];
|
||||
|
||||
@ -206,8 +206,8 @@ posts = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName),
|
||||
localUtils.handlePermissions(docName, 'add', unsafeAttrs),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'add', unsafeAttrs),
|
||||
modelQuery
|
||||
];
|
||||
|
||||
@ -245,8 +245,8 @@ posts = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions}),
|
||||
localUtils.handlePermissions(docName, 'destroy', unsafeAttrs),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'destroy', unsafeAttrs),
|
||||
deletePost
|
||||
];
|
||||
|
||||
|
@ -66,6 +66,7 @@ roles = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: permittedOptions}),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'browse'),
|
||||
modelQuery
|
||||
];
|
||||
|
@ -73,6 +73,7 @@ slugs = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: opts, attrs: attrs}),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'generate'),
|
||||
checkAllowedTypes,
|
||||
modelQuery
|
||||
|
@ -38,6 +38,7 @@ subscribers = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.browseDefaultOptions}),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'browse'),
|
||||
doQuery
|
||||
];
|
||||
@ -79,6 +80,7 @@ subscribers = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {attrs: attrs}),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'read'),
|
||||
doQuery
|
||||
];
|
||||
@ -129,6 +131,7 @@ subscribers = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'add'),
|
||||
doQuery
|
||||
];
|
||||
@ -171,6 +174,7 @@ subscribers = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions}),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'edit'),
|
||||
doQuery
|
||||
];
|
||||
@ -224,6 +228,7 @@ subscribers = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: ['id', 'email']}),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'destroy'),
|
||||
getSubscriberByEmail,
|
||||
doQuery
|
||||
@ -279,6 +284,7 @@ subscribers = {
|
||||
}
|
||||
|
||||
tasks = [
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'browse'),
|
||||
exportSubscribers
|
||||
];
|
||||
@ -341,6 +347,7 @@ subscribers = {
|
||||
}
|
||||
|
||||
tasks = [
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'add'),
|
||||
importCSV
|
||||
];
|
||||
|
@ -37,8 +37,8 @@ tags = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.browseDefaultOptions}),
|
||||
localUtils.handlePublicPermissions(docName, 'browse'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePublicPermissions(docName, 'browse'),
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -79,8 +79,8 @@ tags = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {attrs: attrs}),
|
||||
localUtils.handlePublicPermissions(docName, 'read'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePublicPermissions(docName, 'read'),
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -114,8 +114,8 @@ tags = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName),
|
||||
localUtils.handlePermissions(docName, 'add'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'add'),
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -157,8 +157,8 @@ tags = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions}),
|
||||
localUtils.handlePermissions(docName, 'edit'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'edit'),
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -188,8 +188,8 @@ tags = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions}),
|
||||
localUtils.handlePermissions(docName, 'destroy'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePermissions(docName, 'destroy'),
|
||||
deleteTag
|
||||
];
|
||||
|
||||
|
@ -42,8 +42,8 @@ users = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: permittedOptions}),
|
||||
localUtils.handlePublicPermissions(docName, 'browse'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePublicPermissions(docName, 'browse'),
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -89,8 +89,8 @@ users = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {attrs: attrs}),
|
||||
localUtils.handlePublicPermissions(docName, 'read'),
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
localUtils.handlePublicPermissions(docName, 'read'),
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -153,7 +153,7 @@ users = {
|
||||
editedUserId = options.id;
|
||||
|
||||
return models.User.findOne(
|
||||
{id: options.context.user, status: 'all'}, {include: ['roles']}
|
||||
{id: options.context.user, status: 'all'}, {withRelated: ['roles']}
|
||||
).then(function (contextUser) {
|
||||
var contextRoleId = contextUser.related('roles').toJSON(options)[0].id;
|
||||
|
||||
@ -213,8 +213,8 @@ users = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: permittedOptions}),
|
||||
handlePermissions,
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
handlePermissions,
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -273,8 +273,8 @@ users = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions}),
|
||||
handlePermissions,
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
handlePermissions,
|
||||
deleteUser
|
||||
];
|
||||
|
||||
@ -343,8 +343,8 @@ users = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
validateRequest,
|
||||
handlePermissions,
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
handlePermissions,
|
||||
doQuery
|
||||
];
|
||||
|
||||
@ -395,8 +395,8 @@ users = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate('owner'),
|
||||
handlePermissions,
|
||||
localUtils.convertOptions(allowedIncludes),
|
||||
handlePermissions,
|
||||
doQuery
|
||||
];
|
||||
|
||||
|
@ -277,7 +277,8 @@ utils = {
|
||||
*/
|
||||
return function doConversion(options) {
|
||||
if (options.include) {
|
||||
options.include = utils.prepareInclude(options.include, allowedIncludes);
|
||||
options.withRelated = utils.prepareInclude(options.include, allowedIncludes);
|
||||
delete options.include;
|
||||
}
|
||||
|
||||
if (options.fields) {
|
||||
|
@ -92,6 +92,7 @@ webhooks = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'add'),
|
||||
doQuery
|
||||
];
|
||||
@ -122,6 +123,7 @@ webhooks = {
|
||||
// Push all of our tasks into a `tasks` array in the correct order
|
||||
tasks = [
|
||||
localUtils.validate(docName, {opts: localUtils.idDefaultOptions}),
|
||||
localUtils.convertOptions(),
|
||||
localUtils.handlePermissions(docName, 'destroy'),
|
||||
doQuery
|
||||
];
|
||||
|
@ -322,14 +322,14 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
*
|
||||
* `toJSON` calls `serialize`.
|
||||
*
|
||||
* @param options
|
||||
* @param unfilteredOptions
|
||||
* @returns {*}
|
||||
*/
|
||||
toJSON: function toJSON(options) {
|
||||
var opts = _.cloneDeep(options || {});
|
||||
opts.omitPivot = true;
|
||||
toJSON: function toJSON(unfilteredOptions) {
|
||||
var options = ghostBookshelf.Model.filterOptions(unfilteredOptions, 'toJSON');
|
||||
options.omitPivot = true;
|
||||
|
||||
return proto.toJSON.call(this, opts);
|
||||
return proto.toJSON.call(this, options);
|
||||
},
|
||||
|
||||
// Get attributes that have been updated (values before a .save() call)
|
||||
@ -389,9 +389,13 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
*
|
||||
* @return {Object} Keys allowed in the `options` hash of every model's method.
|
||||
*/
|
||||
permittedOptions: function permittedOptions() {
|
||||
permittedOptions: function permittedOptions(methodName) {
|
||||
if (methodName === 'toJSON') {
|
||||
return ['shallow', 'withRelated', 'context', 'columns'];
|
||||
}
|
||||
|
||||
// terms to whitelist for all methods.
|
||||
return ['context', 'include', 'transacting', 'importing', 'forUpdate'];
|
||||
return ['context', 'withRelated', 'transacting', 'importing', 'forUpdate'];
|
||||
},
|
||||
|
||||
/**
|
||||
@ -455,15 +459,29 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
|
||||
/**
|
||||
* Filters potentially unsafe `options` in a model method's arguments, so you can pass them to Bookshelf / Knex.
|
||||
* @param {Object} options Represents options to filter in order to be passed to the Bookshelf query.
|
||||
* @param {Object} unfilteredOptions Represents options to filter in order to be passed to the Bookshelf query.
|
||||
* @param {String} methodName The name of the method to check valid options for.
|
||||
* @return {Object} The filtered results of `options`.
|
||||
*/
|
||||
filterOptions: function filterOptions(options, methodName) {
|
||||
var permittedOptions = this.permittedOptions(methodName, options),
|
||||
filteredOptions = _.pick(options, permittedOptions);
|
||||
filterOptions: function filterOptions(unfilteredOptions, methodName, filterConfig) {
|
||||
unfilteredOptions = unfilteredOptions || {};
|
||||
filterConfig = filterConfig || {};
|
||||
|
||||
return filteredOptions;
|
||||
if (unfilteredOptions.hasOwnProperty('include')) {
|
||||
throw new common.errors.IncorrectUsageError({
|
||||
message: 'The model layer expects using `withRelated`.'
|
||||
});
|
||||
}
|
||||
|
||||
var options = _.cloneDeep(unfilteredOptions),
|
||||
extraAllowedProperties = filterConfig.extraAllowedProperties || [],
|
||||
permittedOptions;
|
||||
|
||||
permittedOptions = this.permittedOptions(methodName, options);
|
||||
permittedOptions = _.union(permittedOptions, extraAllowedProperties);
|
||||
options = _.pick(options, permittedOptions);
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
// ## Model Data Functions
|
||||
@ -471,14 +489,12 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
/**
|
||||
* ### Find All
|
||||
* Fetches all the data for a particular model
|
||||
* @param {Object} options (optional)
|
||||
* @param {Object} unfilteredOptions (optional)
|
||||
* @return {Promise(ghostBookshelf.Collection)} Collection of all Models
|
||||
*/
|
||||
findAll: function findAll(options) {
|
||||
options = this.filterOptions(options, 'findAll');
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
var itemCollection = this.forge();
|
||||
findAll: function findAll(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'findAll'),
|
||||
itemCollection = this.forge();
|
||||
|
||||
// transforms fictive keywords like 'all' (status:all) into correct allowed values
|
||||
if (this.processOptions) {
|
||||
@ -488,9 +504,9 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
itemCollection.applyDefaultAndCustomFilters(options);
|
||||
|
||||
return itemCollection.fetchAll(options).then(function then(result) {
|
||||
if (options.include) {
|
||||
if (options.withRelated) {
|
||||
_.each(result.models, function each(item) {
|
||||
item.include = options.include;
|
||||
item.withRelated = options.withRelated;
|
||||
});
|
||||
}
|
||||
|
||||
@ -516,12 +532,10 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
* total: __
|
||||
* }
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Object} unfilteredOptions
|
||||
*/
|
||||
findPage: function findPage(options) {
|
||||
options = options || {};
|
||||
|
||||
var self = this,
|
||||
findPage: function findPage(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'findPage'),
|
||||
itemCollection = this.forge(),
|
||||
tableName = _.result(this.prototype, 'tableName'),
|
||||
requestedColumns = options.columns;
|
||||
@ -529,9 +543,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
// Set this to true or pass ?debug=true as an API option to get output
|
||||
itemCollection.debug = options.debug && config.get('env') !== 'production';
|
||||
|
||||
// Filter options so that only permitted ones remain
|
||||
options = this.filterOptions(options, 'findPage');
|
||||
|
||||
// This applies default properties like 'staticPages' and 'status'
|
||||
// And then converts them to 'where' options... this behaviour is effectively deprecated in favour
|
||||
// of using filter - it's only be being kept here so that we can transition cleanly.
|
||||
@ -540,10 +551,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
// Add Filter behaviour
|
||||
itemCollection.applyDefaultAndCustomFilters(options);
|
||||
|
||||
// Handle related objects
|
||||
// TODO: this should just be done for all methods @ the API level
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
// Ensure only valid fields/columns are added to query
|
||||
// and append default columns to fetch
|
||||
if (options.columns) {
|
||||
@ -552,16 +559,16 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
}
|
||||
|
||||
if (options.order) {
|
||||
options.order = self.parseOrderOption(options.order, options.include);
|
||||
} else if (self.orderDefaultRaw) {
|
||||
options.orderRaw = self.orderDefaultRaw();
|
||||
options.order = this.parseOrderOption(options.order, options.withRelated);
|
||||
} else if (this.orderDefaultRaw) {
|
||||
options.orderRaw = this.orderDefaultRaw();
|
||||
} else {
|
||||
options.order = self.orderDefaultOptions();
|
||||
options.order = this.orderDefaultOptions();
|
||||
}
|
||||
|
||||
return itemCollection.fetchPage(options).then(function formatResponse(response) {
|
||||
var data = {},
|
||||
models = [];
|
||||
models;
|
||||
|
||||
options.columns = requestedColumns;
|
||||
models = response.collection.toJSON(options);
|
||||
@ -571,6 +578,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
data[tableName] = _.map(models, function transform(model) {
|
||||
return options.columns ? _.pick(model, options.columns) : model;
|
||||
});
|
||||
|
||||
data.meta = {pagination: response.pagination};
|
||||
return data;
|
||||
});
|
||||
@ -580,13 +588,12 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
* ### Find One
|
||||
* Naive find one where data determines what to match on
|
||||
* @param {Object} data
|
||||
* @param {Object} options (optional)
|
||||
* @param {Object} unfilteredOptions (optional)
|
||||
* @return {Promise(ghostBookshelf.Model)} Single Model
|
||||
*/
|
||||
findOne: function findOne(data, options) {
|
||||
findOne: function findOne(data, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'findOne');
|
||||
data = this.filterData(data);
|
||||
options = this.filterOptions(options, 'findOne');
|
||||
|
||||
return this.forge(data).fetch(options);
|
||||
},
|
||||
|
||||
@ -598,15 +605,15 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
* Based on the `method` option Bookshelf and Ghost can determine if a query is an insert or an update.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {Object} options (optional)
|
||||
* @param {Object} unfilteredOptions (optional)
|
||||
* @return {Promise(ghostBookshelf.Model)} Edited Model
|
||||
*/
|
||||
edit: function edit(data, options) {
|
||||
var id = options.id,
|
||||
edit: function edit(data, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'edit', {extraAllowedProperties: ['id']}),
|
||||
id = options.id,
|
||||
model = this.forge({id: id});
|
||||
|
||||
data = this.filterData(data);
|
||||
options = this.filterOptions(options, 'edit');
|
||||
|
||||
// We allow you to disable timestamps when run migration, so that the posts `updated_at` value is the same
|
||||
if (options.importing) {
|
||||
@ -624,13 +631,15 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
* ### Add
|
||||
* Naive add
|
||||
* @param {Object} data
|
||||
* @param {Object} options (optional)
|
||||
* @param {Object} unfilteredOptions (optional)
|
||||
* @return {Promise(ghostBookshelf.Model)} Newly Added Model
|
||||
*/
|
||||
add: function add(data, options) {
|
||||
add: function add(data, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'add'),
|
||||
model;
|
||||
|
||||
data = this.filterData(data);
|
||||
options = this.filterOptions(options, 'add');
|
||||
var model = this.forge(data);
|
||||
model = this.forge(data);
|
||||
|
||||
// We allow you to disable timestamps when importing posts so that the new posts `updated_at` value is the same
|
||||
// as the import json blob. More details refer to https://github.com/TryGhost/Ghost/issues/1696
|
||||
@ -647,17 +656,19 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
/**
|
||||
* ### Destroy
|
||||
* Naive destroy
|
||||
* @param {Object} options (optional)
|
||||
* @param {Object} unfilteredOptions (optional)
|
||||
* @return {Promise(ghostBookshelf.Model)} Empty Model
|
||||
*/
|
||||
destroy: function destroy(options) {
|
||||
var id = options.id;
|
||||
options = this.filterOptions(options, 'destroy');
|
||||
destroy: function destroy(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'destroy', {extraAllowedProperties: ['id']}),
|
||||
id = options.id;
|
||||
|
||||
// Fetch the object before destroying it, so that the changed data is available to events
|
||||
return this.forge({id: id}).fetch(options).then(function then(obj) {
|
||||
return obj.destroy(options);
|
||||
});
|
||||
return this.forge({id: id})
|
||||
.fetch(options)
|
||||
.then(function then(obj) {
|
||||
return obj.destroy(options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -757,11 +768,11 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
});
|
||||
},
|
||||
|
||||
parseOrderOption: function (order, include) {
|
||||
parseOrderOption: function (order, withRelated) {
|
||||
var permittedAttributes, result, rules;
|
||||
|
||||
permittedAttributes = this.prototype.permittedAttributes();
|
||||
if (include && include.indexOf('count.posts') > -1) {
|
||||
if (withRelated && withRelated.indexOf('count.posts') > -1) {
|
||||
permittedAttributes.push('count.posts');
|
||||
}
|
||||
result = {};
|
||||
|
@ -21,8 +21,9 @@ Basetoken = ghostBookshelf.Model.extend({
|
||||
}
|
||||
|
||||
}, {
|
||||
destroyAllExpired: function destroyAllExpired(options) {
|
||||
options = this.filterOptions(options, 'destroyAll');
|
||||
destroyAllExpired: function destroyAllExpired(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'destroyAll');
|
||||
|
||||
return ghostBookshelf.Collection.forge([], {model: this})
|
||||
.query('where', 'expires', '<', Date.now())
|
||||
.fetch(options)
|
||||
@ -33,12 +34,11 @@ Basetoken = ghostBookshelf.Model.extend({
|
||||
|
||||
/**
|
||||
* ### destroyByUser
|
||||
* @param {[type]} options has context and id. Context is the user doing the destroy, id is the user to destroy
|
||||
* @param {[type]} unfilteredOptions has context and id. Context is the user doing the destroy, id is the user to destroy
|
||||
*/
|
||||
destroyByUser: function destroyByUser(options) {
|
||||
var userId = options.id;
|
||||
|
||||
options = this.filterOptions(options, 'destroyByUser');
|
||||
destroyByUser: function destroyByUser(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'destroyByUser', {extraAllowedProperties: ['id']}),
|
||||
userId = options.id;
|
||||
|
||||
if (userId) {
|
||||
return ghostBookshelf.Collection.forge([], {model: this})
|
||||
@ -54,12 +54,12 @@ Basetoken = ghostBookshelf.Model.extend({
|
||||
|
||||
/**
|
||||
* ### destroyByToken
|
||||
* @param {[type]} options has token where token is the token to destroy
|
||||
* @param {[type]} unfilteredOptions has token where token is the token to destroy
|
||||
*/
|
||||
destroyByToken: function destroyByToken(options) {
|
||||
var token = options.token;
|
||||
destroyByToken: function destroyByToken(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'destroyByToken', {extraAllowedProperties: ['token']}),
|
||||
token = options.token;
|
||||
|
||||
options = this.filterOptions(options, 'destroyByUser');
|
||||
options.require = true;
|
||||
|
||||
return this.forge()
|
||||
|
@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto'),
|
||||
_ = require('lodash'),
|
||||
constants = require('../lib/constants'),
|
||||
ghostBookshelf = require('./base');
|
||||
|
||||
@ -11,10 +10,10 @@ let Invite,
|
||||
Invite = ghostBookshelf.Model.extend({
|
||||
tableName: 'invites',
|
||||
|
||||
toJSON: function (options) {
|
||||
options = options || {};
|
||||
toJSON: function (unfilteredOptions) {
|
||||
var options = Invite.filterOptions(unfilteredOptions, 'toJSON'),
|
||||
attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
||||
|
||||
var attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
||||
delete attrs.token;
|
||||
return attrs;
|
||||
}
|
||||
@ -27,31 +26,10 @@ Invite = ghostBookshelf.Model.extend({
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* @TODO: can't use base class, because:
|
||||
* options.withRelated = _.union(options.withRelated, options.include); is missing
|
||||
* there are some weird self implementations in each model
|
||||
* so adding this line, will destroy other models, because they rely on something else
|
||||
* FIX ME!!!!!
|
||||
*/
|
||||
findOne: function findOne(data, options) {
|
||||
options = options || {};
|
||||
|
||||
options = this.filterOptions(options, 'findOne');
|
||||
data = this.filterData(data, 'findOne');
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
var invite = this.forge(data);
|
||||
return invite.fetch(options);
|
||||
},
|
||||
|
||||
add: function add(data, options) {
|
||||
var hash = crypto.createHash('sha256'),
|
||||
text = '';
|
||||
|
||||
options = this.filterOptions(options, 'add');
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
data.expires = Date.now() + constants.ONE_WEEK_MS;
|
||||
data.status = 'pending';
|
||||
|
||||
@ -60,6 +38,7 @@ Invite = ghostBookshelf.Model.extend({
|
||||
hash.update(data.email.toLocaleLowerCase());
|
||||
text += [data.expires, data.email, hash.digest('base64')].join('|');
|
||||
data.token = new Buffer(text).toString('base64');
|
||||
|
||||
return ghostBookshelf.Model.add.call(this, data, options);
|
||||
}
|
||||
});
|
||||
|
@ -51,8 +51,8 @@ module.exports = function (Bookshelf) {
|
||||
|
||||
var tableName = _.result(this, 'tableName');
|
||||
|
||||
if (options.include && options.include.indexOf('count.posts') > -1) {
|
||||
// remove post_count from withRelated and include
|
||||
if (options.withRelated && options.withRelated.indexOf('count.posts') > -1) {
|
||||
// remove post_count from withRelated
|
||||
options.withRelated = _.pull([].concat(options.withRelated), 'count.posts');
|
||||
|
||||
// Call the query builder
|
||||
|
@ -350,10 +350,9 @@ Post = ghostBookshelf.Model.extend({
|
||||
return attrs;
|
||||
},
|
||||
|
||||
toJSON: function toJSON(options) {
|
||||
options = options || {};
|
||||
|
||||
var attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options),
|
||||
toJSON: function toJSON(unfilteredOptions) {
|
||||
var options = Post.filterOptions(unfilteredOptions, 'toJSON'),
|
||||
attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options),
|
||||
oldPostId = attrs.amp,
|
||||
commentId;
|
||||
|
||||
@ -520,8 +519,6 @@ Post = ghostBookshelf.Model.extend({
|
||||
* **See:** [ghostBookshelf.Model.findOne](base.js.html#Find%20One)
|
||||
*/
|
||||
findOne: function findOne(data, options) {
|
||||
options = options || {};
|
||||
|
||||
data = _.defaults(data || {}, {
|
||||
status: 'published'
|
||||
});
|
||||
@ -530,8 +527,6 @@ Post = ghostBookshelf.Model.extend({
|
||||
delete data.status;
|
||||
}
|
||||
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
return ghostBookshelf.Model.findOne.call(this, data, options);
|
||||
},
|
||||
|
||||
@ -542,15 +537,15 @@ Post = ghostBookshelf.Model.extend({
|
||||
* @extends ghostBookshelf.Model.edit to handle returning the full object and manage _updatedAttributes
|
||||
* **See:** [ghostBookshelf.Model.edit](base.js.html#edit)
|
||||
*/
|
||||
edit: function edit(data, options) {
|
||||
let opts = _.cloneDeep(options || {});
|
||||
edit: function edit(data, unfilteredOptions) {
|
||||
let options = this.filterOptions(unfilteredOptions, 'edit', {extraAllowedProperties: ['id']});
|
||||
|
||||
const editPost = () => {
|
||||
opts.forUpdate = true;
|
||||
options.forUpdate = true;
|
||||
|
||||
return ghostBookshelf.Model.edit.call(this, data, opts)
|
||||
return ghostBookshelf.Model.edit.call(this, data, options)
|
||||
.then((post) => {
|
||||
return this.findOne({status: 'all', id: opts.id}, opts)
|
||||
return this.findOne({status: 'all', id: options.id}, options)
|
||||
.then((found) => {
|
||||
if (found) {
|
||||
// Pass along the updated attributes for checking status changes
|
||||
@ -561,9 +556,9 @@ Post = ghostBookshelf.Model.extend({
|
||||
});
|
||||
};
|
||||
|
||||
if (!opts.transacting) {
|
||||
if (!options.transacting) {
|
||||
return ghostBookshelf.transaction((transacting) => {
|
||||
opts.transacting = transacting;
|
||||
options.transacting = transacting;
|
||||
return editPost();
|
||||
});
|
||||
}
|
||||
@ -576,19 +571,19 @@ Post = ghostBookshelf.Model.extend({
|
||||
* @extends ghostBookshelf.Model.add to handle returning the full object
|
||||
* **See:** [ghostBookshelf.Model.add](base.js.html#add)
|
||||
*/
|
||||
add: function add(data, options) {
|
||||
let opts = _.cloneDeep(options || {});
|
||||
add: function add(data, unfilteredOptions) {
|
||||
let options = this.filterOptions(unfilteredOptions, 'add', {extraAllowedProperties: ['id']});
|
||||
|
||||
const addPost = (() => {
|
||||
return ghostBookshelf.Model.add.call(this, data, opts)
|
||||
return ghostBookshelf.Model.add.call(this, data, options)
|
||||
.then((post) => {
|
||||
return this.findOne({status: 'all', id: post.id}, opts);
|
||||
return this.findOne({status: 'all', id: post.id}, options);
|
||||
});
|
||||
});
|
||||
|
||||
if (!opts.transacting) {
|
||||
if (!options.transacting) {
|
||||
return ghostBookshelf.transaction((transacting) => {
|
||||
opts.transacting = transacting;
|
||||
options.transacting = transacting;
|
||||
|
||||
return addPost();
|
||||
});
|
||||
@ -597,16 +592,16 @@ Post = ghostBookshelf.Model.extend({
|
||||
return addPost();
|
||||
},
|
||||
|
||||
destroy: function destroy(options) {
|
||||
let opts = _.cloneDeep(options || {});
|
||||
destroy: function destroy(unfilteredOptions) {
|
||||
let options = this.filterOptions(unfilteredOptions, 'destroy', {extraAllowedProperties: ['id']});
|
||||
|
||||
const destroyPost = () => {
|
||||
return ghostBookshelf.Model.destroy.call(this, opts);
|
||||
return ghostBookshelf.Model.destroy.call(this, options);
|
||||
};
|
||||
|
||||
if (!opts.transacting) {
|
||||
if (!options.transacting) {
|
||||
return ghostBookshelf.transaction((transacting) => {
|
||||
opts.transacting = transacting;
|
||||
options.transacting = transacting;
|
||||
return destroyPost();
|
||||
});
|
||||
}
|
||||
@ -618,13 +613,10 @@ Post = ghostBookshelf.Model.extend({
|
||||
* ### destroyByAuthor
|
||||
* @param {[type]} options has context and id. Context is the user doing the destroy, id is the user to destroy
|
||||
*/
|
||||
destroyByAuthor: function destroyByAuthor(options) {
|
||||
let opts = _.cloneDeep(options || {});
|
||||
|
||||
let postCollection = Posts.forge(),
|
||||
authorId = opts.id;
|
||||
|
||||
opts = this.filterOptions(opts, 'destroyByAuthor');
|
||||
destroyByAuthor: function destroyByAuthor(unfilteredOptions) {
|
||||
let options = this.filterOptions(unfilteredOptions, 'destroyByAuthor', {extraAllowedProperties: ['id']}),
|
||||
postCollection = Posts.forge(),
|
||||
authorId = options.id;
|
||||
|
||||
if (!authorId) {
|
||||
throw new common.errors.NotFoundError({
|
||||
@ -635,16 +627,16 @@ Post = ghostBookshelf.Model.extend({
|
||||
const destroyPost = (() => {
|
||||
return postCollection
|
||||
.query('where', 'author_id', '=', authorId)
|
||||
.fetch(opts)
|
||||
.call('invokeThen', 'destroy', opts)
|
||||
.fetch(options)
|
||||
.call('invokeThen', 'destroy', options)
|
||||
.catch((err) => {
|
||||
throw new common.errors.GhostError({err: err});
|
||||
});
|
||||
});
|
||||
|
||||
if (!opts.transacting) {
|
||||
if (!options.transacting) {
|
||||
return ghostBookshelf.transaction((transacting) => {
|
||||
opts.transacting = transacting;
|
||||
options.transacting = transacting;
|
||||
return destroyPost();
|
||||
});
|
||||
}
|
||||
|
@ -98,9 +98,9 @@ Settings = ghostBookshelf.Model.extend({
|
||||
return Promise.resolve(ghostBookshelf.Model.findOne.call(this, data, options));
|
||||
},
|
||||
|
||||
edit: function (data, options) {
|
||||
var self = this;
|
||||
options = this.filterOptions(options, 'edit');
|
||||
edit: function (data, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'edit'),
|
||||
self = this;
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
data = [data];
|
||||
@ -146,10 +146,13 @@ Settings = ghostBookshelf.Model.extend({
|
||||
});
|
||||
},
|
||||
|
||||
populateDefaults: function populateDefaults(options) {
|
||||
var self = this;
|
||||
populateDefaults: function populateDefaults(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'populateDefaults'),
|
||||
self = this;
|
||||
|
||||
options = _.merge({}, options || {}, internalContext);
|
||||
if (!options.context) {
|
||||
options.context = internalContext.context;
|
||||
}
|
||||
|
||||
return this
|
||||
.findAll(options)
|
||||
|
@ -75,11 +75,11 @@ Subscriber = ghostBookshelf.Model.extend({
|
||||
},
|
||||
|
||||
// TODO: This is a copy paste of models/user.js!
|
||||
getByEmail: function getByEmail(email, options) {
|
||||
options = options || {};
|
||||
getByEmail: function getByEmail(email, unfilteredOptions) {
|
||||
var options = ghostBookshelf.Model.filterOptions(unfilteredOptions, 'getByEmail');
|
||||
options.require = true;
|
||||
|
||||
return Subscribers.forge(options).fetch(options).then(function then(subscribers) {
|
||||
return Subscribers.forge().fetch(options).then(function then(subscribers) {
|
||||
var subscriberWithEmail = subscribers.find(function findSubscriber(subscriber) {
|
||||
return subscriber.get('email').toLowerCase() === email.toLowerCase();
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
var _ = require('lodash'),
|
||||
ghostBookshelf = require('./base'),
|
||||
var ghostBookshelf = require('./base'),
|
||||
common = require('../lib/common'),
|
||||
Tag,
|
||||
Tags;
|
||||
@ -54,10 +53,9 @@ Tag = ghostBookshelf.Model.extend({
|
||||
return this.belongsToMany('Post');
|
||||
},
|
||||
|
||||
toJSON: function toJSON(options) {
|
||||
options = options || {};
|
||||
|
||||
var attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
||||
toJSON: function toJSON(unfilteredOptions) {
|
||||
var options = Tag.filterOptions(unfilteredOptions, 'toJSON'),
|
||||
attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
||||
|
||||
attrs.parent = attrs.parent || attrs.parent_id;
|
||||
delete attrs.parent_id;
|
||||
@ -94,29 +92,11 @@ Tag = ghostBookshelf.Model.extend({
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
* ### Find One
|
||||
* @overrides ghostBookshelf.Model.findOne
|
||||
*/
|
||||
findOne: function findOne(data, options) {
|
||||
options = options || {};
|
||||
destroy: function destroy(unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'destroy', {extraAllowedProperties: ['id']});
|
||||
options.withRelated = ['posts'];
|
||||
|
||||
options = this.filterOptions(options, 'findOne');
|
||||
data = this.filterData(data, 'findOne');
|
||||
|
||||
var tag = this.forge(data);
|
||||
|
||||
// Add related objects
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
return tag.fetch(options);
|
||||
},
|
||||
|
||||
destroy: function destroy(options) {
|
||||
var id = options.id;
|
||||
options = this.filterOptions(options, 'destroy');
|
||||
|
||||
return this.forge({id: id}).fetch({withRelated: ['posts']}).then(function destroyTagsAndPost(tag) {
|
||||
return this.forge({id: options.id}).fetch(options).then(function destroyTagsAndPost(tag) {
|
||||
return tag.related('posts').detach().then(function destroyTags() {
|
||||
return tag.destroy(options);
|
||||
});
|
||||
|
@ -201,10 +201,9 @@ User = ghostBookshelf.Model.extend({
|
||||
return validation.validateSchema(this.tableName, userData);
|
||||
},
|
||||
|
||||
toJSON: function toJSON(options) {
|
||||
options = options || {};
|
||||
|
||||
var attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
||||
toJSON: function toJSON(unfilteredOptions) {
|
||||
var options = User.filterOptions(unfilteredOptions, 'toJSON'),
|
||||
attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);
|
||||
|
||||
// remove password hash for security reasons
|
||||
delete attrs.password;
|
||||
@ -341,11 +340,11 @@ User = ghostBookshelf.Model.extend({
|
||||
permittedOptionsToReturn = permittedOptionsToReturn.concat(validOptions[methodName]);
|
||||
}
|
||||
|
||||
// CASE: The `include` parameter is allowed when using the public API, but not the `roles` value.
|
||||
// CASE: The `withRelated` parameter is allowed when using the public API, but not the `roles` value.
|
||||
// Otherwise we expose too much information.
|
||||
if (options && options.context && options.context.public) {
|
||||
if (options.include && options.include.indexOf('roles') !== -1) {
|
||||
options.include.splice(options.include.indexOf('roles'), 1);
|
||||
if (options.withRelated && options.withRelated.indexOf('roles') !== -1) {
|
||||
options.withRelated.splice(options.withRelated.indexOf('roles'), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,11 +357,14 @@ User = ghostBookshelf.Model.extend({
|
||||
* We have to clone the data, because we remove values from this object.
|
||||
* This is not expected from outside!
|
||||
*
|
||||
* @TODO: use base class
|
||||
*
|
||||
* @extends ghostBookshelf.Model.findOne to include roles
|
||||
* **See:** [ghostBookshelf.Model.findOne](base.js.html#Find%20One)
|
||||
*/
|
||||
findOne: function findOne(dataToClone, options) {
|
||||
var query,
|
||||
findOne: function findOne(dataToClone, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'findOne'),
|
||||
query,
|
||||
status,
|
||||
data = _.cloneDeep(dataToClone),
|
||||
lookupRole = data.role;
|
||||
@ -376,15 +378,12 @@ User = ghostBookshelf.Model.extend({
|
||||
delete data.status;
|
||||
|
||||
data = this.filterData(data);
|
||||
options = this.filterOptions(options, 'findOne');
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
// Support finding by role
|
||||
if (lookupRole) {
|
||||
options.withRelated = _.union(options.withRelated, ['roles']);
|
||||
options.include = _.union(options.include, ['roles']);
|
||||
|
||||
query = this.forge(data);
|
||||
|
||||
query.query('join', 'roles_users', 'users.id', '=', 'roles_users.user_id');
|
||||
query.query('join', 'roles', 'roles_users.role_id', '=', 'roles.id');
|
||||
query.query('where', 'roles.name', '=', lookupRole);
|
||||
@ -409,8 +408,9 @@ User = ghostBookshelf.Model.extend({
|
||||
* @extends ghostBookshelf.Model.edit to handle returning the full object
|
||||
* **See:** [ghostBookshelf.Model.edit](base.js.html#edit)
|
||||
*/
|
||||
edit: function edit(data, options) {
|
||||
var self = this,
|
||||
edit: function edit(data, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'edit'),
|
||||
self = this,
|
||||
ops = [];
|
||||
|
||||
if (data.roles && data.roles.length > 1) {
|
||||
@ -419,9 +419,6 @@ User = ghostBookshelf.Model.extend({
|
||||
);
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
if (data.email) {
|
||||
ops.push(function checkForDuplicateEmail() {
|
||||
return self.getByEmail(data.email, options).then(function then(user) {
|
||||
@ -476,19 +473,17 @@ User = ghostBookshelf.Model.extend({
|
||||
* This is not expected from outside!
|
||||
*
|
||||
* @param {object} dataToClone
|
||||
* @param {object} options
|
||||
* @param {object} unfilteredOptions
|
||||
* @extends ghostBookshelf.Model.add to manage all aspects of user signup
|
||||
* **See:** [ghostBookshelf.Model.add](base.js.html#Add)
|
||||
*/
|
||||
add: function add(dataToClone, options) {
|
||||
var self = this,
|
||||
add: function add(dataToClone, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'add'),
|
||||
self = this,
|
||||
data = _.cloneDeep(dataToClone),
|
||||
userData = this.filterData(data),
|
||||
roles;
|
||||
|
||||
options = this.filterOptions(options, 'add');
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
// check for too many roles
|
||||
if (data.roles && data.roles.length > 1) {
|
||||
return Promise.reject(new common.errors.ValidationError({message: common.i18n.t('errors.models.user.onlyOneRolePerUserSupported')}));
|
||||
@ -556,8 +551,9 @@ User = ghostBookshelf.Model.extend({
|
||||
* Owner already has a slug -> force setting a new one by setting slug to null
|
||||
* @TODO: kill setup function?
|
||||
*/
|
||||
setup: function setup(data, options) {
|
||||
var self = this,
|
||||
setup: function setup(data, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'setup'),
|
||||
self = this,
|
||||
userData = this.filterData(data),
|
||||
passwordValidation = {};
|
||||
|
||||
@ -567,9 +563,6 @@ User = ghostBookshelf.Model.extend({
|
||||
return Promise.reject(new common.errors.ValidationError({message: passwordValidation.message}));
|
||||
}
|
||||
|
||||
options = this.filterOptions(options, 'setup');
|
||||
options.withRelated = _.union(options.withRelated, options.include);
|
||||
|
||||
userData.slug = null;
|
||||
return self.edit(userData, options);
|
||||
},
|
||||
@ -624,7 +617,7 @@ User = ghostBookshelf.Model.extend({
|
||||
return this.findOne({
|
||||
id: userModelOrId,
|
||||
status: 'all'
|
||||
}, {include: ['roles']}).then(function then(foundUserModel) {
|
||||
}, {withRelated: ['roles']}).then(function then(foundUserModel) {
|
||||
if (!foundUserModel) {
|
||||
throw new common.errors.NotFoundError({
|
||||
message: common.i18n.t('errors.models.user.userNotFound')
|
||||
@ -753,17 +746,20 @@ User = ghostBookshelf.Model.extend({
|
||||
/**
|
||||
* Naive change password method
|
||||
* @param {Object} object
|
||||
* @param {Object} options
|
||||
* @param {Object} unfilteredOptions
|
||||
*/
|
||||
changePassword: function changePassword(object, options) {
|
||||
var self = this,
|
||||
changePassword: function changePassword(object, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'changePassword'),
|
||||
self = this,
|
||||
newPassword = object.newPassword,
|
||||
userId = object.user_id,
|
||||
oldPassword = object.oldPassword,
|
||||
isLoggedInUser = userId === options.context.user,
|
||||
user;
|
||||
|
||||
return self.forge({id: userId}).fetch({require: true})
|
||||
options.require = true;
|
||||
|
||||
return self.forge({id: userId}).fetch(options)
|
||||
.then(function then(_user) {
|
||||
user = _user;
|
||||
|
||||
@ -779,13 +775,14 @@ User = ghostBookshelf.Model.extend({
|
||||
});
|
||||
},
|
||||
|
||||
transferOwnership: function transferOwnership(object, options) {
|
||||
var ownerRole,
|
||||
transferOwnership: function transferOwnership(object, unfilteredOptions) {
|
||||
var options = ghostBookshelf.Model.filterOptions(unfilteredOptions, 'transferOwnership'),
|
||||
ownerRole,
|
||||
contextUser;
|
||||
|
||||
return Promise.join(
|
||||
ghostBookshelf.model('Role').findOne({name: 'Owner'}),
|
||||
User.findOne({id: options.context.user}, {include: ['roles']})
|
||||
User.findOne({id: options.context.user}, {withRelated: ['roles']})
|
||||
)
|
||||
.then(function then(results) {
|
||||
ownerRole = results[0];
|
||||
@ -798,7 +795,7 @@ User = ghostBookshelf.Model.extend({
|
||||
}
|
||||
|
||||
return Promise.join(ghostBookshelf.model('Role').findOne({name: 'Administrator'}),
|
||||
User.findOne({id: object.id}, {include: ['roles']}));
|
||||
User.findOne({id: object.id}, {withRelated: ['roles']}));
|
||||
})
|
||||
.then(function then(results) {
|
||||
var adminRole = results[0],
|
||||
@ -820,7 +817,7 @@ User = ghostBookshelf.Model.extend({
|
||||
.fetch({withRelated: ['roles']});
|
||||
})
|
||||
.then(function then(users) {
|
||||
options.include = ['roles'];
|
||||
options.withRelated = ['roles'];
|
||||
return users.toJSON(options);
|
||||
});
|
||||
},
|
||||
@ -828,15 +825,16 @@ User = ghostBookshelf.Model.extend({
|
||||
// Get the user by email address, enforces case insensitivity rejects if the user is not found
|
||||
// When multi-user support is added, email addresses must be deduplicated with case insensitivity, so that
|
||||
// joe@bloggs.com and JOE@BLOGGS.COM cannot be created as two separate users.
|
||||
getByEmail: function getByEmail(email, options) {
|
||||
options = options || {};
|
||||
getByEmail: function getByEmail(email, unfilteredOptions) {
|
||||
var options = ghostBookshelf.Model.filterOptions(unfilteredOptions, 'getByEmail');
|
||||
|
||||
// We fetch all users and process them in JS as there is no easy way to make this query across all DBs
|
||||
// Although they all support `lower()`, sqlite can't case transform unicode characters
|
||||
// This is somewhat mute, as validator.isEmail() also doesn't support unicode, but this is much easier / more
|
||||
// likely to be fixed in the near future.
|
||||
options.require = true;
|
||||
|
||||
return Users.forge(options).fetch(options).then(function then(users) {
|
||||
return Users.forge().fetch(options).then(function then(users) {
|
||||
var userWithEmail = users.find(function findUser(user) {
|
||||
return user.get('email').toLowerCase() === email.toLowerCase();
|
||||
});
|
||||
|
@ -25,21 +25,20 @@ Webhook = ghostBookshelf.Model.extend({
|
||||
model.emitChange('deleted', options);
|
||||
}
|
||||
}, {
|
||||
findAllByEvent: function findAllByEvent(event, options) {
|
||||
var webhooksCollection = Webhooks.forge();
|
||||
|
||||
options = this.filterOptions(options, 'findAll');
|
||||
findAllByEvent: function findAllByEvent(event, unfilteredOptions) {
|
||||
var options = this.filterOptions(unfilteredOptions, 'findAll'),
|
||||
webhooksCollection = Webhooks.forge();
|
||||
|
||||
return webhooksCollection
|
||||
.query('where', 'event', '=', event)
|
||||
.fetch(options);
|
||||
},
|
||||
|
||||
getByEventAndTarget: function getByEventAndTarget(event, targetUrl, options) {
|
||||
options = options || {};
|
||||
getByEventAndTarget: function getByEventAndTarget(event, targetUrl, unfilteredOptions) {
|
||||
var options = ghostBookshelf.Model.filterOptions(unfilteredOptions, 'getByEventAndTarget');
|
||||
options.require = true;
|
||||
|
||||
return Webhooks.forge(options).fetch(options).then(function then(webhooks) {
|
||||
return Webhooks.forge().fetch(options).then(function then(webhooks) {
|
||||
var webhookWithEventAndTarget = webhooks.find(function findWebhook(webhook) {
|
||||
return webhook.get('event').toLowerCase() === event.toLowerCase()
|
||||
&& webhook.get('target_url').toLowerCase() === targetUrl.toLowerCase();
|
||||
|
@ -19,7 +19,7 @@ strategies = {
|
||||
return models.Client.findOne({slug: clientId}, {withRelated: ['trustedDomains']})
|
||||
.then(function then(model) {
|
||||
if (model) {
|
||||
var client = model.toJSON({include: ['trustedDomains']});
|
||||
var client = model.toJSON({withRelated: ['trustedDomains']});
|
||||
if (client.status === 'enabled' && client.secret === clientSecret) {
|
||||
return done(null, client);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ var _ = require('lodash'),
|
||||
|
||||
module.exports = {
|
||||
user: function (id) {
|
||||
return models.User.findOne({id: id, status: 'all'}, {include: ['permissions', 'roles', 'roles.permissions']})
|
||||
return models.User.findOne({id: id, status: 'all'}, {withRelated: ['permissions', 'roles', 'roles.permissions']})
|
||||
.then(function (foundUser) {
|
||||
// CASE: {context: {user: id}} where the id is not in our database
|
||||
if (!foundUser) {
|
||||
|
@ -286,7 +286,7 @@ describe('Authentication API', function () {
|
||||
should.not.exist(_invite);
|
||||
return models.User.findOne({
|
||||
email: invite.get('email')
|
||||
}, _.merge({include: ['roles']}, context.internal));
|
||||
}, _.merge({withRelated: ['roles']}, context.internal));
|
||||
})
|
||||
.then(function (user) {
|
||||
user.toJSON().roles.length.should.eql(1);
|
||||
|
@ -317,7 +317,7 @@ describe('Invites API', function () {
|
||||
role_id: testUtils.roles.ids.admin
|
||||
}
|
||||
]
|
||||
}, _.merge({}, {include: ['roles']}, testUtils.context.admin)).then(function (response) {
|
||||
}, _.merge({}, {include: 'roles'}, testUtils.context.admin)).then(function (response) {
|
||||
checkAddResponse(response);
|
||||
response.invites[0].role_id.should.equal(testUtils.roles.ids.admin);
|
||||
done();
|
||||
|
@ -864,7 +864,7 @@ describe('Users API', function () {
|
||||
return models.Post.findAll(_.merge({}, {
|
||||
context: context.editor.context,
|
||||
filter: 'author_id:' + userIdFor.editor,
|
||||
include: ['tags']
|
||||
withRelated: ['tags']
|
||||
}, options));
|
||||
}).then(function (posts) {
|
||||
posts.models.length.should.eql(3);
|
||||
@ -877,7 +877,7 @@ describe('Users API', function () {
|
||||
return models.Post.findAll(_.merge({
|
||||
context: context.author.context,
|
||||
filter: 'author_id:' + userIdFor.author,
|
||||
include: ['tags']
|
||||
withRelated: ['tags']
|
||||
}, options));
|
||||
}).then(function (posts) {
|
||||
posts.models.length.should.eql(3);
|
||||
|
@ -534,7 +534,7 @@ describe('Import', function () {
|
||||
// Grab the data from tables
|
||||
// NOTE: we have to return sorted data, sqlite can insert the posts in a different order
|
||||
return Promise.all([
|
||||
models.Post.findPage({include: ['tags']}),
|
||||
models.Post.findPage({withRelated: ['tags']}),
|
||||
models.Tag.findAll()
|
||||
]);
|
||||
}).then(function (importedData) {
|
||||
|
@ -170,16 +170,16 @@ describe('Database Migration (special functions)', function () {
|
||||
|
||||
it('should populate all fixtures correctly', function () {
|
||||
var props = {
|
||||
posts: Models.Post.findAll({include: ['tags']}),
|
||||
posts: Models.Post.findAll({withRelated: ['tags']}),
|
||||
tags: Models.Tag.findAll(),
|
||||
users: Models.User.findAll({
|
||||
filter: 'status:inactive',
|
||||
context: {internal: true},
|
||||
include: ['roles']
|
||||
withRelated: ['roles']
|
||||
}),
|
||||
clients: Models.Client.findAll(),
|
||||
roles: Models.Role.findAll(),
|
||||
permissions: Models.Permission.findAll({include: ['roles']})
|
||||
permissions: Models.Permission.findAll({withRelated: ['roles']})
|
||||
};
|
||||
|
||||
return Promise.props(props).then(function (result) {
|
||||
|
@ -127,7 +127,7 @@ describe('Permission Model', function () {
|
||||
// return testUser.permissions().attach(testPermission);
|
||||
// });
|
||||
// }).then(function () {
|
||||
// return Models.User.findOne({id: 1}, { include: ['permissions']});
|
||||
// return Models.User.findOne({id: 1}, { withRelated: ['permissions']});
|
||||
// }).then(function (updatedUser) {
|
||||
// should.exist(updatedUser);
|
||||
|
||||
|
@ -105,7 +105,7 @@ describe('Post Model', function () {
|
||||
});
|
||||
|
||||
it('can findAll, returning all related data', function (done) {
|
||||
PostModel.findAll({include: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
PostModel.findAll({withRelated: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(0);
|
||||
@ -123,7 +123,7 @@ describe('Post Model', function () {
|
||||
it('can findAll, use formats option', function (done) {
|
||||
var options = {
|
||||
formats: ['mobiledoc', 'plaintext'],
|
||||
include: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']
|
||||
withRelated: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']
|
||||
};
|
||||
|
||||
PostModel.findAll(options)
|
||||
@ -165,7 +165,7 @@ describe('Post Model', function () {
|
||||
});
|
||||
|
||||
it('can findPage, returning all related data', function (done) {
|
||||
PostModel.findPage({include: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
PostModel.findPage({withRelated: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
@ -361,7 +361,7 @@ describe('Post Model', function () {
|
||||
it('can findOne, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findOne({}, {include: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
PostModel.findOne({}, {withRelated: ['author', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (result) {
|
||||
should.exist(result);
|
||||
firstPost = result.toJSON();
|
||||
@ -1417,7 +1417,7 @@ describe('Post Model', function () {
|
||||
var firstItemData = {id: testUtils.DataGenerator.Content.posts[0].id};
|
||||
|
||||
// Test that we have the post we expect, with exactly one tag
|
||||
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
|
||||
PostModel.findOne(firstItemData, {withRelated: ['tags']}).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
post = results.toJSON();
|
||||
@ -1456,7 +1456,7 @@ describe('Post Model', function () {
|
||||
var firstItemData = {id: testUtils.DataGenerator.Content.posts[3].id, status: 'draft'};
|
||||
|
||||
// Test that we have the post we expect, with exactly one tag
|
||||
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
|
||||
PostModel.findOne(firstItemData, {withRelated: ['tags']}).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
post = results.toJSON();
|
||||
@ -1493,7 +1493,7 @@ describe('Post Model', function () {
|
||||
var firstItemData = {id: testUtils.DataGenerator.Content.posts[5].id};
|
||||
|
||||
// Test that we have the post we expect, with exactly one tag
|
||||
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
|
||||
PostModel.findOne(firstItemData, {withRelated: ['tags']}).then(function (results) {
|
||||
var page;
|
||||
should.exist(results);
|
||||
page = results.toJSON();
|
||||
@ -1531,7 +1531,7 @@ describe('Post Model', function () {
|
||||
var firstItemData = {id: testUtils.DataGenerator.Content.posts[6].id, status: 'draft'};
|
||||
|
||||
// Test that we have the post we expect, with exactly one tag
|
||||
PostModel.findOne(firstItemData, {include: ['tags']}).then(function (results) {
|
||||
PostModel.findOne(firstItemData, {withRelated: ['tags']}).then(function (results) {
|
||||
var page;
|
||||
should.exist(results);
|
||||
page = results.toJSON();
|
||||
@ -1724,7 +1724,7 @@ describe('Post Model', function () {
|
||||
tag2: TagModel.add(extraTags[1], context),
|
||||
tag3: TagModel.add(extraTags[2], context)
|
||||
}).then(function (result) {
|
||||
postJSON = result.post.toJSON({include: ['tags']});
|
||||
postJSON = result.post.toJSON({withRelated: ['tags']});
|
||||
tagJSON.push(result.tag1.toJSON());
|
||||
tagJSON.push(result.tag2.toJSON());
|
||||
tagJSON.push(result.tag3.toJSON());
|
||||
@ -1765,7 +1765,7 @@ describe('Post Model', function () {
|
||||
|
||||
// Edit the post
|
||||
return PostModel.edit(newJSON, editOptions).then(function (updatedPost) {
|
||||
updatedPost = updatedPost.toJSON({include: ['tags']});
|
||||
updatedPost = updatedPost.toJSON({withRelated: ['tags']});
|
||||
|
||||
updatedPost.tags.should.have.lengthOf(1);
|
||||
updatedPost.tags[0].name.should.eql(postJSON.tags[0].name);
|
||||
@ -1793,7 +1793,7 @@ describe('Post Model', function () {
|
||||
return PostModel.edit(newJSON, editOptions);
|
||||
})
|
||||
.then(function (updatedPost) {
|
||||
updatedPost = updatedPost.toJSON({include: ['tags']});
|
||||
updatedPost = updatedPost.toJSON({withRelated: ['tags']});
|
||||
|
||||
updatedPost.tags.should.have.lengthOf(1);
|
||||
updatedPost.tags[0].should.have.properties({
|
||||
@ -1823,7 +1823,7 @@ describe('Post Model', function () {
|
||||
|
||||
// Edit the post
|
||||
return PostModel.edit(newJSON, editOptions).then(function (updatedPost) {
|
||||
updatedPost = updatedPost.toJSON({include: ['tags']});
|
||||
updatedPost = updatedPost.toJSON({withRelated: ['tags']});
|
||||
|
||||
updatedPost.tags.should.have.lengthOf(3);
|
||||
updatedPost.tags[0].should.have.properties({
|
||||
@ -1853,7 +1853,7 @@ describe('Post Model', function () {
|
||||
|
||||
// Edit the post
|
||||
return PostModel.edit(newJSON, editOptions).then(function (updatedPost) {
|
||||
updatedPost = updatedPost.toJSON({include: ['tags']});
|
||||
updatedPost = updatedPost.toJSON({withRelated: ['tags']});
|
||||
|
||||
updatedPost.tags.should.have.lengthOf(3);
|
||||
|
||||
@ -1873,7 +1873,7 @@ describe('Post Model', function () {
|
||||
|
||||
// Edit the post
|
||||
return PostModel.edit(newJSON, editOptions).then(function (updatedPost) {
|
||||
updatedPost = updatedPost.toJSON({include: ['tags']});
|
||||
updatedPost = updatedPost.toJSON({withRelated: ['tags']});
|
||||
|
||||
updatedPost.tags.should.have.lengthOf(1);
|
||||
});
|
||||
|
@ -47,7 +47,7 @@ describe('Tag Model', function () {
|
||||
|
||||
it('returns count.posts if include count.posts', function (done) {
|
||||
testUtils.fixtures.insertPostsAndTags().then(function () {
|
||||
TagModel.findOne({slug: 'kitchen-sink'}, {include: 'count.posts'}).then(function (tag) {
|
||||
TagModel.findOne({slug: 'kitchen-sink'}, {withRelated: ['count.posts']}).then(function (tag) {
|
||||
should.exist(tag);
|
||||
tag.toJSON().count.posts.should.equal(2);
|
||||
|
||||
@ -75,7 +75,7 @@ describe('Tag Model', function () {
|
||||
});
|
||||
|
||||
it('with include count.posts', function (done) {
|
||||
TagModel.findPage({limit: 'all', include: 'count.posts'}).then(function (results) {
|
||||
TagModel.findPage({limit: 'all', withRelated: ['count.posts']}).then(function (results) {
|
||||
results.meta.pagination.page.should.equal(1);
|
||||
results.meta.pagination.limit.should.equal('all');
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
|
@ -348,7 +348,7 @@ describe('User Model', function run() {
|
||||
RoleModel.findOne().then(function (role) {
|
||||
userData.roles = [role.toJSON()];
|
||||
|
||||
return UserModel.add(userData, _.extend({}, context, {include: ['roles']}));
|
||||
return UserModel.add(userData, _.extend({}, context, {withRelated: ['roles']}));
|
||||
}).then(function (createdUser) {
|
||||
should.exist(createdUser);
|
||||
createdUser.get('password').should.not.equal(userData.password, 'password was hashed');
|
||||
@ -371,7 +371,7 @@ describe('User Model', function run() {
|
||||
RoleModel.findOne().then(function (role) {
|
||||
userData.roles = [role.toJSON()];
|
||||
|
||||
return UserModel.add(userData, _.extend({}, context, {include: ['roles']}));
|
||||
return UserModel.add(userData, _.extend({}, context, {withRelated: ['roles']}));
|
||||
}).then(function () {
|
||||
done(new Error('User was created with an invalid email address'));
|
||||
}).catch(function () {
|
||||
|
@ -298,14 +298,15 @@ describe('API Utils', function () {
|
||||
allowed = ['a', 'b', 'c'],
|
||||
options = {include: 'a,b'},
|
||||
actualResult;
|
||||
|
||||
actualResult = apiUtils.convertOptions(allowed)(_.clone(options));
|
||||
|
||||
prepareIncludeStub.calledOnce.should.be.true();
|
||||
prepareIncludeStub.calledWith(options.include, allowed).should.be.true();
|
||||
|
||||
actualResult.should.have.hasOwnProperty('include');
|
||||
actualResult.include.should.be.an.Array();
|
||||
actualResult.include.should.eql(expectedResult);
|
||||
actualResult.should.have.hasOwnProperty('withRelated');
|
||||
actualResult.withRelated.should.be.an.Array();
|
||||
actualResult.withRelated.should.eql(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -48,8 +48,9 @@ describe('Permission Providers', function () {
|
||||
roles: fakeAdminRole,
|
||||
permissions: fakeAdminRolePermissions
|
||||
};
|
||||
|
||||
// We use this inside toJSON.
|
||||
fakeUser.include = ['roles', 'permissions', 'roles.permissions'];
|
||||
fakeUser.withRelated = ['roles', 'permissions', 'roles.permissions'];
|
||||
|
||||
return Promise.resolve(fakeUser);
|
||||
});
|
||||
@ -96,7 +97,7 @@ describe('Permission Providers', function () {
|
||||
roles: fakeAdminRole
|
||||
};
|
||||
// We use this inside toJSON.
|
||||
fakeUser.include = ['roles', 'permissions', 'roles.permissions'];
|
||||
fakeUser.withRelated = ['roles', 'permissions', 'roles.permissions'];
|
||||
|
||||
return Promise.resolve(fakeUser);
|
||||
});
|
||||
@ -144,7 +145,7 @@ describe('Permission Providers', function () {
|
||||
permissions: fakeAdminRolePermissions
|
||||
};
|
||||
// We use this inside toJSON.
|
||||
fakeUser.include = ['roles', 'permissions', 'roles.permissions'];
|
||||
fakeUser.withRelated = ['roles', 'permissions', 'roles.permissions'];
|
||||
|
||||
return Promise.resolve(fakeUser);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user