diff --git a/core/server/api/v2/utils/serializers/input/integrations.js b/core/server/api/v2/utils/serializers/input/integrations.js index ba0be09417..60435ff0ae 100644 --- a/core/server/api/v2/utils/serializers/input/integrations.js +++ b/core/server/api/v2/utils/serializers/input/integrations.js @@ -1,7 +1,25 @@ const _ = require('lodash'); const debug = require('ghost-ignition').debug('api:v2:utils:serializers:input:integrations'); +function setDefaultFilter(frame) { + if (frame.options.filter) { + frame.options.filter = `(${frame.options.filter})+type:[custom,builtin]`; + } else { + frame.options.filter = 'type:[custom,builtin]'; + } +} + module.exports = { + browse(apiConfig, frame) { + debug('browse'); + + setDefaultFilter(frame); + }, + read(apiConfig, frame) { + debug('read'); + + setDefaultFilter(frame); + }, add(apiConfig, frame) { debug('add'); diff --git a/core/server/models/integration.js b/core/server/models/integration.js index 5ae32c1edb..57e1a0e32f 100644 --- a/core/server/models/integration.js +++ b/core/server/models/integration.js @@ -89,6 +89,16 @@ const Integration = ghostBookshelf.Model.extend({ webhooks: function webhooks() { return this.hasMany('Webhook', 'integration_id'); } +}, { + permittedOptions(methodName) { + let options = ghostBookshelf.Model.permittedOptions.call(this, methodName); + + if (methodName === 'findOne') { + options = options.concat(['filter']); + } + + return options; + } }); const Integrations = ghostBookshelf.Collection.extend({ diff --git a/core/test/acceptance/old/admin/integrations_spec.js b/core/test/acceptance/old/admin/integrations_spec.js index e300a2f578..328c726497 100644 --- a/core/test/acceptance/old/admin/integrations_spec.js +++ b/core/test/acceptance/old/admin/integrations_spec.js @@ -46,6 +46,14 @@ describe('Integrations API', function () { }); }); + it('Can not read internal integration', function () { + return request.get(localUtils.API.getApiQuery(`integrations/${testUtils.DataGenerator.Content.integrations[1].id}/`)) + .set('Origin', config.get('url')) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(404); + }); + it('Can successfully create a single integration with auto generated content and admin api key', function (done) { request.post(localUtils.API.getApiQuery('integrations/')) .set('Origin', config.get('url')) diff --git a/core/test/unit/api/v2/utils/serializers/input/integrations_spec.js b/core/test/unit/api/v2/utils/serializers/input/integrations_spec.js new file mode 100644 index 0000000000..75f24e8be9 --- /dev/null +++ b/core/test/unit/api/v2/utils/serializers/input/integrations_spec.js @@ -0,0 +1,58 @@ +const should = require('should'); +const serializers = require('../../../../../../../server/api/v2/utils/serializers'); + +describe('Unit: v2/utils/serializers/input/pages', function () { + describe('browse', function () { + it('default', function () { + const apiConfig = {}; + const frame = { + options: { + context: {} + } + }; + + serializers.input.integrations.browse(apiConfig, frame); + frame.options.filter.should.eql('type:[custom,builtin]'); + }); + + it('combines filters', function () { + const apiConfig = {}; + const frame = { + options: { + filter: 'type:internal', + context: {} + } + }; + + serializers.input.integrations.browse(apiConfig, frame); + frame.options.filter.should.eql('(type:internal)+type:[custom,builtin]'); + }); + }); + + describe('read', function () { + it('default', function () { + const apiConfig = {}; + const frame = { + options: { + context: {} + } + }; + + serializers.input.integrations.read(apiConfig, frame); + frame.options.filter.should.eql('type:[custom,builtin]'); + }); + + it('combines filters', function () { + const apiConfig = {}; + const frame = { + options: { + filter: 'type:internal', + context: {} + } + }; + + serializers.input.integrations.read(apiConfig, frame); + frame.options.filter.should.eql('(type:internal)+type:[custom,builtin]'); + }); + }); +}); diff --git a/core/test/unit/models/integration_spec.js b/core/test/unit/models/integration_spec.js new file mode 100644 index 0000000000..e7c74057a8 --- /dev/null +++ b/core/test/unit/models/integration_spec.js @@ -0,0 +1,71 @@ +const should = require('should'); +const url = require('url'); +const sinon = require('sinon'); +const models = require('../../../server/models'); +const testUtils = require('../../utils'); +const {knex} = require('../../../server/data/db'); + +describe('Unit: models/integration', function () { + before(function () { + models.init(); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe('permittedOptions', function () { + let basePermittedOptionsReturnVal; + let basePermittedOptionsStub; + + beforeEach(function () { + basePermittedOptionsReturnVal = ['super', 'doopa']; + basePermittedOptionsStub = sinon.stub(models.Base.Model, 'permittedOptions') + .returns(basePermittedOptionsReturnVal); + }); + + it('returns the base permittedOptions result', function () { + const returnedOptions = models.Integration.permittedOptions(); + should.deepEqual(returnedOptions, basePermittedOptionsReturnVal); + }); + + it('returns the base permittedOptions result plus "filter" when methodName is findOne', function () { + const returnedOptions = models.Integration.permittedOptions('findOne'); + should.deepEqual(returnedOptions, basePermittedOptionsReturnVal.concat('filter')); + }); + }); + + describe('findOne', function () { + const mockDb = require('mock-knex'); + let tracker; + + before(function () { + mockDb.mock(knex); + tracker = mockDb.getTracker(); + }); + + after(function () { + mockDb.unmock(knex); + }); + + it('generates correct query (allows use of options.filter)', function () { + const queries = []; + tracker.install(); + + tracker.on('query', (query) => { + queries.push(query); + query.response([]); + }); + + return models.Integration.findOne({ + id: '123' + }, { + filter: 'type:[custom,builtin]' + }).then(() => { + queries.length.should.eql(1); + queries[0].sql.should.eql('select `integrations`.* from `integrations` where `integrations`.`type` in (?, ?) and `integrations`.`id` = ? limit ?'); + queries[0].bindings.should.eql(['custom', 'builtin', '123', 1]); + }); + }); + }); +}); diff --git a/core/test/utils/fixtures/data-generator.js b/core/test/utils/fixtures/data-generator.js index 525f3fd442..a7776848da 100644 --- a/core/test/utils/fixtures/data-generator.js +++ b/core/test/utils/fixtures/data-generator.js @@ -379,6 +379,12 @@ DataGenerator.Content = { id: ObjectId.generate(), name: 'Test Integration', slug: 'test-integration' + }, + { + id: ObjectId.generate(), + name: 'Test Internal Integration', + slug: 'test-internal-integration', + type: 'internal' } ], @@ -885,7 +891,8 @@ DataGenerator.forKnex = (function () { ]; const integrations = [ - createBasic(DataGenerator.Content.integrations[0]) + createBasic(DataGenerator.Content.integrations[0]), + createBasic(DataGenerator.Content.integrations[1]) ]; const api_keys = [