diff --git a/core/server/api/utils.js b/core/server/api/utils.js index a48ef98e3a..4e030435af 100644 --- a/core/server/api/utils.js +++ b/core/server/api/utils.js @@ -23,7 +23,7 @@ utils = { // ### Manual Default Options // These must be provided by the endpoint // browseDefaultOptions - valid for all browse api endpoints - browseDefaultOptions: ['page', 'limit', 'fields', 'filter', 'order'], + browseDefaultOptions: ['page', 'limit', 'fields', 'filter', 'order', 'debug'], // idDefaultOptions - valid whenever an id is valid idDefaultOptions: ['id'], diff --git a/core/server/models/base/index.js b/core/server/models/base/index.js index 7ac7875274..d94e03fc9c 100644 --- a/core/server/models/base/index.js +++ b/core/server/models/base/index.js @@ -275,6 +275,9 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ itemCollection = this.forge(null, {context: options.context}), tableName = _.result(this.prototype, 'tableName'); + // Set this to true or pass ?debug=true as an API option to get output + itemCollection.debug = options.debug && process.env.NODE_ENV !== 'production'; + // Filter options so that only permitted ones remain options = this.filterOptions(options, 'findPage'); diff --git a/core/server/models/plugins/filter.js b/core/server/models/plugins/filter.js index dfb49e3dec..a31ffb4f15 100644 --- a/core/server/models/plugins/filter.js +++ b/core/server/models/plugins/filter.js @@ -143,6 +143,10 @@ filter = function filter(Bookshelf) { } if (this._filters) { + if (this.debug) { + gql.json.printStatements(this._filters.statements); + } + this.query(function (qb) { gql.knexify(qb, self._filters); }); diff --git a/core/server/models/plugins/include-count.js b/core/server/models/plugins/include-count.js index b9ef5af6d8..f28ff3011e 100644 --- a/core/server/models/plugins/include-count.js +++ b/core/server/models/plugins/include-count.js @@ -38,12 +38,20 @@ module.exports = function (Bookshelf) { fetch: function () { this.addCounts.apply(this, arguments); + if (this.debug) { + console.log('QUERY', this.query().toQuery()); + } + // Call parent fetch return modelProto.fetch.apply(this, arguments); }, fetchAll: function () { this.addCounts.apply(this, arguments); + if (this.debug) { + console.log('QUERY', this.query().toQuery()); + } + // Call parent fetchAll return modelProto.fetchAll.apply(this, arguments); } diff --git a/core/server/models/plugins/pagination.js b/core/server/models/plugins/pagination.js index 66623a4cc5..a5aee72168 100644 --- a/core/server/models/plugins/pagination.js +++ b/core/server/models/plugins/pagination.js @@ -172,6 +172,10 @@ pagination = function pagination(bookshelf) { }); } + if (this.debug) { + console.log('COUNT', countPromise.toQuery()); + } + // Setup the promise to do a fetch on our collection, running the specified query // @TODO: ensure option handling is done using an explicit pick elsewhere collectionPromise = self.fetchAll(_.omit(options, ['page', 'limit'])); diff --git a/core/test/unit/api_utils_spec.js b/core/test/unit/api_utils_spec.js index 6ec4487058..dd3dbcc473 100644 --- a/core/test/unit/api_utils_spec.js +++ b/core/test/unit/api_utils_spec.js @@ -18,7 +18,7 @@ describe('API Utils', function () { describe('Default Options', function () { it('should provide a set of default options', function () { apiUtils.globalDefaultOptions.should.eql(['context', 'include']); - apiUtils.browseDefaultOptions.should.eql(['page', 'limit', 'fields', 'filter', 'order']); + apiUtils.browseDefaultOptions.should.eql(['page', 'limit', 'fields', 'filter', 'order', 'debug']); apiUtils.dataDefaultOptions.should.eql(['data']); apiUtils.idDefaultOptions.should.eql(['id']); }); diff --git a/core/test/unit/models_plugins/filter_spec.js b/core/test/unit/models_plugins/filter_spec.js index 62e95926f0..a7f421fe4e 100644 --- a/core/test/unit/models_plugins/filter_spec.js +++ b/core/test/unit/models_plugins/filter_spec.js @@ -206,6 +206,19 @@ describe('Filter', function () { {prop: 'title', op: '=', value: 'Hello Word'} ]}); }); + + it('should print statements in debug mode', function () { + ghostBookshelf.Model.prototype.debug = true; + ghostBookshelf.Model.prototype._filters = {statements: [ + {prop: 'tags', op: 'IN', value: ['photo', 'video']} + ]}; + + ghostBookshelf.Model.prototype.applyFilters(); + filterGQL.json.printStatements.calledOnce.should.be.true; + filterGQL.json.printStatements.firstCall.args[0].should.eql([ + {prop: 'tags', op: 'IN', value: ['photo', 'video']} + ]); + }); }); describe('Post Process Filters', function () { diff --git a/core/test/unit/models_plugins/pagination_spec.js b/core/test/unit/models_plugins/pagination_spec.js index 008505ab83..24a9d9301f 100644 --- a/core/test/unit/models_plugins/pagination_spec.js +++ b/core/test/unit/models_plugins/pagination_spec.js @@ -190,7 +190,8 @@ describe('pagination', function () { // Mock out bookshelf model mockQuery = { clone: sandbox.stub(), - select: sandbox.stub() + select: sandbox.stub(), + toQuery: sandbox.stub() }; mockQuery.clone.returns(mockQuery); mockQuery.select.returns([{aggregate: 1}]); @@ -213,7 +214,7 @@ describe('pagination', function () { bookshelf.Model.prototype.fetchPage.should.be.a.Function; }); - it('fetchPage calls all paginationUtils and methods', function (done) { + it('calls all paginationUtils and methods', function (done) { paginationUtils.parseOptions.returns({}); bookshelf.Model.prototype.fetchPage().then(function () { @@ -249,7 +250,7 @@ describe('pagination', function () { }).catch(done); }); - it('fetchPage calls all paginationUtils and methods when order set', function (done) { + it('calls all paginationUtils and methods when order set', function (done) { var orderOptions = {order: {id: 'DESC'}}; paginationUtils.parseOptions.returns(orderOptions); @@ -288,7 +289,7 @@ describe('pagination', function () { }).catch(done); }); - it('fetchPage calls all paginationUtils and methods when group by set', function (done) { + it('calls all paginationUtils and methods when group by set', function (done) { var groupOptions = {groups: ['posts.id']}; paginationUtils.parseOptions.returns(groupOptions); @@ -327,7 +328,7 @@ describe('pagination', function () { }).catch(done); }); - it('fetchPage returns expected response', function (done) { + it('returns expected response', function (done) { paginationUtils.parseOptions.returns({}); bookshelf.Model.prototype.fetchPage().then(function (result) { result.should.have.ownProperty('collection'); @@ -339,7 +340,7 @@ describe('pagination', function () { }); }); - it('fetchPage returns expected response even when aggregate is empty', function (done) { + it('returns expected response even when aggregate is empty', function (done) { // override aggregate response mockQuery.select.returns([]); paginationUtils.parseOptions.returns({}); @@ -353,5 +354,18 @@ describe('pagination', function () { done(); }); }); + + it('will output sql statements in debug mode', function (done) { + model.prototype.debug = true; + mockQuery.select.returns({toQuery: function () {}}); + paginationUtils.parseOptions.returns({}); + + var consoleSpy = sandbox.spy(console, 'log'); + + bookshelf.Model.prototype.fetchPage().then(function () { + consoleSpy.calledOnce.should.be.true; + done(); + }); + }); }); });