diff --git a/packages/server/src/api/controllers/webhook.js b/packages/server/src/api/controllers/webhook.js index 7a343b1b07..5b76f86190 100644 --- a/packages/server/src/api/controllers/webhook.js +++ b/packages/server/src/api/controllers/webhook.js @@ -43,12 +43,10 @@ exports.save = async ctx => { webhook._id = generateWebhookID() } const response = await db.put(webhook) + webhook._rev = response.rev ctx.body = { message: "Webhook created successfully", - webhook: { - ...webhook, - ...response, - }, + webhook, } } @@ -95,5 +93,7 @@ exports.trigger = async ctx => { }) } ctx.status = 200 - ctx.body = "Webhook trigger fired successfully" + ctx.body = { + message: "Webhook trigger fired successfully", + } } diff --git a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js index a124ca607e..34b8c4fb10 100644 --- a/packages/server/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/server/src/api/routes/tests/utilities/TestConfiguration.js @@ -9,6 +9,7 @@ const { basicDatasource, basicQuery, basicScreen, + basicWebhook, } = require("./structures") const controllers = require("./controllers") const supertest = require("supertest") @@ -223,6 +224,14 @@ class TestConfiguration { return this._req(config, null, controllers.screen.save) } + async createWebhook(config = null) { + if (!this.automation) { + throw "Must create an automation before creating webhook." + } + config = config || basicWebhook(this.automation._id) + return (await this._req(config, null, controllers.webhook.save)).webhook + } + async createUser( email = EMAIL, password = PASSWORD, diff --git a/packages/server/src/api/routes/tests/utilities/controllers.js b/packages/server/src/api/routes/tests/utilities/controllers.js index e16aa6964c..d6524bb7f0 100644 --- a/packages/server/src/api/routes/tests/utilities/controllers.js +++ b/packages/server/src/api/routes/tests/utilities/controllers.js @@ -10,4 +10,5 @@ module.exports = { datasource: require("../../../controllers/datasource"), query: require("../../../controllers/query"), screen: require("../../../controllers/screen"), + webhook: require("../../../controllers/webhook"), } diff --git a/packages/server/src/api/routes/tests/utilities/structures.js b/packages/server/src/api/routes/tests/utilities/structures.js index aec482bb24..500ff72044 100644 --- a/packages/server/src/api/routes/tests/utilities/structures.js +++ b/packages/server/src/api/routes/tests/utilities/structures.js @@ -90,3 +90,14 @@ exports.basicUser = role => { exports.basicScreen = () => { return createHomeScreen() } + +exports.basicWebhook = automationId => { + return { + live: true, + name: "webhook", + action: { + type: "automation", + target: automationId, + }, + } +} diff --git a/packages/server/src/api/routes/tests/webhook.spec.js b/packages/server/src/api/routes/tests/webhook.spec.js new file mode 100644 index 0000000000..2bf5445a09 --- /dev/null +++ b/packages/server/src/api/routes/tests/webhook.spec.js @@ -0,0 +1,130 @@ +const setup = require("./utilities") +const { checkBuilderEndpoint } = require("./utilities/TestFunctions") +const { basicWebhook, basicAutomation } = require("./utilities/structures") + +describe("/webhooks", () => { + let request = setup.getRequest() + let config = setup.getConfig() + let webhook + + afterAll(setup.afterAll) + + beforeEach(async () => { + await config.init() + const autoConfig = basicAutomation() + autoConfig.definition.trigger = { + schema: { outputs: { properties: {} } }, + inputs: {}, + } + await config.createAutomation(autoConfig) + webhook = await config.createWebhook() + }) + + describe("create", () => { + it("should create a webhook successfully", async () => { + const automation = await config.createAutomation() + const res = await request + .put(`/api/webhooks`) + .send(basicWebhook(automation._id)) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(res.body.webhook).toBeDefined() + expect(typeof res.body.webhook._id).toEqual("string") + expect(typeof res.body.webhook._rev).toEqual("string") + }) + + it("should apply authorization to endpoint", async () => { + await checkBuilderEndpoint({ + config, + method: "PUT", + url: `/api/webhooks`, + }) + }) + }) + + describe("fetch", () => { + it("returns the correct routing for basic user", async () => { + const res = await request + .get(`/api/webhooks`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(Array.isArray(res.body)).toEqual(true) + expect(res.body[0]._id).toEqual(webhook._id) + }) + + it("should apply authorization to endpoint", async () => { + await checkBuilderEndpoint({ + config, + method: "GET", + url: `/api/webhooks`, + }) + }) + }) + + describe("delete", () => { + it("should successfully delete", async () => { + const res = await request + .delete(`/api/webhooks/${webhook._id}/${webhook._rev}`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(res.body).toBeDefined() + expect(res.body.ok).toEqual(true) + }) + + it("should apply authorization to endpoint", async () => { + await checkBuilderEndpoint({ + config, + method: "DELETE", + url: `/api/webhooks/${webhook._id}/${webhook._rev}`, + }) + }) + }) + + describe("build schema", () => { + it("should allow building a schema", async () => { + const res = await request + .post(`/api/webhooks/schema/${config.getAppId()}/${webhook._id}`) + .send({ + a: 1 + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(res.body).toBeDefined() + // fetch to see if the schema has been updated + const fetch = await request + .get(`/api/webhooks`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(fetch.body[0]).toBeDefined() + expect(fetch.body[0].bodySchema).toEqual({ + properties: { + a: { type: "integer" } + }, + type: "object", + }) + }) + + it("should apply authorization to endpoint", async () => { + await checkBuilderEndpoint({ + config, + method: "POST", + url: `/api/webhooks/schema/${config.getAppId()}/${webhook._id}`, + }) + }) + }) + + describe("trigger", () => { + it("should allow triggering from public", async () => { + const res = await request + .post(`/api/webhooks/trigger/${config.getAppId()}/${webhook._id}`) + .expect("Content-Type", /json/) + .expect(200) + expect(res.body.message).toBeDefined() + }) + }) +}) \ No newline at end of file diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index 7eac602f78..1f8b687ba8 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -13,7 +13,7 @@ const { AuthTypes } = require("../constants") const ADMIN_ROLES = [BUILTIN_ROLE_IDS.ADMIN, BUILTIN_ROLE_IDS.BUILDER] -const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|")) +const LOCAL_PASS = new RegExp(["webhooks/trigger"].join("|")) function hasResource(ctx) { return ctx.resourceId != null