Adding a modal on creation of a webhook automation to make sure the user can setup a schema.
This commit is contained in:
parent
877f0f46a4
commit
cc19e2e582
|
@ -11,12 +11,18 @@ const automationActions = store => ({
|
||||||
])
|
])
|
||||||
const jsonResponses = await Promise.all(responses.map(x => x.json()))
|
const jsonResponses = await Promise.all(responses.map(x => x.json()))
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
|
let selected = state.selectedAutomation?.automation
|
||||||
state.automations = jsonResponses[0]
|
state.automations = jsonResponses[0]
|
||||||
state.blockDefinitions = {
|
state.blockDefinitions = {
|
||||||
TRIGGER: jsonResponses[1].trigger,
|
TRIGGER: jsonResponses[1].trigger,
|
||||||
ACTION: jsonResponses[1].action,
|
ACTION: jsonResponses[1].action,
|
||||||
LOGIC: jsonResponses[1].logic,
|
LOGIC: jsonResponses[1].logic,
|
||||||
}
|
}
|
||||||
|
// if previously selected find the new obj and select it
|
||||||
|
if (selected) {
|
||||||
|
selected = jsonResponses[0].filter(automation => automation._id === selected._id)
|
||||||
|
state.selectedAutomation = new Automation(selected[0])
|
||||||
|
}
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore, automationStore } from "builderStore"
|
import { backendUiStore, automationStore } from "builderStore"
|
||||||
|
import CreateWebookModal from "./CreateWebhookModal.svelte"
|
||||||
import analytics from "analytics"
|
import analytics from "analytics"
|
||||||
|
import { Modal } from "@budibase/bbui"
|
||||||
|
|
||||||
export let blockDefinition
|
export let blockDefinition
|
||||||
export let stepId
|
export let stepId
|
||||||
export let blockType
|
export let blockType
|
||||||
|
|
||||||
|
let modal
|
||||||
|
|
||||||
$: blockDefinitions = $automationStore.blockDefinitions
|
$: blockDefinitions = $automationStore.blockDefinitions
|
||||||
$: instanceId = $backendUiStore.selectedDatabase._id
|
$: instanceId = $backendUiStore.selectedDatabase._id
|
||||||
$: automation = $automationStore.selectedAutomation?.automation
|
$: automation = $automationStore.selectedAutomation?.automation
|
||||||
|
@ -18,10 +22,7 @@
|
||||||
type: blockType,
|
type: blockType,
|
||||||
})
|
})
|
||||||
if (stepId === blockDefinitions.TRIGGER["WEBHOOK"].stepId) {
|
if (stepId === blockDefinitions.TRIGGER["WEBHOOK"].stepId) {
|
||||||
automationStore.actions.save({
|
modal.show()
|
||||||
instanceId,
|
|
||||||
automation,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
analytics.captureEvent("Added Automation Block", {
|
analytics.captureEvent("Added Automation Block", {
|
||||||
name: blockDefinition.name,
|
name: blockDefinition.name,
|
||||||
|
@ -39,6 +40,9 @@
|
||||||
<p>{blockDefinition.description}</p>
|
<p>{blockDefinition.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Modal bind:this={modal} width="30%">
|
||||||
|
<CreateWebookModal />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.automation-block {
|
.automation-block {
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script>
|
||||||
|
import { store, backendUiStore, automationStore } from "builderStore"
|
||||||
|
import WebhookDisplay from "./WebhookDisplay.svelte"
|
||||||
|
import { ModalContent } from "@budibase/bbui"
|
||||||
|
import { onMount, onDestroy } from "svelte"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import analytics from "analytics"
|
||||||
|
|
||||||
|
const POLL_RATE_MS = 2500
|
||||||
|
const DEFAULT_SCHEMA_OUTPUT = "Any input allowed"
|
||||||
|
let name
|
||||||
|
let interval
|
||||||
|
let valid = false
|
||||||
|
let schemaURL
|
||||||
|
let schema = DEFAULT_SCHEMA_OUTPUT
|
||||||
|
|
||||||
|
$: instanceId = $backendUiStore.selectedDatabase._id
|
||||||
|
$: appId = $store.appId
|
||||||
|
$: automation = $automationStore.selectedAutomation?.automation
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
// save the automation initially
|
||||||
|
await automationStore.actions.save({
|
||||||
|
instanceId,
|
||||||
|
automation,
|
||||||
|
})
|
||||||
|
interval = setInterval(async () => {
|
||||||
|
await automationStore.actions.fetch()
|
||||||
|
const outputs = automation?.definition?.trigger.schema.outputs?.properties
|
||||||
|
if (Object.keys(outputs).length !== 0) {
|
||||||
|
schema = cloneDeep(outputs)
|
||||||
|
// clear out the "description" properties
|
||||||
|
for (let key of Object.keys(schema)) {
|
||||||
|
delete schema[key].description
|
||||||
|
}
|
||||||
|
schema = JSON.stringify(schema, null, 2)
|
||||||
|
//schema = schema.replace(/(?:\r\n|\r|\n)/g, "<br />")
|
||||||
|
//console.log(schema)
|
||||||
|
valid = true
|
||||||
|
}
|
||||||
|
}, POLL_RATE_MS)
|
||||||
|
schemaURL = automation?.definition?.trigger?.inputs.schemaUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
clearInterval(interval)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalContent
|
||||||
|
title="Webhook Setup"
|
||||||
|
confirmText="Finished"
|
||||||
|
cancelText="Skip"
|
||||||
|
disabled={!valid}>
|
||||||
|
<p class="webhook-exp">To configure a webhook we need to create a schema for your webhook to validate against.
|
||||||
|
Use the URL shown below and send a <b>POST</b> request to it with a JSON body in the format that
|
||||||
|
your webhook should use!</p>
|
||||||
|
<WebhookDisplay value={schemaURL}/>
|
||||||
|
<h5>Schema</h5>
|
||||||
|
<code>
|
||||||
|
{schema}
|
||||||
|
</code>
|
||||||
|
<div slot="footer">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://docs.budibase.com/automate/steps/triggers">
|
||||||
|
<i class="ri-information-line" />
|
||||||
|
<span>Learn about webhooks</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
a {
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 14px;
|
||||||
|
vertical-align: middle;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a span {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--grey-5);
|
||||||
|
background-color: var(--grey-4);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: block;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<script>
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import { Input } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let value
|
||||||
|
|
||||||
|
function fullWebhookURL(uri) {
|
||||||
|
return `http://localhost:4001/${uri}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToClipboard() {
|
||||||
|
const dummy = document.createElement("textarea")
|
||||||
|
document.body.appendChild(dummy)
|
||||||
|
dummy.value = fullWebhookURL(value)
|
||||||
|
dummy.select()
|
||||||
|
document.execCommand("copy")
|
||||||
|
document.body.removeChild(dummy)
|
||||||
|
notifier.success(`URL copied to clipboard`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="copy-area">
|
||||||
|
<Input
|
||||||
|
disabled="true"
|
||||||
|
thin
|
||||||
|
value={fullWebhookURL(value)}/>
|
||||||
|
<span
|
||||||
|
class="copy-btn"
|
||||||
|
on:click={() => copyToClipboard()}>
|
||||||
|
<i class="ri-clipboard-line copy-icon" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.copy-area {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn {
|
||||||
|
position: absolute;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
background: white;
|
||||||
|
right: var(--spacing-s);
|
||||||
|
bottom: 9px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn:hover {
|
||||||
|
background-color: var(--grey-3);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,7 +3,7 @@
|
||||||
import RowSelector from "./ParamInputs/RowSelector.svelte"
|
import RowSelector from "./ParamInputs/RowSelector.svelte"
|
||||||
import { Button, Input, TextArea, Select, Label } from "@budibase/bbui"
|
import { Button, Input, TextArea, Select, Label } from "@budibase/bbui"
|
||||||
import { automationStore } from "builderStore"
|
import { automationStore } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import WebhookDisplay from "../AutomationPanel/BlockList/WebhookDisplay.svelte"
|
||||||
import BindableInput from "../../userInterface/BindableInput.svelte"
|
import BindableInput from "../../userInterface/BindableInput.svelte"
|
||||||
|
|
||||||
export let block
|
export let block
|
||||||
|
@ -43,20 +43,6 @@
|
||||||
}
|
}
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
function fullWebhookURL(uri) {
|
|
||||||
return `http://localhost:4001/${uri}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyToClipboard(input) {
|
|
||||||
const dummy = document.createElement("textarea")
|
|
||||||
document.body.appendChild(dummy)
|
|
||||||
dummy.value = input
|
|
||||||
dummy.select()
|
|
||||||
document.execCommand("copy")
|
|
||||||
document.body.removeChild(dummy)
|
|
||||||
notifier.success(`URL copied to clipboard`)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container" data-cy="automation-block-setup">
|
<div class="container" data-cy="automation-block-setup">
|
||||||
|
@ -80,17 +66,7 @@
|
||||||
{:else if value.customType === 'row'}
|
{:else if value.customType === 'row'}
|
||||||
<RowSelector bind:value={block.inputs[key]} {bindings} />
|
<RowSelector bind:value={block.inputs[key]} {bindings} />
|
||||||
{:else if value.customType === 'webhookUrl'}
|
{:else if value.customType === 'webhookUrl'}
|
||||||
<div class="copy-area">
|
<WebhookDisplay value={block.inputs[key]}/>
|
||||||
<Input
|
|
||||||
disabled="true"
|
|
||||||
thin
|
|
||||||
value={fullWebhookURL(block.inputs[key])} />
|
|
||||||
<span
|
|
||||||
class="copy-btn"
|
|
||||||
on:click={() => copyToClipboard(fullWebhookURL(block.inputs[key]))}>
|
|
||||||
<i class="ri-clipboard-line copy-icon" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{:else if value.type === 'string' || value.type === 'number'}
|
{:else if value.type === 'string' || value.type === 'number'}
|
||||||
<BindableInput
|
<BindableInput
|
||||||
type="string"
|
type="string"
|
||||||
|
@ -119,27 +95,4 @@
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-area {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-btn {
|
|
||||||
position: absolute;
|
|
||||||
border: none;
|
|
||||||
border-radius: 50%;
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
background: white;
|
|
||||||
right: var(--spacing-s);
|
|
||||||
bottom: 9px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy-btn:hover {
|
|
||||||
background-color: var(--grey-4);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -41,6 +41,8 @@ function cleanAutomationInputs(automation) {
|
||||||
* written to DB (this does not write to DB as it would be wasteful to repeat).
|
* written to DB (this does not write to DB as it would be wasteful to repeat).
|
||||||
*/
|
*/
|
||||||
async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
||||||
|
const oldTrigger = oldAuto ? oldAuto.definition.trigger : null
|
||||||
|
const newTrigger = newAuto ? newAuto.definition.trigger : null
|
||||||
function isWebhookTrigger(auto) {
|
function isWebhookTrigger(auto) {
|
||||||
return (
|
return (
|
||||||
auto &&
|
auto &&
|
||||||
|
@ -52,21 +54,19 @@ async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
||||||
if (
|
if (
|
||||||
isWebhookTrigger(oldAuto) &&
|
isWebhookTrigger(oldAuto) &&
|
||||||
!isWebhookTrigger(newAuto) &&
|
!isWebhookTrigger(newAuto) &&
|
||||||
oldAuto.definition.trigger.webhook
|
oldTrigger.webhookId
|
||||||
) {
|
) {
|
||||||
|
let db = new CouchDB(user.instanceId)
|
||||||
|
// need to get the webhook to get the rev
|
||||||
|
const webhook = await db.get(oldTrigger.webhookId)
|
||||||
const ctx = {
|
const ctx = {
|
||||||
user,
|
user,
|
||||||
params: {
|
params: { id: webhook._id, rev: webhook._rev },
|
||||||
id: oldAuto.definition.trigger.webhook.id,
|
|
||||||
rev: oldAuto.definition.trigger.webhook.rev,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
// reset the inputs to remove the URLs
|
// might be updating - reset the inputs to remove the URLs
|
||||||
if (newAuto && newAuto.definition.trigger) {
|
if (newTrigger) {
|
||||||
const trigger = newAuto.definition.trigger
|
delete newTrigger.webhookId
|
||||||
delete trigger.webhook
|
newTrigger.inputs = {}
|
||||||
delete trigger.inputs.schemaUrl
|
|
||||||
delete trigger.inputs.triggerUrl
|
|
||||||
}
|
}
|
||||||
await webhooks.destroy(ctx)
|
await webhooks.destroy(ctx)
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,9 @@ async function checkForWebhooks({ user, oldAuto, newAuto }) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
await webhooks.save(ctx)
|
await webhooks.save(ctx)
|
||||||
const id = ctx.body.webhook._id,
|
const id = ctx.body.webhook._id
|
||||||
rev = ctx.body.webhook._rev
|
newTrigger.webhookId = id
|
||||||
newAuto.definition.trigger.webhook = { id, rev }
|
newTrigger.inputs = {
|
||||||
newAuto.definition.trigger.inputs = {
|
|
||||||
schemaUrl: `api/webhooks/schema/${user.instanceId}/${id}`,
|
schemaUrl: `api/webhooks/schema/${user.instanceId}/${id}`,
|
||||||
triggerUrl: `api/webhooks/trigger/${user.instanceId}/${id}`,
|
triggerUrl: `api/webhooks/trigger/${user.instanceId}/${id}`,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue