diff --git a/core/server/data/migrations/versions/2.15/1-add-type-column-to-integrations.js b/core/server/data/migrations/versions/2.15/1-add-type-column-to-integrations.js new file mode 100644 index 0000000000..e59adbf384 --- /dev/null +++ b/core/server/data/migrations/versions/2.15/1-add-type-column-to-integrations.js @@ -0,0 +1,40 @@ +const common = require('../../../../lib/common'); +const commands = require('../../../schema').commands; +const table = 'integrations'; +const newColumnNames = [ + 'type' +]; + +function printResult(operation, columnName) { + return `${operation} column ${columnName} in ${table} table`; +} + +module.exports.up = (options) => { + const connection = options.connection; + return Promise.map(newColumnNames, (newColumnName) => { + return connection.schema.hasColumn(table, newColumnName) + .then((exists) => { + if (exists) { + common.logging.warn(printResult('Adding', newColumnName)); + return; + } + common.logging.info(printResult('Adding', newColumnName)); + return commands.addColumn(table, newColumnName, connection); + }); + }); +}; + +module.exports.down = (options) => { + const connection = options.connection; + return Promise.map(newColumnNames, (newColumnName) => { + return connection.schema.hasColumn(table, newColumnName) + .then((exists) => { + if (!exists) { + common.logging.warn(printResult('Dropping', newColumnName)); + return; + } + common.logging.info(printResult('Dropping', newColumnName)); + return commands.dropColumn(table, newColumnName, connection); + }); + }); +}; diff --git a/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js b/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js new file mode 100644 index 0000000000..4e66153565 --- /dev/null +++ b/core/server/data/migrations/versions/2.15/2-insert-zapier-integration.js @@ -0,0 +1,69 @@ +const logging = require('../../../../lib/common/logging'); +const merge = require('lodash/merge'); +const models = require('../../../../models'); +const utils = require('../../../schema/fixtures/utils'); + +const _private = {}; + +_private.printResult = function printResult(result, message) { + if (result.done === result.expected) { + logging.info(message); + } else { + logging.warn(`(${result.done}/${result.expected}) ${message}`); + } +}; + +_private.addZapierIntegration = (options) => { + const message = 'Adding "Zapier" integration'; + const fixtureIntegration = utils.findModelFixtureEntry('Integration', {slug: 'zapier'}); + + return models.Integration.findOne({slug: fixtureIntegration.slug}, options) + .then((integration) => { + if (!integration) { + return utils.addFixturesForModel({ + name: 'Integration', + entries: [fixtureIntegration] + }, options).then(result => _private.printResult(result, message)); + } + + logging.warn(message); + }); +}; + +_private.removeZapierIntegration = (options) => { + const message = 'Rollback: Removing "Zapier" integration'; + + return models.Integration.findOne({slug: 'zapier'}, options) + .then((integration) => { + if (!integration) { + logging.warn(message); + return; + } + + return integration.destroy().then(() => { + logging.info(message); + }); + }); +}; + +module.exports.config = { + transaction: true +}; + +module.exports.up = (options) => { + const localOptions = merge({ + context: {internal: true}, + migrating: true + }, options); + + return _private.addZapierIntegration(localOptions); +}; + +module.exports.down = (options) => { + const localOptions = merge({ + context: {internal: true}, + migrating: true + }, options); + + return _private.removeZapierIntegration(localOptions); +}; diff --git a/core/server/data/schema/fixtures/fixtures.json b/core/server/data/schema/fixtures/fixtures.json index 5d123e3b04..5756fbcea5 100644 --- a/core/server/data/schema/fixtures/fixtures.json +++ b/core/server/data/schema/fixtures/fixtures.json @@ -533,6 +533,18 @@ "feature_image": "https://static.ghost.org/v2.0.0/images/welcome-to-ghost.jpg" } ] + }, + { + "name": "Integration", + "entries": [ + { + "slug": "zapier", + "name": "Zapier", + "description": "Built-in Zapier integration", + "type": "builtin", + "api_keys": [{"type": "admin"}] + } + ] } ], "relations": [ diff --git a/core/server/data/schema/schema.js b/core/server/data/schema/schema.js index 10b9d9b4c5..abcfd51a7a 100644 --- a/core/server/data/schema/schema.js +++ b/core/server/data/schema/schema.js @@ -333,6 +333,13 @@ module.exports = { }, integrations: { id: {type: 'string', maxlength: 24, nullable: false, primary: true}, + type: { + type: 'string', + maxlength: 50, + nullable: false, + defaultTo: 'custom', + validations: {isIn: [['internal', 'builtin', 'custom']]} + }, name: {type: 'string', maxlength: 191, nullable: false}, slug: {type: 'string', maxlength: 191, nullable: false, unique: true}, icon_image: {type: 'string', maxlength: 2000, nullable: true}, diff --git a/core/server/models/api-key.js b/core/server/models/api-key.js index 1632c047b5..0fa6ea0ea2 100644 --- a/core/server/models/api-key.js +++ b/core/server/models/api-key.js @@ -40,9 +40,6 @@ const ApiKey = ghostBookshelf.Model.extend({ return this.belongsTo('Role'); }, - // if an ApiKey does not have a related Integration then it's considered - // "internal" and shouldn't show up in the UI. Example internal API Keys - // would be the ones used for the scheduler and backup clients integration() { return this.belongsTo('Integration'); }, diff --git a/core/server/models/integration.js b/core/server/models/integration.js index b58d567d68..a441b83e42 100644 --- a/core/server/models/integration.js +++ b/core/server/models/integration.js @@ -10,6 +10,12 @@ const Integration = ghostBookshelf.Model.extend({ webhooks: 'webhooks' }, + defaults() { + return { + type: 'custom' + }; + }, + add(data, options) { const addIntegration = () => { return ghostBookshelf.Model.add.call(this, data, options) diff --git a/core/test/unit/data/schema/integrity_spec.js b/core/test/unit/data/schema/integrity_spec.js index 94ae9cccc2..053ce86340 100644 --- a/core/test/unit/data/schema/integrity_spec.js +++ b/core/test/unit/data/schema/integrity_spec.js @@ -19,8 +19,8 @@ var should = require('should'), */ describe('DB version integrity', function () { // Only these variables should need updating - const currentSchemaHash = 'ddca519660d4c9489259557438a41c78'; - const currentFixturesHash = '6b154399f5582f7744fbfd9c30ec709b'; + const currentSchemaHash = '7c5d34376392d01c274700350de228c1'; + const currentFixturesHash = 'db8cd644bc496d0ba34c7f54d7183ee4'; // If this test is failing, then it is likely a change has been made that requires a DB version bump, // and the values above will need updating as confirmation