Merge remote-tracking branch 'origin/develop' into feature/binding-v2-updates
This commit is contained in:
commit
0730c15b14
|
@ -44,7 +44,10 @@ jobs:
|
|||
node-version: 14.x
|
||||
cache: "yarn"
|
||||
- run: yarn
|
||||
- run: yarn nx run-many -t=build --configuration=production
|
||||
# Run build all the projects
|
||||
- run: yarn build
|
||||
# Check the types of the projects built via esbuild
|
||||
- run: yarn check:types
|
||||
|
||||
test-libraries:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -54,6 +54,9 @@ jobs:
|
|||
- run: yarn build --configuration=production
|
||||
- run: yarn build:sdk
|
||||
|
||||
- name: Reset pro dependencies
|
||||
run: node scripts/resetProDependencies.js
|
||||
|
||||
- name: Publish budibase packages to NPM
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
|
|
@ -60,6 +60,9 @@ jobs:
|
|||
- run: yarn build --configuration=production
|
||||
- run: yarn build:sdk
|
||||
|
||||
- name: Reset pro dependencies
|
||||
run: node scripts/resetProDependencies.js
|
||||
|
||||
- name: Publish budibase packages to NPM
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "2.6.19-alpha.11",
|
||||
"version": "2.6.19-alpha.21",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/backend-core",
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
"devDependencies": {
|
||||
"@esbuild-plugins/node-resolve": "^0.2.2",
|
||||
"@esbuild-plugins/tsconfig-paths": "^0.1.2",
|
||||
"@nx/esbuild": "16.2.1",
|
||||
"@nx/js": "16.2.1",
|
||||
"@rollup/plugin-json": "^4.0.2",
|
||||
"@typescript-eslint/parser": "5.45.0",
|
||||
|
@ -34,6 +33,7 @@
|
|||
"bootstrap": "./scripts/link-dependencies.sh && echo '***BOOTSTRAP ONLY REQUIRED FOR USE WITH ACCOUNT PORTAL***'",
|
||||
"build": "yarn nx run-many -t=build",
|
||||
"build:dev": "lerna run --stream prebuild && yarn nx run-many --target=build --output-style=dynamic --watch --preserveWatchOutput",
|
||||
"check:types": "lerna run check:types --skip-nx-cache",
|
||||
"backend:bootstrap": "./scripts/scopeBackend.sh && yarn run bootstrap",
|
||||
"backend:build": "./scripts/scopeBackend.sh 'lerna run --stream build'",
|
||||
"build:sdk": "lerna run --stream build:sdk",
|
||||
|
@ -52,7 +52,7 @@
|
|||
"dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker",
|
||||
"dev:server": "yarn run kill-server && lerna run --stream --parallel dev:builder --scope @budibase/worker --scope @budibase/server",
|
||||
"dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream --parallel dev:built",
|
||||
"dev:docker": "yarn build && docker-compose -f hosting/docker-compose.dev.yaml -f hosting/docker-compose.build.yaml up --build --scale proxy-service=0 ",
|
||||
"dev:docker": "yarn build && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0",
|
||||
"test": "lerna run --stream test --stream",
|
||||
"lint:eslint": "eslint packages && eslint qa-core",
|
||||
"lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\" && prettier --write \"examples/**/*.{js,ts,svelte}\" && prettier --check \"qa-core/**/*.{js,ts,svelte}\"",
|
||||
|
|
|
@ -90,6 +90,10 @@ export const useScimIntegration = () => {
|
|||
return useFeature(Feature.SCIM)
|
||||
}
|
||||
|
||||
export const useSyncAutomations = () => {
|
||||
return useFeature(Feature.SYNC_AUTOMATIONS)
|
||||
}
|
||||
|
||||
// QUOTAS
|
||||
|
||||
export const setAutomationLogsQuota = (value: number) => {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { datasources, tables } from "../stores/backend"
|
|||
import { IntegrationNames } from "../constants/backend"
|
||||
import { get } from "svelte/store"
|
||||
import cloneDeep from "lodash/cloneDeepWith"
|
||||
import { API } from "api"
|
||||
|
||||
function prepareData(config) {
|
||||
let datasource = {}
|
||||
|
@ -37,3 +38,9 @@ export async function createRestDatasource(integration) {
|
|||
const config = cloneDeep(integration)
|
||||
return saveDatasource(config)
|
||||
}
|
||||
|
||||
export async function validateDatasourceConfig(config) {
|
||||
const datasource = prepareData(config)
|
||||
const resp = await API.validateDatasource(datasource)
|
||||
return resp
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { ActionStepID } from "constants/backend/automations"
|
||||
import { TableNames } from "../constants"
|
||||
import {
|
||||
AUTO_COLUMN_DISPLAY_NAMES,
|
||||
|
@ -53,3 +54,9 @@ export function buildAutoColumn(tableName, name, subtype) {
|
|||
}
|
||||
return base
|
||||
}
|
||||
|
||||
export function checkForCollectStep(automation) {
|
||||
return automation.definition.steps.some(
|
||||
step => step.stepId === ActionStepID.COLLECT
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,24 +6,48 @@
|
|||
Body,
|
||||
Icon,
|
||||
notifications,
|
||||
Tags,
|
||||
Tag,
|
||||
} from "@budibase/bbui"
|
||||
import { automationStore } from "builderStore"
|
||||
import { admin } from "stores/portal"
|
||||
import { automationStore, selectedAutomation } from "builderStore"
|
||||
import { admin, licensing } from "stores/portal"
|
||||
import { externalActions } from "./ExternalActions"
|
||||
import { TriggerStepID } from "constants/backend/automations"
|
||||
import { checkForCollectStep } from "builderStore/utils"
|
||||
|
||||
export let blockIdx
|
||||
export let lastStep
|
||||
|
||||
const disabled = {
|
||||
SEND_EMAIL_SMTP: {
|
||||
disabled: !$admin.checklist.smtp.checked,
|
||||
message: "Please configure SMTP",
|
||||
},
|
||||
}
|
||||
|
||||
let syncAutomationsEnabled = $licensing.syncAutomationsEnabled
|
||||
let collectBlockAllowedSteps = [TriggerStepID.APP, TriggerStepID.WEBHOOK]
|
||||
let selectedAction
|
||||
let actionVal
|
||||
let actions = Object.entries($automationStore.blockDefinitions.ACTION)
|
||||
|
||||
$: collectBlockExists = checkForCollectStep($selectedAutomation)
|
||||
|
||||
const disabled = () => {
|
||||
return {
|
||||
SEND_EMAIL_SMTP: {
|
||||
disabled: !$admin.checklist.smtp.checked,
|
||||
message: "Please configure SMTP",
|
||||
},
|
||||
COLLECT: {
|
||||
disabled: !lastStep || !syncAutomationsEnabled || collectBlockExists,
|
||||
message: collectDisabledMessage(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const collectDisabledMessage = () => {
|
||||
if (collectBlockExists) {
|
||||
return "Only one Collect step allowed"
|
||||
}
|
||||
if (!lastStep) {
|
||||
return "Only available as the last step"
|
||||
}
|
||||
}
|
||||
|
||||
const external = actions.reduce((acc, elm) => {
|
||||
const [k, v] = elm
|
||||
if (!v.internal && !v.custom) {
|
||||
|
@ -38,6 +62,15 @@
|
|||
acc[k] = v
|
||||
}
|
||||
delete acc.LOOP
|
||||
|
||||
// Filter out Collect block if not App Action or Webhook
|
||||
if (
|
||||
!collectBlockAllowedSteps.includes(
|
||||
$selectedAutomation.definition.trigger.stepId
|
||||
)
|
||||
) {
|
||||
delete acc.COLLECT
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
|
@ -48,7 +81,6 @@
|
|||
}
|
||||
return acc
|
||||
}, {})
|
||||
console.log(plugins)
|
||||
|
||||
const selectAction = action => {
|
||||
actionVal = action
|
||||
|
@ -72,7 +104,7 @@
|
|||
<ModalContent
|
||||
title="Add automation step"
|
||||
confirmText="Save"
|
||||
size="M"
|
||||
size="L"
|
||||
disabled={!selectedAction}
|
||||
onConfirm={addBlockToAutomation}
|
||||
>
|
||||
|
@ -107,7 +139,7 @@
|
|||
<Detail size="S">Actions</Detail>
|
||||
<div class="item-list">
|
||||
{#each Object.entries(internal) as [idx, action]}
|
||||
{@const isDisabled = disabled[idx] && disabled[idx].disabled}
|
||||
{@const isDisabled = disabled()[idx] && disabled()[idx].disabled}
|
||||
<div
|
||||
class="item"
|
||||
class:disabled={isDisabled}
|
||||
|
@ -117,8 +149,14 @@
|
|||
<div class="item-body">
|
||||
<Icon name={action.icon} />
|
||||
<Body size="XS">{action.name}</Body>
|
||||
{#if isDisabled}
|
||||
<Icon name="Help" tooltip={disabled[idx].message} />
|
||||
{#if isDisabled && !syncAutomationsEnabled}
|
||||
<div class="tag-color">
|
||||
<Tags>
|
||||
<Tag icon="LockClosed">Business</Tag>
|
||||
</Tags>
|
||||
</div>
|
||||
{:else if isDisabled}
|
||||
<Icon name="Help" tooltip={disabled()[idx].message} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -152,6 +190,7 @@
|
|||
display: flex;
|
||||
margin-left: var(--spacing-m);
|
||||
gap: var(--spacing-m);
|
||||
align-items: center;
|
||||
}
|
||||
.item-list {
|
||||
display: grid;
|
||||
|
@ -181,4 +220,8 @@
|
|||
.disabled :global(.spectrum-Body) {
|
||||
color: var(--spectrum-global-color-gray-600);
|
||||
}
|
||||
|
||||
.tag-color :global(.spectrum-Tags-item) {
|
||||
background: var(--spectrum-global-color-gray-200);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
import ActionModal from "./ActionModal.svelte"
|
||||
import FlowItemHeader from "./FlowItemHeader.svelte"
|
||||
import RoleSelect from "components/design/settings/controls/RoleSelect.svelte"
|
||||
import { ActionStepID, TriggerStepID } from "constants/backend/automations"
|
||||
import {
|
||||
ActionStepID,
|
||||
TriggerStepID,
|
||||
Features,
|
||||
} from "constants/backend/automations"
|
||||
import { permissions } from "stores/backend"
|
||||
|
||||
export let block
|
||||
|
@ -31,6 +35,9 @@
|
|||
let showLooping = false
|
||||
let role
|
||||
|
||||
$: collectBlockExists = $selectedAutomation.definition.steps.some(
|
||||
step => step.stepId === ActionStepID.COLLECT
|
||||
)
|
||||
$: automationId = $selectedAutomation?._id
|
||||
$: showBindingPicker =
|
||||
block.stepId === ActionStepID.CREATE_ROW ||
|
||||
|
@ -184,7 +191,7 @@
|
|||
{#if !isTrigger}
|
||||
<div>
|
||||
<div class="block-options">
|
||||
{#if !loopBlock}
|
||||
{#if block?.features?.[Features.LOOPING] || !block.features}
|
||||
<ActionButton on:click={() => addLooping()} icon="Reuse">
|
||||
Add Looping
|
||||
</ActionButton>
|
||||
|
@ -224,21 +231,28 @@
|
|||
</Layout>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Modal bind:this={actionModal} width="30%">
|
||||
<ActionModal {blockIdx} />
|
||||
</Modal>
|
||||
|
||||
<Modal bind:this={webhookModal} width="30%">
|
||||
<CreateWebhookModal />
|
||||
</Modal>
|
||||
</div>
|
||||
<div class="separator" />
|
||||
<Icon on:click={() => actionModal.show()} hoverable name="AddCircle" size="S" />
|
||||
{#if isTrigger ? totalBlocks > 1 : blockIdx !== totalBlocks - 2}
|
||||
{#if !collectBlockExists || !lastStep}
|
||||
<div class="separator" />
|
||||
<Icon
|
||||
on:click={() => actionModal.show()}
|
||||
hoverable
|
||||
name="AddCircle"
|
||||
size="S"
|
||||
/>
|
||||
{#if isTrigger ? totalBlocks > 1 : blockIdx !== totalBlocks - 2}
|
||||
<div class="separator" />
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<Modal bind:this={actionModal} width="30%">
|
||||
<ActionModal {lastStep} {blockIdx} />
|
||||
</Modal>
|
||||
|
||||
<Modal bind:this={webhookModal} width="30%">
|
||||
<CreateWebhookModal />
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.delete-padding {
|
||||
padding-left: 30px;
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
config,
|
||||
schema: selected.datasource,
|
||||
auth: selected.auth,
|
||||
features: selected.features || [],
|
||||
}
|
||||
if (selected.friendlyName) {
|
||||
integration.name = selected.friendlyName
|
||||
|
|
|
@ -4,55 +4,68 @@
|
|||
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
||||
import { IntegrationNames } from "constants/backend"
|
||||
import cloneDeep from "lodash/cloneDeepWith"
|
||||
import { saveDatasource as save } from "builderStore/datasource"
|
||||
import { onMount } from "svelte"
|
||||
import {
|
||||
saveDatasource as save,
|
||||
validateDatasourceConfig,
|
||||
} from "builderStore/datasource"
|
||||
import { DatasourceFeature } from "@budibase/types"
|
||||
|
||||
export let integration
|
||||
export let modal
|
||||
|
||||
// kill the reference so the input isn't saved
|
||||
let datasource = cloneDeep(integration)
|
||||
let skipFetch = false
|
||||
let isValid = false
|
||||
|
||||
$: name =
|
||||
IntegrationNames[datasource.type] || datasource.name || datasource.type
|
||||
|
||||
async function validateConfig() {
|
||||
const displayError = message =>
|
||||
notifications.error(message ?? "Error validating datasource")
|
||||
|
||||
let connected = false
|
||||
try {
|
||||
const resp = await validateDatasourceConfig(datasource)
|
||||
if (!resp.connected) {
|
||||
displayError(`Unable to connect - ${resp.error}`)
|
||||
}
|
||||
connected = resp.connected
|
||||
} catch (err) {
|
||||
displayError(err?.message)
|
||||
}
|
||||
return connected
|
||||
}
|
||||
|
||||
async function saveDatasource() {
|
||||
if (integration.features[DatasourceFeature.CONNECTION_CHECKING]) {
|
||||
const valid = await validateConfig()
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!datasource.name) {
|
||||
datasource.name = name
|
||||
}
|
||||
const resp = await save(datasource, skipFetch)
|
||||
const resp = await save(datasource)
|
||||
$goto(`./datasource/${resp._id}`)
|
||||
notifications.success(`Datasource updated successfully.`)
|
||||
notifications.success(`Datasource created successfully.`)
|
||||
} catch (err) {
|
||||
notifications.error(err?.message ?? "Error saving datasource")
|
||||
// prevent the modal from closing
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
skipFetch = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<ModalContent
|
||||
title={`Connect to ${name}`}
|
||||
onConfirm={() => saveDatasource()}
|
||||
onCancel={() => modal.show()}
|
||||
confirmText={datasource.plus
|
||||
? "Save and fetch tables"
|
||||
: "Save and continue to query"}
|
||||
confirmText={datasource.plus ? "Connect" : "Save and continue to query"}
|
||||
cancelText="Back"
|
||||
showSecondaryButton={datasource.plus}
|
||||
secondaryButtonText={datasource.plus ? "Skip table fetch" : undefined}
|
||||
secondaryAction={() => {
|
||||
skipFetch = true
|
||||
saveDatasource()
|
||||
return true
|
||||
}}
|
||||
size="L"
|
||||
disabled={!isValid}
|
||||
>
|
||||
|
|
|
@ -126,8 +126,7 @@
|
|||
}
|
||||
|
||||
const getAllBindings = (bindings, eventContextBindings, actions) => {
|
||||
let allBindings = eventContextBindings.concat(bindings)
|
||||
|
||||
let allBindings = []
|
||||
if (!actions) {
|
||||
return []
|
||||
}
|
||||
|
@ -145,14 +144,35 @@
|
|||
.forEach(action => {
|
||||
// Check we have a binding for this action, and generate one if not
|
||||
const stateBinding = makeStateBinding(action.parameters.key)
|
||||
const hasKey = allBindings.some(binding => {
|
||||
const hasKey = bindings.some(binding => {
|
||||
return binding.runtimeBinding === stateBinding.runtimeBinding
|
||||
})
|
||||
if (!hasKey) {
|
||||
allBindings.push(stateBinding)
|
||||
bindings.push(stateBinding)
|
||||
}
|
||||
})
|
||||
// Get which indexes are asynchronous automations as we want to filter them out from the bindings
|
||||
const asynchronousAutomationIndexes = actions
|
||||
.map((action, index) => {
|
||||
if (
|
||||
action[EVENT_TYPE_KEY] === "Trigger Automation" &&
|
||||
!action.parameters?.synchronous
|
||||
) {
|
||||
return index
|
||||
}
|
||||
})
|
||||
.filter(index => index !== undefined)
|
||||
|
||||
// Based on the above, filter out the asynchronous automations from the bindings
|
||||
if (asynchronousAutomationIndexes) {
|
||||
allBindings = eventContextBindings
|
||||
.filter((binding, index) => {
|
||||
return !asynchronousAutomationIndexes.includes(index)
|
||||
})
|
||||
.concat(bindings)
|
||||
} else {
|
||||
allBindings = eventContextBindings.concat(bindings)
|
||||
}
|
||||
return allBindings
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { Select, Label, Input, Checkbox } from "@budibase/bbui"
|
||||
import { Select, Label, Input, Checkbox, Icon } from "@budibase/bbui"
|
||||
import { automationStore } from "builderStore"
|
||||
import SaveFields from "./SaveFields.svelte"
|
||||
import { TriggerStepID } from "constants/backend/automations"
|
||||
import { TriggerStepID, ActionStepID } from "constants/backend/automations"
|
||||
|
||||
export let parameters = {}
|
||||
export let bindings = []
|
||||
|
@ -16,6 +16,14 @@
|
|||
? AUTOMATION_STATUS.EXISTING
|
||||
: AUTOMATION_STATUS.NEW
|
||||
|
||||
$: {
|
||||
if (automationStatus === AUTOMATION_STATUS.NEW) {
|
||||
parameters.synchronous = false
|
||||
}
|
||||
parameters.synchronous = automations.find(
|
||||
automation => automation._id === parameters.automationId
|
||||
)?.synchronous
|
||||
}
|
||||
$: automations = $automationStore.automations
|
||||
.filter(a => a.definition.trigger?.stepId === TriggerStepID.APP)
|
||||
.map(automation => {
|
||||
|
@ -23,10 +31,15 @@
|
|||
automation.definition.trigger.inputs.fields || {}
|
||||
).map(([name, type]) => ({ name, type }))
|
||||
|
||||
let hasCollectBlock = automation.definition.steps.some(
|
||||
step => step.stepId === ActionStepID.COLLECT
|
||||
)
|
||||
|
||||
return {
|
||||
name: automation.name,
|
||||
_id: automation._id,
|
||||
schema,
|
||||
synchronous: hasCollectBlock,
|
||||
}
|
||||
})
|
||||
$: hasAutomations = automations && automations.length > 0
|
||||
|
@ -35,6 +48,8 @@
|
|||
)
|
||||
$: selectedSchema = selectedAutomation?.schema
|
||||
|
||||
$: error = parameters.timeout > 120 ? "Timeout must be less than 120s" : null
|
||||
|
||||
const onFieldsChanged = e => {
|
||||
parameters.fields = Object.entries(e.detail || {}).reduce(
|
||||
(acc, [key, value]) => {
|
||||
|
@ -57,6 +72,14 @@
|
|||
parameters.fields = {}
|
||||
parameters.automationId = automations[0]?._id
|
||||
}
|
||||
|
||||
const onChange = value => {
|
||||
let automationId = value.detail
|
||||
parameters.synchronous = automations.find(
|
||||
automation => automation._id === automationId
|
||||
)?.synchronous
|
||||
parameters.automationId = automationId
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
|
@ -85,6 +108,7 @@
|
|||
|
||||
{#if automationStatus === AUTOMATION_STATUS.EXISTING}
|
||||
<Select
|
||||
on:change={onChange}
|
||||
bind:value={parameters.automationId}
|
||||
placeholder="Choose automation"
|
||||
options={automations}
|
||||
|
@ -98,6 +122,29 @@
|
|||
/>
|
||||
{/if}
|
||||
|
||||
{#if parameters.synchronous}
|
||||
<Label small />
|
||||
|
||||
<div class="synchronous-info">
|
||||
<Icon name="Info" />
|
||||
<div>
|
||||
<i
|
||||
>This automation will run synchronously as it contains a Collect
|
||||
step</i
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<Label small />
|
||||
|
||||
<div class="timeout-width">
|
||||
<Input
|
||||
label="Timeout in seconds (120 max)"
|
||||
type="number"
|
||||
{error}
|
||||
bind:value={parameters.timeout}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<Label small />
|
||||
<Checkbox
|
||||
text="Do not display default notification"
|
||||
|
@ -133,6 +180,9 @@
|
|||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.timeout-width {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.params {
|
||||
display: grid;
|
||||
|
@ -142,6 +192,11 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.synchronous-info {
|
||||
display: flex;
|
||||
gap: var(--spacing-s);
|
||||
}
|
||||
|
||||
.fields {
|
||||
margin-top: var(--spacing-l);
|
||||
display: grid;
|
||||
|
|
|
@ -57,7 +57,13 @@
|
|||
{
|
||||
"name": "Trigger Automation",
|
||||
"type": "application",
|
||||
"component": "TriggerAutomation"
|
||||
"component": "TriggerAutomation",
|
||||
"context": [
|
||||
{
|
||||
"label": "Automation Result",
|
||||
"value": "result"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Update Field Value",
|
||||
|
|
|
@ -20,9 +20,14 @@ export const ActionStepID = {
|
|||
FILTER: "FILTER",
|
||||
QUERY_ROWS: "QUERY_ROWS",
|
||||
LOOP: "LOOP",
|
||||
COLLECT: "COLLECT",
|
||||
// these used to be lowercase step IDs, maintain for backwards compat
|
||||
discord: "discord",
|
||||
slack: "slack",
|
||||
zapier: "zapier",
|
||||
integromat: "integromat",
|
||||
}
|
||||
|
||||
export const Features = {
|
||||
LOOPING: "LOOPING",
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
import { isEqual } from "lodash"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import ImportRestQueriesModal from "components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte"
|
||||
import { API } from "api"
|
||||
import { DatasourceFeature } from "@budibase/types"
|
||||
|
||||
const querySchema = {
|
||||
name: {},
|
||||
|
@ -45,7 +47,30 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function validateConfig() {
|
||||
const displayError = message =>
|
||||
notifications.error(message ?? "Error validating datasource")
|
||||
|
||||
let connected = false
|
||||
try {
|
||||
const resp = await API.validateDatasource(datasource)
|
||||
if (!resp.connected) {
|
||||
displayError(`Unable to connect - ${resp.error}`)
|
||||
}
|
||||
connected = resp.connected
|
||||
} catch (err) {
|
||||
displayError(err?.message)
|
||||
}
|
||||
return connected
|
||||
}
|
||||
|
||||
const saveDatasource = async () => {
|
||||
if (integration.features[DatasourceFeature.CONNECTION_CHECKING]) {
|
||||
const valid = await validateConfig()
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
try {
|
||||
// Create datasource
|
||||
await datasources.save(datasource)
|
||||
|
|
|
@ -31,6 +31,18 @@
|
|||
return "Invalid URL"
|
||||
}
|
||||
}
|
||||
|
||||
$: urlManuallySet = false
|
||||
|
||||
const updateUrl = event => {
|
||||
const appName = event.detail
|
||||
if (urlManuallySet) {
|
||||
return
|
||||
}
|
||||
|
||||
const parsedUrl = appName.toLowerCase().replace(/\s+/g, "-")
|
||||
url = encodeURI(parsedUrl)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
@ -43,11 +55,13 @@
|
|||
bind:value={name}
|
||||
bind:error={nameError}
|
||||
validate={validateName}
|
||||
on:change={updateUrl}
|
||||
label="Name"
|
||||
/>
|
||||
<FancyInput
|
||||
bind:value={url}
|
||||
bind:error={urlError}
|
||||
on:change={() => (urlManuallySet = true)}
|
||||
validate={validateUrl}
|
||||
label="URL"
|
||||
/>
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
import { Roles } from "constants/backend"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import { helpers } from "@budibase/shared-core"
|
||||
import { validateDatasourceConfig } from "builderStore/datasource"
|
||||
import { DatasourceFeature } from "@budibase/types"
|
||||
|
||||
let name = "My first app"
|
||||
let url = "my-first-app"
|
||||
|
@ -108,7 +110,24 @@
|
|||
isGoogle,
|
||||
}) => {
|
||||
let app
|
||||
|
||||
try {
|
||||
if (
|
||||
datasourceConfig &&
|
||||
plusIntegrations[stage].features[DatasourceFeature.CONNECTION_CHECKING]
|
||||
) {
|
||||
const resp = await validateDatasourceConfig({
|
||||
config: datasourceConfig,
|
||||
type: stage,
|
||||
})
|
||||
if (!resp.connected) {
|
||||
notifications.error(
|
||||
`Unable to connect - ${resp.error ?? "Error validating datasource"}`
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
app = await createApp(useSampleData)
|
||||
|
||||
let datasource
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { writable } from "svelte/store"
|
||||
import { API } from "api"
|
||||
import { licensing } from "./licensing"
|
||||
import { ConfigType } from "../../../../types/src/documents"
|
||||
import { ConfigType } from "@budibase/types"
|
||||
|
||||
export const createFeatureStore = () => {
|
||||
const internalStore = writable({
|
||||
|
|
|
@ -116,6 +116,9 @@ export const createLicensingStore = () => {
|
|||
const auditLogsEnabled = license.features.includes(
|
||||
Constants.Features.AUDIT_LOGS
|
||||
)
|
||||
const syncAutomationsEnabled = license.features.includes(
|
||||
Constants.Features.SYNC_AUTOMATIONS
|
||||
)
|
||||
store.update(state => {
|
||||
return {
|
||||
...state,
|
||||
|
@ -130,6 +133,7 @@ export const createLicensingStore = () => {
|
|||
environmentVariablesEnabled,
|
||||
auditLogsEnabled,
|
||||
enforceableSSO,
|
||||
syncAutomationsEnabled,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
@ -122,13 +122,23 @@ const deleteRowHandler = async action => {
|
|||
}
|
||||
|
||||
const triggerAutomationHandler = async action => {
|
||||
const { fields, notificationOverride } = action.parameters
|
||||
const { fields, notificationOverride, timeout } = action.parameters
|
||||
if (fields) {
|
||||
try {
|
||||
await API.triggerAutomation({
|
||||
const result = await API.triggerAutomation({
|
||||
automationId: action.parameters.automationId,
|
||||
fields,
|
||||
timeout,
|
||||
})
|
||||
|
||||
// Value will exist if automation is synchronous, so return it.
|
||||
if (result.value) {
|
||||
if (!notificationOverride) {
|
||||
notificationStore.actions.success("Automation ran successfully")
|
||||
}
|
||||
return { result }
|
||||
}
|
||||
|
||||
if (!notificationOverride) {
|
||||
notificationStore.actions.success("Automation triggered")
|
||||
}
|
||||
|
@ -138,7 +148,6 @@ const triggerAutomationHandler = async action => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const navigationHandler = action => {
|
||||
const { url, peek, externalNewTab } = action.parameters
|
||||
routeStore.actions.navigate(url, peek, externalNewTab)
|
||||
|
|
|
@ -4,10 +4,10 @@ export const buildAutomationEndpoints = API => ({
|
|||
* @param automationId the ID of the automation to trigger
|
||||
* @param fields the fields to trigger the automation with
|
||||
*/
|
||||
triggerAutomation: async ({ automationId, fields }) => {
|
||||
triggerAutomation: async ({ automationId, fields, timeout }) => {
|
||||
return await API.post({
|
||||
url: `/api/automations/${automationId}/trigger`,
|
||||
body: { fields },
|
||||
body: { fields, timeout },
|
||||
})
|
||||
},
|
||||
|
||||
|
|
|
@ -58,4 +58,15 @@ export const buildDatasourceEndpoints = API => ({
|
|||
url: `/api/datasources/${datasourceId}/${datasourceRev}`,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate a datasource configuration
|
||||
* @param datasource the datasource configuration to validate
|
||||
*/
|
||||
validateDatasource: async datasource => {
|
||||
return await API.post({
|
||||
url: `/api/datasources/verify`,
|
||||
body: { datasource },
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
.boolean-cell {
|
||||
padding: 2px var(--cell-padding);
|
||||
pointer-events: none;
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.boolean-cell.editable {
|
||||
pointer-events: all;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
export let selected
|
||||
export let rowFocused
|
||||
export let rowIdx
|
||||
export let topRow = false
|
||||
export let focused
|
||||
export let selectedUser
|
||||
export let column
|
||||
|
@ -68,6 +69,7 @@
|
|||
{highlighted}
|
||||
{selected}
|
||||
{rowIdx}
|
||||
{topRow}
|
||||
{focused}
|
||||
{selectedUser}
|
||||
{readonly}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
export let selectedUser = null
|
||||
export let error = null
|
||||
export let rowIdx
|
||||
export let topRow = false
|
||||
export let defaultHeight = false
|
||||
export let center = false
|
||||
export let readonly = false
|
||||
|
@ -31,13 +32,14 @@
|
|||
class:readonly
|
||||
class:default-height={defaultHeight}
|
||||
class:selected-other={selectedUser != null}
|
||||
class:alt={rowIdx % 2 === 1}
|
||||
class:top={topRow}
|
||||
on:focus
|
||||
on:mousedown
|
||||
on:mouseup
|
||||
on:click
|
||||
on:contextmenu
|
||||
{style}
|
||||
data-row={rowIdx}
|
||||
>
|
||||
{#if error}
|
||||
<div class="label">
|
||||
|
@ -70,6 +72,9 @@
|
|||
width: 0;
|
||||
--cell-color: transparent;
|
||||
}
|
||||
.cell.alt {
|
||||
--cell-background: var(--cell-background-alt);
|
||||
}
|
||||
.cell.default-height {
|
||||
height: var(--default-row-height);
|
||||
}
|
||||
|
@ -98,8 +103,8 @@
|
|||
.cell.selected-other:not(.focused):after {
|
||||
border-radius: 0 2px 2px 2px;
|
||||
}
|
||||
.cell[data-row="0"].error:after,
|
||||
.cell[data-row="0"].selected-other:not(.focused):after {
|
||||
.cell.top.error:after,
|
||||
.cell.top.selected-other:not(.focused):after {
|
||||
border-radius: 2px 2px 2px 0;
|
||||
}
|
||||
|
||||
|
@ -152,7 +157,7 @@
|
|||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
.cell[data-row="0"] .label {
|
||||
.cell.top .label {
|
||||
bottom: auto;
|
||||
top: 100%;
|
||||
border-radius: 0 2px 2px 2px;
|
||||
|
|
|
@ -21,16 +21,7 @@
|
|||
svelteDispatch("select")
|
||||
const id = row?._id
|
||||
if (id) {
|
||||
selectedRows.update(state => {
|
||||
let newState = {
|
||||
...state,
|
||||
[id]: !state[id],
|
||||
}
|
||||
if (!newState[id]) {
|
||||
delete newState[id]
|
||||
}
|
||||
return newState
|
||||
})
|
||||
selectedRows.actions.toggleRow(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +38,7 @@
|
|||
highlighted={rowFocused || rowHovered}
|
||||
selected={rowSelected}
|
||||
{defaultHeight}
|
||||
rowIdx={row?.__idx}
|
||||
>
|
||||
<div class="gutter">
|
||||
{#if $$slots.default}
|
||||
|
|
|
@ -196,7 +196,11 @@
|
|||
<MenuItem disabled={!canMoveRight} icon="ChevronRight" on:click={moveRight}>
|
||||
Move right
|
||||
</MenuItem>
|
||||
<MenuItem icon="VisibilityOff" on:click={hideColumn}>Hide column</MenuItem>
|
||||
<MenuItem
|
||||
disabled={idx === "sticky"}
|
||||
icon="VisibilityOff"
|
||||
on:click={hideColumn}>Hide column</MenuItem
|
||||
>
|
||||
</Menu>
|
||||
</Popover>
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
export let allowExpandRows = true
|
||||
export let allowEditRows = true
|
||||
export let allowDeleteRows = true
|
||||
export let stripeRows = false
|
||||
|
||||
// Unique identifier for DOM nodes inside this instance
|
||||
const rand = Math.random()
|
||||
|
@ -55,6 +56,7 @@
|
|||
allowExpandRows,
|
||||
allowEditRows,
|
||||
allowDeleteRows,
|
||||
stripeRows,
|
||||
})
|
||||
|
||||
// Build up context
|
||||
|
@ -90,6 +92,7 @@
|
|||
allowExpandRows,
|
||||
allowEditRows,
|
||||
allowDeleteRows,
|
||||
stripeRows,
|
||||
})
|
||||
|
||||
// Set context for children to consume
|
||||
|
@ -107,6 +110,7 @@
|
|||
id="grid-{rand}"
|
||||
class:is-resizing={$isResizing}
|
||||
class:is-reordering={$isReordering}
|
||||
class:stripe={$config.stripeRows}
|
||||
style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};"
|
||||
>
|
||||
<div class="controls">
|
||||
|
@ -169,6 +173,7 @@
|
|||
/* Variables */
|
||||
--cell-background: var(--spectrum-global-color-gray-50);
|
||||
--cell-background-hover: var(--spectrum-global-color-gray-100);
|
||||
--cell-background-alt: var(--cell-background);
|
||||
--cell-padding: 8px;
|
||||
--cell-spacing: 4px;
|
||||
--cell-border: 1px solid var(--spectrum-global-color-gray-200);
|
||||
|
@ -185,6 +190,9 @@
|
|||
.grid.is-reordering :global(*) {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
.grid.stripe {
|
||||
--cell-background-alt: var(--spectrum-global-color-gray-75);
|
||||
}
|
||||
|
||||
.grid-data-outer,
|
||||
.grid-data-inner {
|
||||
|
|
|
@ -36,7 +36,11 @@
|
|||
<div bind:this={body} class="grid-body">
|
||||
<GridScrollWrapper scrollHorizontally scrollVertically wheelInteractive>
|
||||
{#each $renderedRows as row, idx}
|
||||
<GridRow {row} {idx} invertY={idx >= $rowVerticalInversionIndex} />
|
||||
<GridRow
|
||||
{row}
|
||||
top={idx === 0}
|
||||
invertY={idx >= $rowVerticalInversionIndex}
|
||||
/>
|
||||
{/each}
|
||||
{#if $config.allowAddRows && $renderedColumns.length}
|
||||
<div
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import DataCell from "../cells/DataCell.svelte"
|
||||
|
||||
export let row
|
||||
export let idx
|
||||
export let top = false
|
||||
export let invertY = false
|
||||
|
||||
const {
|
||||
|
@ -41,7 +41,8 @@
|
|||
invertX={columnIdx >= $columnHorizontalInversionIndex}
|
||||
highlighted={rowHovered || rowFocused || reorderSource === column.name}
|
||||
selected={rowSelected}
|
||||
rowIdx={idx}
|
||||
rowIdx={row.__idx}
|
||||
topRow={top}
|
||||
focused={$focusedCellId === cellId}
|
||||
selectedUser={$selectedCellMap[cellId]}
|
||||
width={column.width}
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
border-right: var(--cell-border);
|
||||
border-bottom: var(--cell-border);
|
||||
background: var(--spectrum-global-color-gray-100);
|
||||
z-index: 20;
|
||||
z-index: 1;
|
||||
}
|
||||
.add:hover {
|
||||
background: var(--spectrum-global-color-gray-200);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
background-color: var(--spectrum-global-color-gray-200);
|
||||
background-color: var(--spectrum-global-color-gray-300);
|
||||
color: var(--spectrum-global-color-gray-700);
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
|
|
|
@ -167,7 +167,7 @@
|
|||
focused={$focusedCellId === cellId}
|
||||
width={$stickyColumn.width}
|
||||
{updateValue}
|
||||
rowIdx={0}
|
||||
topRow={offset === 0}
|
||||
{invertY}
|
||||
>
|
||||
{#if $stickyColumn?.schema?.autocolumn}
|
||||
|
@ -193,7 +193,7 @@
|
|||
row={newRow}
|
||||
focused={$focusedCellId === cellId}
|
||||
width={column.width}
|
||||
rowIdx={0}
|
||||
topRow={offset === 0}
|
||||
invertX={columnIdx >= $columnHorizontalInversionIndex}
|
||||
{invertY}
|
||||
>
|
||||
|
@ -219,7 +219,7 @@
|
|||
<Button size="M" secondary newStyles on:click={clear}>
|
||||
<div class="button-with-keys">
|
||||
Cancel
|
||||
<KeyboardShortcut overlay keybind="Esc" />
|
||||
<KeyboardShortcut keybind="Esc" />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -82,7 +82,8 @@
|
|||
{rowFocused}
|
||||
selected={rowSelected}
|
||||
highlighted={rowHovered || rowFocused}
|
||||
rowIdx={idx}
|
||||
rowIdx={row.__idx}
|
||||
topRow={idx === 0}
|
||||
focused={$focusedCellId === cellId}
|
||||
selectedUser={$selectedCellMap[cellId]}
|
||||
width={$stickyColumn.width}
|
||||
|
|
|
@ -224,10 +224,7 @@
|
|||
if (!id || id === NewRowID) {
|
||||
return
|
||||
}
|
||||
selectedRows.update(state => {
|
||||
state[id] = !state[id]
|
||||
return state
|
||||
})
|
||||
selectedRows.actions.toggleRow(id)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
|
|
@ -4,9 +4,10 @@ const reorderInitialState = {
|
|||
sourceColumn: null,
|
||||
targetColumn: null,
|
||||
breakpoints: [],
|
||||
initialMouseX: null,
|
||||
scrollLeft: 0,
|
||||
gridLeft: 0,
|
||||
width: 0,
|
||||
latestX: 0,
|
||||
increment: 0,
|
||||
}
|
||||
|
||||
export const createStores = () => {
|
||||
|
@ -23,14 +24,24 @@ export const createStores = () => {
|
|||
}
|
||||
|
||||
export const deriveStores = context => {
|
||||
const { reorder, columns, visibleColumns, scroll, bounds, stickyColumn, ui } =
|
||||
context
|
||||
const {
|
||||
reorder,
|
||||
columns,
|
||||
visibleColumns,
|
||||
scroll,
|
||||
bounds,
|
||||
stickyColumn,
|
||||
ui,
|
||||
maxScrollLeft,
|
||||
} = context
|
||||
|
||||
let autoScrollInterval
|
||||
let isAutoScrolling
|
||||
|
||||
// Callback when dragging on a colum header and starting reordering
|
||||
const startReordering = (column, e) => {
|
||||
const $visibleColumns = get(visibleColumns)
|
||||
const $bounds = get(bounds)
|
||||
const $scroll = get(scroll)
|
||||
const $stickyColumn = get(stickyColumn)
|
||||
ui.actions.blur()
|
||||
|
||||
|
@ -51,9 +62,8 @@ export const deriveStores = context => {
|
|||
sourceColumn: column,
|
||||
targetColumn: null,
|
||||
breakpoints,
|
||||
initialMouseX: e.clientX,
|
||||
scrollLeft: $scroll.left,
|
||||
gridLeft: $bounds.left,
|
||||
width: $bounds.width,
|
||||
})
|
||||
|
||||
// Add listeners to handle mouse movement
|
||||
|
@ -66,12 +76,44 @@ export const deriveStores = context => {
|
|||
|
||||
// Callback when moving the mouse when reordering columns
|
||||
const onReorderMouseMove = e => {
|
||||
// Immediately handle the current position
|
||||
const x = e.clientX
|
||||
reorder.update(state => ({
|
||||
...state,
|
||||
latestX: x,
|
||||
}))
|
||||
considerReorderPosition()
|
||||
|
||||
// Check if we need to start auto-scrolling
|
||||
const $reorder = get(reorder)
|
||||
const proximityCutoff = 140
|
||||
const speedFactor = 8
|
||||
const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x)
|
||||
const leftProximity = Math.max(0, x - $reorder.gridLeft)
|
||||
if (rightProximity < proximityCutoff) {
|
||||
const weight = proximityCutoff - rightProximity
|
||||
const increment = (weight / proximityCutoff) * speedFactor
|
||||
reorder.update(state => ({ ...state, increment }))
|
||||
startAutoScroll()
|
||||
} else if (leftProximity < proximityCutoff) {
|
||||
const weight = -1 * (proximityCutoff - leftProximity)
|
||||
const increment = (weight / proximityCutoff) * speedFactor
|
||||
reorder.update(state => ({ ...state, increment }))
|
||||
startAutoScroll()
|
||||
} else {
|
||||
stopAutoScroll()
|
||||
}
|
||||
}
|
||||
|
||||
// Actual logic to consider the current position and determine the new order
|
||||
const considerReorderPosition = () => {
|
||||
const $reorder = get(reorder)
|
||||
const $scroll = get(scroll)
|
||||
|
||||
// Compute the closest breakpoint to the current position
|
||||
let targetColumn
|
||||
let minDistance = Number.MAX_SAFE_INTEGER
|
||||
const mouseX = e.clientX - $reorder.gridLeft + $reorder.scrollLeft
|
||||
const mouseX = $reorder.latestX - $reorder.gridLeft + $scroll.left
|
||||
$reorder.breakpoints.forEach(point => {
|
||||
const distance = Math.abs(point.x - mouseX)
|
||||
if (distance < minDistance) {
|
||||
|
@ -79,7 +121,6 @@ export const deriveStores = context => {
|
|||
targetColumn = point.column
|
||||
}
|
||||
})
|
||||
|
||||
if (targetColumn !== $reorder.targetColumn) {
|
||||
reorder.update(state => ({
|
||||
...state,
|
||||
|
@ -88,8 +129,35 @@ export const deriveStores = context => {
|
|||
}
|
||||
}
|
||||
|
||||
// Commences auto-scrolling in a certain direction, triggered when the mouse
|
||||
// approaches the edges of the grid
|
||||
const startAutoScroll = () => {
|
||||
if (isAutoScrolling) {
|
||||
return
|
||||
}
|
||||
isAutoScrolling = true
|
||||
autoScrollInterval = setInterval(() => {
|
||||
const $maxLeft = get(maxScrollLeft)
|
||||
const { increment } = get(reorder)
|
||||
scroll.update(state => ({
|
||||
...state,
|
||||
left: Math.max(0, Math.min($maxLeft, state.left + increment)),
|
||||
}))
|
||||
considerReorderPosition()
|
||||
}, 10)
|
||||
}
|
||||
|
||||
// Stops auto scrolling
|
||||
const stopAutoScroll = () => {
|
||||
isAutoScrolling = false
|
||||
clearInterval(autoScrollInterval)
|
||||
}
|
||||
|
||||
// Callback when stopping reordering columns
|
||||
const stopReordering = async () => {
|
||||
// Ensure auto-scrolling is stopped
|
||||
stopAutoScroll()
|
||||
|
||||
// Swap position of columns
|
||||
let { sourceColumn, targetColumn } = get(reorder)
|
||||
moveColumn(sourceColumn, targetColumn)
|
||||
|
|
|
@ -25,14 +25,33 @@ export const createStores = () => {
|
|||
null
|
||||
)
|
||||
|
||||
// Toggles whether a certain row ID is selected or not
|
||||
const toggleSelectedRow = id => {
|
||||
selectedRows.update(state => {
|
||||
let newState = {
|
||||
...state,
|
||||
[id]: !state[id],
|
||||
}
|
||||
if (!newState[id]) {
|
||||
delete newState[id]
|
||||
}
|
||||
return newState
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
focusedCellId,
|
||||
focusedCellAPI,
|
||||
focusedRowId,
|
||||
previousFocusedRowId,
|
||||
selectedRows,
|
||||
hoveredRowId,
|
||||
rowHeight,
|
||||
selectedRows: {
|
||||
...selectedRows,
|
||||
actions: {
|
||||
toggleRow: toggleSelectedRow,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ export const Features = {
|
|||
ENFORCEABLE_SSO: "enforceableSSO",
|
||||
BRANDING: "branding",
|
||||
SCIM: "scim",
|
||||
SYNC_AUTOMATIONS: "syncAutomations",
|
||||
}
|
||||
|
||||
// Role IDs
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit aea8a4acb0bae6a1036520bf4c6d8cae428cc7d9
|
||||
Subproject commit 3d307df17a53ba25bbf5d9ddc94b1706c813eb6f
|
|
@ -11,6 +11,7 @@
|
|||
"scripts": {
|
||||
"prebuild": "rimraf dist/",
|
||||
"build": "node ./scripts/build.js",
|
||||
"check:types": "tsc -p tsconfig.build.json --noEmit",
|
||||
"postbuild": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client",
|
||||
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
||||
"debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js",
|
||||
|
@ -47,7 +48,7 @@
|
|||
"@apidevtools/swagger-parser": "10.0.3",
|
||||
"@budibase/backend-core": "0.0.1",
|
||||
"@budibase/client": "0.0.1",
|
||||
"@budibase/pro": "develop",
|
||||
"@budibase/pro": "0.0.1",
|
||||
"@budibase/shared-core": "0.0.1",
|
||||
"@budibase/string-templates": "0.0.1",
|
||||
"@budibase/types": "0.0.1",
|
||||
|
|
|
@ -14,9 +14,16 @@ import { deleteEntityMetadata } from "../../utilities"
|
|||
import { MetadataTypes } from "../../constants"
|
||||
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
||||
import { context, cache, events } from "@budibase/backend-core"
|
||||
import { automations } from "@budibase/pro"
|
||||
import { Automation, BBContext } from "@budibase/types"
|
||||
import { automations, features } from "@budibase/pro"
|
||||
import {
|
||||
Automation,
|
||||
AutomationActionStepId,
|
||||
AutomationResults,
|
||||
BBContext,
|
||||
} from "@budibase/types"
|
||||
import { getActionDefinitions as actionDefs } from "../../automations/actions"
|
||||
import sdk from "../../sdk"
|
||||
import { db as dbCore } from "@budibase/backend-core"
|
||||
|
||||
async function getActionDefinitions() {
|
||||
return removeDeprecated(await actionDefs())
|
||||
|
@ -257,13 +264,34 @@ export async function getDefinitionList(ctx: BBContext) {
|
|||
export async function trigger(ctx: BBContext) {
|
||||
const db = context.getAppDB()
|
||||
let automation = await db.get(ctx.params.id)
|
||||
await triggers.externalTrigger(automation, {
|
||||
...ctx.request.body,
|
||||
appId: ctx.appId,
|
||||
})
|
||||
ctx.body = {
|
||||
message: `Automation ${automation._id} has been triggered.`,
|
||||
automation,
|
||||
|
||||
let hasCollectStep = sdk.automations.utils.checkForCollectStep(automation)
|
||||
if (hasCollectStep && (await features.isSyncAutomationsEnabled())) {
|
||||
const response: AutomationResults = await triggers.externalTrigger(
|
||||
automation,
|
||||
{
|
||||
fields: ctx.request.body.fields,
|
||||
timeout: ctx.request.body.timeout * 1000 || 120000,
|
||||
},
|
||||
{ getResponses: true }
|
||||
)
|
||||
|
||||
let collectedValue = response.steps.find(
|
||||
step => step.stepId === AutomationActionStepId.COLLECT
|
||||
)
|
||||
ctx.body = collectedValue?.outputs
|
||||
} else {
|
||||
if (ctx.appId && !dbCore.isProdAppID(ctx.appId)) {
|
||||
ctx.throw(400, "Only apps in production support this endpoint")
|
||||
}
|
||||
await triggers.externalTrigger(automation, {
|
||||
...ctx.request.body,
|
||||
appId: ctx.appId,
|
||||
})
|
||||
ctx.body = {
|
||||
message: `Automation ${automation._id} has been triggered.`,
|
||||
automation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,11 @@ import {
|
|||
WebhookActionType,
|
||||
BBContext,
|
||||
Automation,
|
||||
AutomationActionStepId,
|
||||
} from "@budibase/types"
|
||||
import sdk from "../../sdk"
|
||||
import * as pro from "@budibase/pro"
|
||||
|
||||
const toJsonSchema = require("to-json-schema")
|
||||
const validate = require("jsonschema").validate
|
||||
|
||||
|
@ -78,15 +81,36 @@ export async function trigger(ctx: BBContext) {
|
|||
if (webhook.action.type === WebhookActionType.AUTOMATION) {
|
||||
// trigger with both the pure request and then expand it
|
||||
// incase the user has produced a schema to bind to
|
||||
await triggers.externalTrigger(target, {
|
||||
body: ctx.request.body,
|
||||
...ctx.request.body,
|
||||
appId: prodAppId,
|
||||
})
|
||||
}
|
||||
ctx.status = 200
|
||||
ctx.body = {
|
||||
message: "Webhook trigger fired successfully",
|
||||
let hasCollectStep = sdk.automations.utils.checkForCollectStep(target)
|
||||
|
||||
if (hasCollectStep && (await pro.features.isSyncAutomationsEnabled())) {
|
||||
const response = await triggers.externalTrigger(
|
||||
target,
|
||||
{
|
||||
body: ctx.request.body,
|
||||
...ctx.request.body,
|
||||
appId: prodAppId,
|
||||
},
|
||||
{ getResponses: true }
|
||||
)
|
||||
|
||||
let collectedValue = response.steps.find(
|
||||
(step: any) => step.stepId === AutomationActionStepId.COLLECT
|
||||
)
|
||||
|
||||
ctx.status = 200
|
||||
ctx.body = collectedValue.outputs
|
||||
} else {
|
||||
await triggers.externalTrigger(target, {
|
||||
body: ctx.request.body,
|
||||
...ctx.request.body,
|
||||
appId: prodAppId,
|
||||
})
|
||||
ctx.status = 200
|
||||
ctx.body = {
|
||||
message: "Webhook trigger fired successfully",
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (err.status === 404) {
|
||||
|
|
|
@ -65,7 +65,6 @@ router
|
|||
)
|
||||
.post(
|
||||
"/api/automations/:id/trigger",
|
||||
appInfoMiddleware({ appType: AppType.PROD }),
|
||||
paramResource("id"),
|
||||
authorized(
|
||||
permissions.PermissionType.AUTOMATION,
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
const {
|
||||
import {
|
||||
checkBuilderEndpoint,
|
||||
getAllTableRows,
|
||||
clearAllAutomations,
|
||||
testAutomation,
|
||||
} = require("./utilities/TestFunctions")
|
||||
const setup = require("./utilities")
|
||||
const { basicAutomation, newAutomation, automationTrigger, automationStep } = setup.structures
|
||||
const MAX_RETRIES = 4
|
||||
const { TRIGGER_DEFINITIONS, BUILTIN_ACTION_DEFINITIONS } = require("../../../automations")
|
||||
const { events } = require("@budibase/backend-core")
|
||||
} from "./utilities/TestFunctions"
|
||||
import * as setup from "./utilities"
|
||||
import {
|
||||
TRIGGER_DEFINITIONS,
|
||||
BUILTIN_ACTION_DEFINITIONS,
|
||||
} from "../../../automations"
|
||||
import { events } from "@budibase/backend-core"
|
||||
import sdk from "../../../sdk"
|
||||
import { Automation } from "@budibase/types"
|
||||
import { mocks } from "@budibase/backend-core/tests"
|
||||
|
||||
const MAX_RETRIES = 4
|
||||
let {
|
||||
basicAutomation,
|
||||
newAutomation,
|
||||
automationTrigger,
|
||||
automationStep,
|
||||
collectAutomation,
|
||||
} = setup.structures
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
|
@ -24,6 +36,7 @@ describe("/automations", () => {
|
|||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
events.automation.deleted.mockClear()
|
||||
})
|
||||
|
||||
|
@ -32,7 +45,7 @@ describe("/automations", () => {
|
|||
const res = await request
|
||||
.get(`/api/automations/action/list`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(Object.keys(res.body).length).not.toEqual(0)
|
||||
|
@ -42,7 +55,7 @@ describe("/automations", () => {
|
|||
const res = await request
|
||||
.get(`/api/automations/trigger/list`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(Object.keys(res.body).length).not.toEqual(0)
|
||||
|
@ -52,14 +65,18 @@ describe("/automations", () => {
|
|||
const res = await request
|
||||
.get(`/api/automations/definitions/list`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
let definitionsLength = Object.keys(BUILTIN_ACTION_DEFINITIONS).length
|
||||
definitionsLength-- // OUTGOING_WEBHOOK is deprecated
|
||||
|
||||
expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(definitionsLength)
|
||||
expect(Object.keys(res.body.trigger).length).toEqual(Object.keys(TRIGGER_DEFINITIONS).length)
|
||||
expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(
|
||||
definitionsLength
|
||||
)
|
||||
expect(Object.keys(res.body.trigger).length).toEqual(
|
||||
Object.keys(TRIGGER_DEFINITIONS).length
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -72,7 +89,7 @@ describe("/automations", () => {
|
|||
.post(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.message).toEqual("Automation created successfully")
|
||||
|
@ -91,7 +108,7 @@ describe("/automations", () => {
|
|||
.post(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.message).toEqual("Automation created successfully")
|
||||
|
@ -107,7 +124,7 @@ describe("/automations", () => {
|
|||
config,
|
||||
method: "POST",
|
||||
url: `/api/automations`,
|
||||
body: automation
|
||||
body: automation,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -118,7 +135,7 @@ describe("/automations", () => {
|
|||
const res = await request
|
||||
.get(`/api/automations/${automation._id}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body._id).toEqual(automation._id)
|
||||
expect(res.body._rev).toEqual(automation._rev)
|
||||
|
@ -134,8 +151,8 @@ describe("/automations", () => {
|
|||
row: {
|
||||
name: "{{trigger.row.name}}",
|
||||
description: "{{trigger.row.description}}",
|
||||
tableId: table._id
|
||||
}
|
||||
tableId: table._id,
|
||||
},
|
||||
}
|
||||
automation.appId = config.appId
|
||||
automation = await config.createAutomation(automation)
|
||||
|
@ -162,23 +179,68 @@ describe("/automations", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
describe("trigger", () => {
|
||||
it("does not trigger an automation when not synchronous and in dev", async () => {
|
||||
let automation = newAutomation()
|
||||
automation = await config.createAutomation(automation)
|
||||
const res = await request
|
||||
.post(`/api/automations/${automation._id}/trigger`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400)
|
||||
|
||||
const update = async (automation) => {
|
||||
expect(res.body.message).toEqual(
|
||||
"Only apps in production support this endpoint"
|
||||
)
|
||||
})
|
||||
|
||||
it("triggers a synchronous automation", async () => {
|
||||
mocks.licenses.useSyncAutomations()
|
||||
let automation = collectAutomation()
|
||||
automation = await config.createAutomation(automation)
|
||||
const res = await request
|
||||
.post(`/api/automations/${automation._id}/trigger`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.success).toEqual(true)
|
||||
expect(res.body.value).toEqual([1, 2, 3])
|
||||
})
|
||||
|
||||
it("triggers an asynchronous automation", async () => {
|
||||
let automation = newAutomation()
|
||||
automation = await config.createAutomation(automation)
|
||||
await config.publish()
|
||||
|
||||
const res = await request
|
||||
.post(`/api/automations/${automation._id}/trigger`)
|
||||
.set(config.defaultHeaders({}, true))
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.message).toEqual(
|
||||
`Automation ${automation._id} has been triggered.`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
const update = async (automation: Automation) => {
|
||||
return request
|
||||
.put(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
}
|
||||
|
||||
const updateWithPost = async (automation) => {
|
||||
const updateWithPost = async (automation: Automation) => {
|
||||
return request
|
||||
.post(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.send(automation)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
}
|
||||
|
||||
|
@ -199,7 +261,9 @@ describe("/automations", () => {
|
|||
expect(automationRes._rev).not.toEqual(automation._rev)
|
||||
// content updates
|
||||
expect(automationRes.name).toEqual("Updated Name")
|
||||
expect(message).toEqual(`Automation ${automation._id} updated successfully.`)
|
||||
expect(message).toEqual(
|
||||
`Automation ${automation._id} updated successfully.`
|
||||
)
|
||||
// events
|
||||
expect(events.automation.created).not.toBeCalled()
|
||||
expect(events.automation.stepCreated).not.toBeCalled()
|
||||
|
@ -207,7 +271,6 @@ describe("/automations", () => {
|
|||
expect(events.automation.triggerUpdated).not.toBeCalled()
|
||||
})
|
||||
|
||||
|
||||
it("updates a automations name using POST request", async () => {
|
||||
let automation = newAutomation()
|
||||
await config.createAutomation(automation)
|
||||
|
@ -226,7 +289,9 @@ describe("/automations", () => {
|
|||
expect(automationRes._rev).not.toEqual(automation._rev)
|
||||
// content updates
|
||||
expect(automationRes.name).toEqual("Updated Name")
|
||||
expect(message).toEqual(`Automation ${automation._id} updated successfully.`)
|
||||
expect(message).toEqual(
|
||||
`Automation ${automation._id} updated successfully.`
|
||||
)
|
||||
// events
|
||||
expect(events.automation.created).not.toBeCalled()
|
||||
expect(events.automation.stepCreated).not.toBeCalled()
|
||||
|
@ -237,7 +302,9 @@ describe("/automations", () => {
|
|||
it("updates an automation trigger", async () => {
|
||||
let automation = newAutomation()
|
||||
automation = await config.createAutomation(automation)
|
||||
automation.definition.trigger = automationTrigger(TRIGGER_DEFINITIONS.WEBHOOK)
|
||||
automation.definition.trigger = automationTrigger(
|
||||
TRIGGER_DEFINITIONS.WEBHOOK
|
||||
)
|
||||
jest.clearAllMocks()
|
||||
|
||||
await update(automation)
|
||||
|
@ -266,7 +333,6 @@ describe("/automations", () => {
|
|||
expect(events.automation.triggerUpdated).not.toBeCalled()
|
||||
})
|
||||
|
||||
|
||||
it("removes automation steps", async () => {
|
||||
let automation = newAutomation()
|
||||
automation.definition.steps.push(automationStep())
|
||||
|
@ -305,11 +371,11 @@ describe("/automations", () => {
|
|||
it("return all the automations for an instance", async () => {
|
||||
await clearAllAutomations(config)
|
||||
const autoConfig = basicAutomation()
|
||||
automation = await config.createAutomation(autoConfig)
|
||||
await config.createAutomation(autoConfig)
|
||||
const res = await request
|
||||
.get(`/api/automations`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body[0]).toEqual(expect.objectContaining(autoConfig))
|
||||
|
@ -330,7 +396,7 @@ describe("/automations", () => {
|
|||
const res = await request
|
||||
.delete(`/api/automations/${automation.id}/${automation.rev}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect('Content-Type', /json/)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.id).toEqual(automation._id)
|
||||
|
@ -346,4 +412,13 @@ describe("/automations", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("checkForCollectStep", () => {
|
||||
it("should return true if a collect step exists in an automation", async () => {
|
||||
let automation = collectAutomation()
|
||||
await config.createAutomation(automation)
|
||||
let res = await sdk.automations.utils.checkForCollectStep(automation)
|
||||
expect(res).toEqual(true)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,11 +1,13 @@
|
|||
const setup = require("./utilities")
|
||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||
const { basicWebhook, basicAutomation } = setup.structures
|
||||
import { Webhook } from "@budibase/types"
|
||||
import * as setup from "./utilities"
|
||||
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
||||
import { mocks } from "@budibase/backend-core/tests"
|
||||
const { basicWebhook, basicAutomation, collectAutomation } = setup.structures
|
||||
|
||||
describe("/webhooks", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
let webhook
|
||||
let webhook: Webhook
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
|
@ -13,10 +15,11 @@ describe("/webhooks", () => {
|
|||
config.modeSelf()
|
||||
await config.init()
|
||||
const autoConfig = basicAutomation()
|
||||
autoConfig.definition.trigger = {
|
||||
schema: { outputs: { properties: {} } },
|
||||
inputs: {},
|
||||
autoConfig.definition.trigger.schema = {
|
||||
outputs: { properties: {} },
|
||||
inputs: { properties: {} },
|
||||
}
|
||||
autoConfig.definition.trigger.inputs = {}
|
||||
await config.createAutomation(autoConfig)
|
||||
webhook = await config.createWebhook()
|
||||
}
|
||||
|
@ -70,7 +73,7 @@ describe("/webhooks", () => {
|
|||
|
||||
describe("delete", () => {
|
||||
beforeAll(setupTest)
|
||||
|
||||
|
||||
it("should successfully delete", async () => {
|
||||
const res = await request
|
||||
.delete(`/api/webhooks/${webhook._id}/${webhook._rev}`)
|
||||
|
@ -97,7 +100,7 @@ describe("/webhooks", () => {
|
|||
const res = await request
|
||||
.post(`/api/webhooks/schema/${config.getAppId()}/${webhook._id}`)
|
||||
.send({
|
||||
a: 1
|
||||
a: 1,
|
||||
})
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
|
@ -112,7 +115,7 @@ describe("/webhooks", () => {
|
|||
expect(fetch.body[0]).toBeDefined()
|
||||
expect(fetch.body[0].bodySchema).toEqual({
|
||||
properties: {
|
||||
a: { type: "integer" }
|
||||
a: { type: "integer" },
|
||||
},
|
||||
type: "object",
|
||||
})
|
||||
|
@ -131,4 +134,23 @@ describe("/webhooks", () => {
|
|||
expect(res.body.message).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("should trigger a synchronous webhook call ", async () => {
|
||||
mocks.licenses.useSyncAutomations()
|
||||
let automation = collectAutomation()
|
||||
let newAutomation = await config.createAutomation(automation)
|
||||
let syncWebhook = await config.createWebhook(
|
||||
basicWebhook(newAutomation._id)
|
||||
)
|
||||
|
||||
// replicate changes before checking webhook
|
||||
await config.publish()
|
||||
|
||||
const res = await request
|
||||
.post(`/api/webhooks/trigger/${config.prodAppId}/${syncWebhook._id}`)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body.success).toEqual(true)
|
||||
expect(res.body.value).toEqual([1, 2, 3])
|
||||
})
|
||||
})
|
|
@ -14,6 +14,7 @@ import * as filter from "./steps/filter"
|
|||
import * as delay from "./steps/delay"
|
||||
import * as queryRow from "./steps/queryRows"
|
||||
import * as loop from "./steps/loop"
|
||||
import * as collect from "./steps/collect"
|
||||
import env from "../environment"
|
||||
import {
|
||||
AutomationStepSchema,
|
||||
|
@ -39,6 +40,7 @@ const ACTION_IMPLS: Record<
|
|||
DELAY: delay.run,
|
||||
FILTER: filter.run,
|
||||
QUERY_ROWS: queryRow.run,
|
||||
COLLECT: collect.run,
|
||||
// these used to be lowercase step IDs, maintain for backwards compat
|
||||
discord: discord.run,
|
||||
slack: slack.run,
|
||||
|
@ -59,6 +61,7 @@ export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
|
|||
FILTER: filter.definition,
|
||||
QUERY_ROWS: queryRow.definition,
|
||||
LOOP: loop.definition,
|
||||
COLLECT: collect.definition,
|
||||
// these used to be lowercase step IDs, maintain for backwards compat
|
||||
discord: discord.definition,
|
||||
slack: slack.definition,
|
||||
|
|
|
@ -5,6 +5,7 @@ import environment from "../../environment"
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
AutomationIOType,
|
||||
AutomationStepInput,
|
||||
AutomationStepSchema,
|
||||
|
@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Run a bash script",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
stepId: AutomationActionStepId.EXECUTE_BASH,
|
||||
inputs: {},
|
||||
schema: {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationStepSchema,
|
||||
AutomationStepInput,
|
||||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
export const definition: AutomationStepSchema = {
|
||||
name: "Collect Data",
|
||||
tagline: "Collect data to be sent to design",
|
||||
icon: "Collection",
|
||||
description:
|
||||
"Collects specified data so it can be provided to the design section",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {},
|
||||
stepId: AutomationActionStepId.COLLECT,
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
properties: {
|
||||
collection: {
|
||||
type: AutomationIOType.STRING,
|
||||
title: "What to Collect",
|
||||
},
|
||||
},
|
||||
required: ["collection"],
|
||||
},
|
||||
outputs: {
|
||||
properties: {
|
||||
success: {
|
||||
type: AutomationIOType.BOOLEAN,
|
||||
description: "Whether the action was successful",
|
||||
},
|
||||
value: {
|
||||
type: AutomationIOType.STRING,
|
||||
description: "Collected data",
|
||||
},
|
||||
},
|
||||
required: ["success", "value"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export async function run({ inputs }: AutomationStepInput) {
|
||||
if (!inputs.collection) {
|
||||
return {
|
||||
success: false,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
value: inputs.collection,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import { buildCtx } from "./utils"
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
AutomationIOType,
|
||||
AutomationStepInput,
|
||||
AutomationStepSchema,
|
||||
|
@ -17,6 +18,9 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Add a row to your database",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
stepId: AutomationActionStepId.CREATE_ROW,
|
||||
inputs: {},
|
||||
schema: {
|
||||
|
|
|
@ -14,6 +14,7 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Delay the automation until an amount of time has passed",
|
||||
stepId: AutomationActionStepId.DELAY,
|
||||
internal: true,
|
||||
features: {},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
export const definition: AutomationStepSchema = {
|
||||
|
@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
|
|||
type: AutomationStepType.ACTION,
|
||||
stepId: AutomationActionStepId.DELETE_ROW,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
AutomationStepInput,
|
||||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
const DEFAULT_USERNAME = "Budibase Automate"
|
||||
|
@ -19,6 +20,9 @@ export const definition: AutomationStepSchema = {
|
|||
stepId: AutomationActionStepId.discord,
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: false,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -4,6 +4,7 @@ import * as automationUtils from "../automationUtils"
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
AutomationIOType,
|
||||
AutomationStepInput,
|
||||
AutomationStepSchema,
|
||||
|
@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
|
|||
type: AutomationStepType.ACTION,
|
||||
stepId: AutomationActionStepId.EXECUTE_QUERY,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -4,6 +4,7 @@ import * as automationUtils from "../automationUtils"
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
AutomationIOType,
|
||||
AutomationStepInput,
|
||||
AutomationStepSchema,
|
||||
|
@ -19,6 +20,9 @@ export const definition: AutomationStepSchema = {
|
|||
internal: true,
|
||||
stepId: AutomationActionStepId.EXECUTE_SCRIPT,
|
||||
inputs: {},
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
schema: {
|
||||
inputs: {
|
||||
properties: {
|
||||
|
|
|
@ -28,6 +28,7 @@ export const definition: AutomationStepSchema = {
|
|||
"Conditionally halt automations which do not meet certain conditions",
|
||||
type: AutomationStepType.LOGIC,
|
||||
internal: true,
|
||||
features: {},
|
||||
stepId: AutomationActionStepId.FILTER,
|
||||
inputs: {
|
||||
condition: FilterConditions.EQUAL,
|
||||
|
|
|
@ -13,6 +13,7 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Loop",
|
||||
stepId: AutomationActionStepId.LOOP,
|
||||
internal: true,
|
||||
features: {},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
AutomationStepInput,
|
||||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
export const definition: AutomationStepSchema = {
|
||||
|
@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
|
|||
stepId: AutomationActionStepId.integromat,
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: false,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -22,6 +22,7 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Interact with the OpenAI ChatGPT API.",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {},
|
||||
stepId: AutomationActionStepId.OPENAI,
|
||||
inputs: {
|
||||
prompt: "",
|
||||
|
|
|
@ -4,6 +4,7 @@ import * as automationUtils from "../automationUtils"
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
AutomationIOType,
|
||||
AutomationStepInput,
|
||||
AutomationStepSchema,
|
||||
|
@ -32,6 +33,9 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Send a request of specified method to a URL",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
stepId: AutomationActionStepId.OUTGOING_WEBHOOK,
|
||||
inputs: {
|
||||
requestMethod: "POST",
|
||||
|
|
|
@ -6,6 +6,7 @@ import * as automationUtils from "../automationUtils"
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
AutomationIOType,
|
||||
AutomationStepInput,
|
||||
AutomationStepSchema,
|
||||
|
@ -42,6 +43,9 @@ export const definition: AutomationStepSchema = {
|
|||
type: AutomationStepType.ACTION,
|
||||
stepId: AutomationActionStepId.QUERY_ROWS,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
AutomationStepInput,
|
||||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
export const definition: AutomationStepSchema = {
|
||||
|
@ -15,6 +16,9 @@ export const definition: AutomationStepSchema = {
|
|||
name: "Send Email (SMTP)",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
stepId: AutomationActionStepId.SEND_EMAIL_SMTP,
|
||||
inputs: {},
|
||||
schema: {
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
AutomationStepInput,
|
||||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
/**
|
||||
|
@ -19,6 +20,9 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Logs the given text to the server (using console.log)",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
stepId: AutomationActionStepId.SERVER_LOG,
|
||||
inputs: {
|
||||
text: "",
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
AutomationStepInput,
|
||||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
export const definition: AutomationStepSchema = {
|
||||
|
@ -16,6 +17,9 @@ export const definition: AutomationStepSchema = {
|
|||
stepId: AutomationActionStepId.slack,
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: false,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
||||
|
|
|
@ -4,6 +4,7 @@ import { buildCtx } from "./utils"
|
|||
import {
|
||||
AutomationActionStepId,
|
||||
AutomationCustomIOType,
|
||||
AutomationFeature,
|
||||
AutomationIOType,
|
||||
AutomationStepInput,
|
||||
AutomationStepSchema,
|
||||
|
@ -17,6 +18,9 @@ export const definition: AutomationStepSchema = {
|
|||
description: "Update a row in your database",
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: true,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
stepId: AutomationActionStepId.UPDATE_ROW,
|
||||
inputs: {},
|
||||
schema: {
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
AutomationStepInput,
|
||||
AutomationStepType,
|
||||
AutomationIOType,
|
||||
AutomationFeature,
|
||||
} from "@budibase/types"
|
||||
|
||||
export const definition: AutomationStepSchema = {
|
||||
|
@ -13,6 +14,9 @@ export const definition: AutomationStepSchema = {
|
|||
stepId: AutomationActionStepId.zapier,
|
||||
type: AutomationStepType.ACTION,
|
||||
internal: false,
|
||||
features: {
|
||||
[AutomationFeature.LOOPING]: true,
|
||||
},
|
||||
description: "Trigger a Zapier Zap via webhooks",
|
||||
tagline: "Trigger a Zapier Zap",
|
||||
icon: "ri-flashlight-line",
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as utils from "./utils"
|
|||
import env from "../environment"
|
||||
import { context, db as dbCore } from "@budibase/backend-core"
|
||||
import { Automation, Row, AutomationData, AutomationJob } from "@budibase/types"
|
||||
import { executeSynchronously } from "../threads/automation"
|
||||
|
||||
export const TRIGGER_DEFINITIONS = definitions
|
||||
const JOB_OPTS = {
|
||||
|
@ -91,7 +92,7 @@ emitter.on("row:delete", async function (event) {
|
|||
|
||||
export async function externalTrigger(
|
||||
automation: Automation,
|
||||
params: { fields: Record<string, any> },
|
||||
params: { fields: Record<string, any>; timeout?: number },
|
||||
{ getResponses }: { getResponses?: boolean } = {}
|
||||
) {
|
||||
if (
|
||||
|
@ -118,7 +119,7 @@ export async function externalTrigger(
|
|||
automation,
|
||||
}
|
||||
const job = { data } as AutomationJob
|
||||
return utils.processEvent(job)
|
||||
return executeSynchronously(job)
|
||||
} else {
|
||||
return automationQueue.add(data, JOB_OPTS)
|
||||
}
|
||||
|
|
|
@ -25,9 +25,8 @@ export async function runView(
|
|||
}))
|
||||
)
|
||||
let fn = (doc: Document, emit: any) => emit(doc._id)
|
||||
;(0, eval)(
|
||||
"fn = " + view?.map?.replace("function (doc)", "function (doc, emit)")
|
||||
)
|
||||
// BUDI-7060 -> indirect eval call appears to cause issues in cloud
|
||||
eval("fn = " + view?.map?.replace("function (doc)", "function (doc, emit)"))
|
||||
const queryFns: any = {
|
||||
meta: view.meta,
|
||||
map: fn,
|
||||
|
|
|
@ -20,7 +20,9 @@ const SCHEMA: Integration = {
|
|||
"Airtable is a spreadsheet-database hybrid, with the features of a database but applied to a spreadsheet.",
|
||||
friendlyName: "Airtable",
|
||||
type: "Spreadsheet",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
apiKey: {
|
||||
type: DatasourceFieldType.PASSWORD,
|
||||
|
|
|
@ -23,7 +23,9 @@ const SCHEMA: Integration = {
|
|||
type: "Non-relational",
|
||||
description:
|
||||
"ArangoDB is a scalable open-source multi-model database natively supporting graph, document and search. All supported data models & access patterns can be combined in queries allowing for maximal flexibility. ",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
url: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -20,7 +20,9 @@ const SCHEMA: Integration = {
|
|||
type: "Non-relational",
|
||||
description:
|
||||
"Apache CouchDB is an open-source document-oriented NoSQL database, implemented in Erlang.",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
url: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -25,7 +25,9 @@ const SCHEMA: Integration = {
|
|||
"Amazon DynamoDB is a key-value and document database that delivers single-digit millisecond performance at any scale.",
|
||||
friendlyName: "DynamoDB",
|
||||
type: "Non-relational",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
region: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -22,7 +22,9 @@ const SCHEMA: Integration = {
|
|||
"Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.",
|
||||
friendlyName: "ElasticSearch",
|
||||
type: "Non-relational",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
url: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -20,7 +20,9 @@ const SCHEMA: Integration = {
|
|||
type: "Non-relational",
|
||||
description:
|
||||
"Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud.",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
email: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -66,10 +66,10 @@ const SCHEMA: Integration = {
|
|||
"Create and collaborate on online spreadsheets in real-time and from any device.",
|
||||
friendlyName: "Google Sheets",
|
||||
type: "Spreadsheet",
|
||||
features: [
|
||||
DatasourceFeature.CONNECTION_CHECKING,
|
||||
DatasourceFeature.FETCH_TABLE_NAMES,
|
||||
],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
[DatasourceFeature.FETCH_TABLE_NAMES]: true,
|
||||
},
|
||||
datasource: {
|
||||
spreadsheetId: {
|
||||
display: "Google Sheet URL",
|
||||
|
|
|
@ -40,10 +40,10 @@ const SCHEMA: Integration = {
|
|||
"Microsoft SQL Server is a relational database management system developed by Microsoft. ",
|
||||
friendlyName: "MS SQL Server",
|
||||
type: "Relational",
|
||||
features: [
|
||||
DatasourceFeature.CONNECTION_CHECKING,
|
||||
DatasourceFeature.FETCH_TABLE_NAMES,
|
||||
],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
[DatasourceFeature.FETCH_TABLE_NAMES]: true,
|
||||
},
|
||||
datasource: {
|
||||
user: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -40,7 +40,9 @@ const getSchema = () => {
|
|||
type: "Non-relational",
|
||||
description:
|
||||
"MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era.",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
connectionString: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -36,10 +36,10 @@ const SCHEMA: Integration = {
|
|||
type: "Relational",
|
||||
description:
|
||||
"MySQL Database Service is a fully managed database service to deploy cloud-native applications. ",
|
||||
features: [
|
||||
DatasourceFeature.CONNECTION_CHECKING,
|
||||
DatasourceFeature.FETCH_TABLE_NAMES,
|
||||
],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
[DatasourceFeature.FETCH_TABLE_NAMES]: true,
|
||||
},
|
||||
datasource: {
|
||||
host: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -50,10 +50,10 @@ const SCHEMA: Integration = {
|
|||
type: "Relational",
|
||||
description:
|
||||
"Oracle Database is an object-relational database management system developed by Oracle Corporation",
|
||||
features: [
|
||||
DatasourceFeature.CONNECTION_CHECKING,
|
||||
DatasourceFeature.FETCH_TABLE_NAMES,
|
||||
],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
[DatasourceFeature.FETCH_TABLE_NAMES]: true,
|
||||
},
|
||||
datasource: {
|
||||
host: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -52,10 +52,10 @@ const SCHEMA: Integration = {
|
|||
type: "Relational",
|
||||
description:
|
||||
"PostgreSQL, also known as Postgres, is a free and open-source relational database management system emphasizing extensibility and SQL compliance.",
|
||||
features: [
|
||||
DatasourceFeature.CONNECTION_CHECKING,
|
||||
DatasourceFeature.FETCH_TABLE_NAMES,
|
||||
],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
[DatasourceFeature.FETCH_TABLE_NAMES]: true,
|
||||
},
|
||||
datasource: {
|
||||
host: {
|
||||
type: DatasourceFieldType.STRING,
|
||||
|
|
|
@ -21,7 +21,9 @@ const SCHEMA: Integration = {
|
|||
"Redis is a caching tool, providing powerful key-value store capabilities.",
|
||||
friendlyName: "Redis",
|
||||
type: "Non-relational",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
host: {
|
||||
type: "string",
|
||||
|
|
|
@ -24,7 +24,9 @@ const SCHEMA: Integration = {
|
|||
"Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance.",
|
||||
friendlyName: "Amazon S3",
|
||||
type: "Object store",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
region: {
|
||||
type: "string",
|
||||
|
|
|
@ -22,7 +22,9 @@ const SCHEMA: Integration = {
|
|||
"Snowflake is a solution for data warehousing, data lakes, data engineering, data science, data application development, and securely sharing and consuming shared data.",
|
||||
friendlyName: "Snowflake",
|
||||
type: "Relational",
|
||||
features: [DatasourceFeature.CONNECTION_CHECKING],
|
||||
features: {
|
||||
[DatasourceFeature.CONNECTION_CHECKING]: true,
|
||||
},
|
||||
datasource: {
|
||||
account: {
|
||||
type: "string",
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import * as webhook from "./webhook"
|
||||
import * as utils from "./utils"
|
||||
|
||||
export default {
|
||||
webhook,
|
||||
utils,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { Automation, AutomationActionStepId } from "@budibase/types"
|
||||
|
||||
export function checkForCollectStep(automation: Automation) {
|
||||
return automation.definition.steps.some(
|
||||
(step: any) => step.stepId === AutomationActionStepId.COLLECT
|
||||
)
|
||||
}
|
|
@ -373,7 +373,7 @@ class TestConfiguration {
|
|||
|
||||
// HEADERS
|
||||
|
||||
defaultHeaders(extras = {}) {
|
||||
defaultHeaders(extras = {}, prodApp = false) {
|
||||
const tenantId = this.getTenantId()
|
||||
const authObj: AuthToken = {
|
||||
userId: this.defaultUserValues.globalUserId,
|
||||
|
@ -390,7 +390,9 @@ class TestConfiguration {
|
|||
...extras,
|
||||
}
|
||||
|
||||
if (this.appId) {
|
||||
if (prodApp) {
|
||||
headers[constants.Header.APP_ID] = this.prodAppId
|
||||
} else if (this.appId) {
|
||||
headers[constants.Header.APP_ID] = this.appId
|
||||
}
|
||||
return headers
|
||||
|
|
|
@ -199,6 +199,48 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
|
|||
return automation as Automation
|
||||
}
|
||||
|
||||
export function collectAutomation(tableId?: string): Automation {
|
||||
const automation: any = {
|
||||
name: "looping",
|
||||
type: "automation",
|
||||
definition: {
|
||||
steps: [
|
||||
{
|
||||
id: "b",
|
||||
type: "ACTION",
|
||||
internal: true,
|
||||
stepId: AutomationActionStepId.EXECUTE_SCRIPT,
|
||||
inputs: {
|
||||
code: "return [1,2,3]",
|
||||
},
|
||||
schema: BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT.schema,
|
||||
},
|
||||
{
|
||||
id: "c",
|
||||
type: "ACTION",
|
||||
internal: true,
|
||||
stepId: AutomationActionStepId.COLLECT,
|
||||
inputs: {
|
||||
collection: "{{ literal steps.1.value }}",
|
||||
},
|
||||
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
|
||||
},
|
||||
],
|
||||
trigger: {
|
||||
id: "a",
|
||||
type: "TRIGGER",
|
||||
event: "row:save",
|
||||
stepId: AutomationTriggerStepId.ROW_SAVED,
|
||||
inputs: {
|
||||
tableId,
|
||||
},
|
||||
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
|
||||
},
|
||||
},
|
||||
}
|
||||
return automation as Automation
|
||||
}
|
||||
|
||||
export function basicRow(tableId: string) {
|
||||
return {
|
||||
name: "Test Contact",
|
||||
|
|
|
@ -68,6 +68,7 @@ class Orchestrator {
|
|||
constructor(job: AutomationJob) {
|
||||
let automation = job.data.automation
|
||||
let triggerOutput = job.data.event
|
||||
let timeout = job.data.event.timeout
|
||||
const metadata = triggerOutput.metadata
|
||||
this._chainCount = metadata ? metadata.automationChainCount! : 0
|
||||
this._appId = triggerOutput.appId as string
|
||||
|
@ -240,7 +241,9 @@ class Orchestrator {
|
|||
let loopStepNumber: any = undefined
|
||||
let loopSteps: LoopStep[] | undefined = []
|
||||
let metadata
|
||||
let timeoutFlag = false
|
||||
let wasLoopStep = false
|
||||
let timeout = this._job.data.event.timeout
|
||||
// check if this is a recurring automation,
|
||||
if (isProdAppID(this._appId) && isRecurring(automation)) {
|
||||
metadata = await this.getMetadata()
|
||||
|
@ -251,6 +254,16 @@ class Orchestrator {
|
|||
}
|
||||
|
||||
for (let step of automation.definition.steps) {
|
||||
if (timeoutFlag) {
|
||||
break
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
setTimeout(() => {
|
||||
timeoutFlag = true
|
||||
}, timeout || 12000)
|
||||
}
|
||||
|
||||
stepCount++
|
||||
let input: any,
|
||||
iterations = 1,
|
||||
|
@ -495,6 +508,32 @@ export function execute(job: Job, callback: WorkerCallback) {
|
|||
})
|
||||
}
|
||||
|
||||
export function executeSynchronously(job: Job) {
|
||||
const appId = job.data.event.appId
|
||||
if (!appId) {
|
||||
throw new Error("Unable to execute, event doesn't contain app ID.")
|
||||
}
|
||||
|
||||
const timeoutPromise = new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error("Timeout exceeded"))
|
||||
}, job.data.event.timeout || 12000)
|
||||
})
|
||||
|
||||
return context.doInAppContext(appId, async () => {
|
||||
const envVars = await sdkUtils.getEnvironmentVariables()
|
||||
// put into automation thread for whole context
|
||||
return context.doInEnvironmentContext(envVars, async () => {
|
||||
const automationOrchestrator = new Orchestrator(job)
|
||||
const response = await Promise.race([
|
||||
automationOrchestrator.execute(),
|
||||
timeoutPromise,
|
||||
])
|
||||
return response
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const removeStalled = async (job: Job) => {
|
||||
const appId = job.data.event.appId
|
||||
if (!appId) {
|
||||
|
|
|
@ -57,6 +57,7 @@ export enum AutomationActionStepId {
|
|||
FILTER = "FILTER",
|
||||
QUERY_ROWS = "QUERY_ROWS",
|
||||
LOOP = "LOOP",
|
||||
COLLECT = "COLLECT",
|
||||
OPENAI = "OPENAI",
|
||||
// these used to be lowercase step IDs, maintain for backwards compat
|
||||
discord = "discord",
|
||||
|
@ -123,6 +124,11 @@ export interface AutomationStepSchema {
|
|||
outputs: InputOutputBlock
|
||||
}
|
||||
custom?: boolean
|
||||
features?: Partial<Record<AutomationFeature, boolean>>
|
||||
}
|
||||
|
||||
export enum AutomationFeature {
|
||||
LOOPING = "LOOPING",
|
||||
}
|
||||
|
||||
export interface AutomationStep extends AutomationStepSchema {
|
||||
|
|
|
@ -5,6 +5,7 @@ export interface AutomationDataEvent {
|
|||
appId?: string
|
||||
metadata?: AutomationMetadata
|
||||
automation?: Automation
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
export interface AutomationData {
|
||||
|
|
|
@ -116,7 +116,7 @@ export interface Integration {
|
|||
docs: string
|
||||
plus?: boolean
|
||||
auth?: { type: string }
|
||||
features?: DatasourceFeature[]
|
||||
features?: Partial<Record<DatasourceFeature, boolean>>
|
||||
relationships?: boolean
|
||||
description: string
|
||||
friendlyName: string
|
||||
|
|
|
@ -8,6 +8,7 @@ export enum Feature {
|
|||
ENFORCEABLE_SSO = "enforceableSSO",
|
||||
BRANDING = "branding",
|
||||
SCIM = "scim",
|
||||
SYNC_AUTOMATIONS = "syncAutomations",
|
||||
}
|
||||
|
||||
export type PlanFeatures = { [key in PlanType]: Feature[] | undefined }
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
*
|
||||
!/dist/
|
||||
!/docker_run.sh
|
||||
!/docker_run.sh
|
||||
!/package.json
|
|
@ -12,7 +12,7 @@ RUN apk add --no-cache --virtual .gyp python3 make g++
|
|||
RUN yarn global add pm2
|
||||
|
||||
|
||||
COPY dist/package.json .
|
||||
COPY package.json .
|
||||
RUN yarn install --frozen-lockfile --production=true
|
||||
# Remove unneeded data from file system to reduce image size
|
||||
RUN apk del .gyp \
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
],
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist/",
|
||||
"build": "cd ../.. && nx build @budibase/worker",
|
||||
"build": "node ../../scripts/build.js",
|
||||
"check:types": "tsc -p tsconfig.build.json --noEmit",
|
||||
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
||||
"run:docker": "node dist/index.js",
|
||||
"debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js",
|
||||
|
@ -38,7 +39,7 @@
|
|||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@budibase/backend-core": "0.0.1",
|
||||
"@budibase/pro": "develop",
|
||||
"@budibase/pro": "0.0.1",
|
||||
"@budibase/string-templates": "0.0.1",
|
||||
"@budibase/types": "0.0.1",
|
||||
"@koa/router": "8.0.8",
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"main": "packages/worker/src/index.ts",
|
||||
"outputPath": "packages/worker/dist",
|
||||
"outputFileName": "index.js",
|
||||
"tsConfig": "packages/worker/tsconfig.build.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*.hbs",
|
||||
"input": "packages/worker/src/constants/templates",
|
||||
"output": "."
|
||||
}
|
||||
],
|
||||
"external": ["graphql/*", "deasync", "mock-aws-s3", "nock"],
|
||||
"format": ["cjs"],
|
||||
"esbuildOptions": {
|
||||
"outExtension": {
|
||||
".js": ".js"
|
||||
},
|
||||
"sourcemap": true
|
||||
},
|
||||
"minify": true,
|
||||
"generatePackageJson": true,
|
||||
"skipTypeCheck": true
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"skipTypeCheck": false,
|
||||
"esbuildOptions": {
|
||||
"sourcemap": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependsOn": [
|
||||
{
|
||||
"projects": ["@budibase/types", "@budibase/string-templates"],
|
||||
"target": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const { execSync } = require("child_process")
|
||||
|
||||
// Get the list of workspaces with mismatched dependencies
|
||||
const output = execSync("yarn --silent workspaces info --json", {
|
||||
encoding: "utf-8",
|
||||
})
|
||||
const data = JSON.parse(output)
|
||||
|
||||
const packageJsonPath = path.join(
|
||||
data["@budibase/pro"].location,
|
||||
"package.json"
|
||||
)
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"))
|
||||
|
||||
let hasChanges = false
|
||||
|
||||
const dependencies = data["@budibase/pro"].workspaceDependencies
|
||||
dependencies.forEach(dependency => {
|
||||
if (packageJson.dependencies?.[dependency]) {
|
||||
packageJson.dependencies[dependency] = "0.0.1"
|
||||
hasChanges = true
|
||||
}
|
||||
if (packageJson.devDependencies?.[dependency]) {
|
||||
packageJson.devDependencies[dependency] = "0.0.1"
|
||||
hasChanges = true
|
||||
}
|
||||
if (packageJson.peerDependencies?.[dependency]) {
|
||||
packageJson.peerDependencies[dependency] = "0.0.1"
|
||||
hasChanges = true
|
||||
}
|
||||
})
|
||||
|
||||
// Write changes to package.json if there are any
|
||||
if (hasChanges) {
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n")
|
||||
}
|
149
yarn.lock
149
yarn.lock
|
@ -1728,47 +1728,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@2.6.19-alpha.4":
|
||||
version "2.6.19-alpha.4"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.6.19-alpha.4.tgz#525dd7ba6c5db4404cf00d1165f79d34a1093826"
|
||||
integrity sha512-1pfOr+J9xYawedVmvqpQ4b/8C2SQP4cKhFmSz5uErM2SCgbRj+JuzOUTPNX0vzAXPvat/kEegt79xThummDvhA==
|
||||
dependencies:
|
||||
"@budibase/nano" "10.1.2"
|
||||
"@budibase/pouchdb-replication-stream" "1.2.10"
|
||||
"@budibase/types" "2.6.19-alpha.4"
|
||||
"@shopify/jest-koa-mocks" "5.0.1"
|
||||
"@techpass/passport-openidconnect" "0.3.2"
|
||||
aws-cloudfront-sign "2.2.0"
|
||||
aws-sdk "2.1030.0"
|
||||
bcrypt "5.0.1"
|
||||
bcryptjs "2.4.3"
|
||||
bull "4.10.1"
|
||||
correlation-id "4.0.0"
|
||||
dotenv "16.0.1"
|
||||
emitter-listener "1.1.2"
|
||||
ioredis "4.28.0"
|
||||
joi "17.6.0"
|
||||
jsonwebtoken "9.0.0"
|
||||
koa-passport "4.1.4"
|
||||
koa-pino-logger "4.0.0"
|
||||
lodash "4.17.21"
|
||||
lodash.isarguments "3.1.0"
|
||||
node-fetch "2.6.7"
|
||||
passport-google-oauth "2.0.0"
|
||||
passport-jwt "4.0.0"
|
||||
passport-local "1.0.0"
|
||||
passport-oauth2-refresh "^2.1.0"
|
||||
pino "8.11.0"
|
||||
pino-http "8.3.3"
|
||||
posthog-node "1.3.0"
|
||||
pouchdb "7.3.0"
|
||||
pouchdb-find "7.2.2"
|
||||
redlock "4.2.0"
|
||||
sanitize-s3-objectkey "0.0.1"
|
||||
semver "7.3.7"
|
||||
tar-fs "2.1.1"
|
||||
uuid "8.3.2"
|
||||
|
||||
"@budibase/bbui@^0.9.139":
|
||||
version "0.9.190"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.190.tgz#e1ec400ac90f556bfbc80fc23a04506f1585ea81"
|
||||
|
@ -1869,32 +1828,6 @@
|
|||
pouchdb-promise "^6.0.4"
|
||||
through2 "^2.0.0"
|
||||
|
||||
"@budibase/pro@develop":
|
||||
version "2.6.19-alpha.4"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.6.19-alpha.4.tgz#5d4c885ac9ac4ccfb2f8896961aca903c1178750"
|
||||
integrity sha512-iu2QzV8Z77c00muBSK+NVsZdug3lLD0lR+vcKancGEz1PPE4yNIH7g8jB6i8h9agArbx9S2ICeHQqGb6nQaGHQ==
|
||||
dependencies:
|
||||
"@budibase/backend-core" "2.6.19-alpha.4"
|
||||
"@budibase/shared-core" "2.6.19-alpha.4"
|
||||
"@budibase/string-templates" "2.6.19-alpha.4"
|
||||
"@budibase/types" "2.6.19-alpha.4"
|
||||
"@koa/router" "8.0.8"
|
||||
bull "4.10.1"
|
||||
joi "17.6.0"
|
||||
jsonwebtoken "8.5.1"
|
||||
lru-cache "^7.14.1"
|
||||
memorystream "^0.3.1"
|
||||
node-fetch "^2.6.1"
|
||||
scim-patch "^0.7.0"
|
||||
scim2-parse-filter "^0.2.8"
|
||||
|
||||
"@budibase/shared-core@2.6.19-alpha.4":
|
||||
version "2.6.19-alpha.4"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/shared-core/-/shared-core-2.6.19-alpha.4.tgz#dd22dd0a18ee4d6739b629f461e5caec90706282"
|
||||
integrity sha512-ac6iWSsgz70OYbdA+QHPLpTnRbIZ4OecVc6Y7gnEZ78hZ4S5da8a+73jswuy0/t4YsiT/gjukjzjoihg3NemVg==
|
||||
dependencies:
|
||||
"@budibase/types" "2.6.19-alpha.4"
|
||||
|
||||
"@budibase/standard-components@^0.9.139":
|
||||
version "0.9.139"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.139.tgz#cf8e2b759ae863e469e50272b3ca87f2827e66e3"
|
||||
|
@ -1913,25 +1846,6 @@
|
|||
svelte-apexcharts "^1.0.2"
|
||||
svelte-flatpickr "^3.1.0"
|
||||
|
||||
"@budibase/string-templates@2.6.19-alpha.4":
|
||||
version "2.6.19-alpha.4"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.6.19-alpha.4.tgz#92ebd69a6841174b8af91f338c4754ca7c402707"
|
||||
integrity sha512-KsH3NlQcJibRj98Q8zQ3KQHhfSIWPQfvR80MmBTIe05llEZGox4re4pQQUnlMafaUEyNNtIqVnbTJ1XP0LmFng==
|
||||
dependencies:
|
||||
"@budibase/handlebars-helpers" "^0.11.8"
|
||||
dayjs "^1.10.4"
|
||||
handlebars "^4.7.6"
|
||||
handlebars-utils "^1.0.6"
|
||||
lodash "^4.17.20"
|
||||
vm2 "^3.9.15"
|
||||
|
||||
"@budibase/types@2.6.19-alpha.4":
|
||||
version "2.6.19-alpha.4"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.6.19-alpha.4.tgz#bcf81699329d3f8509e4b0a489211f35b6cfa7ce"
|
||||
integrity sha512-qFsXHZTSigcfCv02aTZGsf17vBT/MC+zK9ky7WZVX4h0sJiE0li4A66/tMaSDz3/vQ7ToPRhJK/p+LOWA/oceg==
|
||||
dependencies:
|
||||
scim-patch "^0.7.0"
|
||||
|
||||
"@bull-board/api@3.7.0":
|
||||
version "3.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.7.0.tgz#231f687187c0cb34e0b97f463917b6aaeb4ef6af"
|
||||
|
@ -3621,6 +3535,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.13":
|
||||
version "1.4.15"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
|
||||
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
||||
|
||||
"@jridgewell/trace-mapping@0.3.9":
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
|
||||
|
@ -4037,13 +3956,6 @@
|
|||
dependencies:
|
||||
"@nx/devkit" "16.2.2"
|
||||
|
||||
"@nrwl/esbuild@16.2.1":
|
||||
version "16.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@nrwl/esbuild/-/esbuild-16.2.1.tgz#83f8fbf2c7c541c220ccfff8be17a0e542aa7d01"
|
||||
integrity sha512-qyNpdtPAzk2IhYmK5Qzn9gM1cvEtWFp6vJfgdbCKQngdqioYcJpGtdKhRm6Vcj8mFXF7zDgtUd2ecqJSiJU6VA==
|
||||
dependencies:
|
||||
"@nx/esbuild" "16.2.1"
|
||||
|
||||
"@nrwl/js@16.2.1":
|
||||
version "16.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-16.2.1.tgz#6eacfa1f0658ca1e288da86b6c38be4846f2779a"
|
||||
|
@ -4096,21 +4008,6 @@
|
|||
tmp "~0.2.1"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@nx/esbuild@16.2.1":
|
||||
version "16.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@nx/esbuild/-/esbuild-16.2.1.tgz#157a408a617b5095ba1372ec27b03d6c9aa3b78c"
|
||||
integrity sha512-jG4zsGc1EN+SfEBUky44+4cpj7M0Sf0v3pGWsw4hCOuBeLl6qW+eAcFypDXecDosS7ct/M2nEtlZP45IVDMZgQ==
|
||||
dependencies:
|
||||
"@nrwl/esbuild" "16.2.1"
|
||||
"@nx/devkit" "16.2.1"
|
||||
"@nx/js" "16.2.1"
|
||||
chalk "^4.1.0"
|
||||
dotenv "~10.0.0"
|
||||
fast-glob "3.2.7"
|
||||
fs-extra "^11.1.0"
|
||||
tsconfig-paths "^4.1.2"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@nx/js@16.2.1":
|
||||
version "16.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@nx/js/-/js-16.2.1.tgz#41c8c2d610131fa064bbb2b6bb25bd67ed06be79"
|
||||
|
@ -4499,7 +4396,7 @@
|
|||
dependencies:
|
||||
slash "^3.0.0"
|
||||
|
||||
"@rollup/plugin-commonjs@^16.0.0":
|
||||
"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0":
|
||||
version "16.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f"
|
||||
integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw==
|
||||
|
@ -4582,6 +4479,22 @@
|
|||
"@rollup/pluginutils" "^3.1.0"
|
||||
magic-string "^0.25.7"
|
||||
|
||||
"@rollup/plugin-replace@^5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz#45f53501b16311feded2485e98419acb8448c61d"
|
||||
integrity sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^5.0.1"
|
||||
magic-string "^0.27.0"
|
||||
|
||||
"@rollup/plugin-typescript@8.3.0":
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz#bc1077fa5897b980fc27e376c4e377882c63e68b"
|
||||
integrity sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
|
||||
|
@ -12614,7 +12527,7 @@ fs.realpath@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||
|
||||
fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2:
|
||||
fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
|
@ -17662,6 +17575,13 @@ magic-string@^0.26.2:
|
|||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
magic-string@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3"
|
||||
integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.4.13"
|
||||
|
||||
make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
||||
|
@ -22488,6 +22408,13 @@ rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0,
|
|||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@2.45.2:
|
||||
version "2.45.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.2.tgz#8fb85917c9f35605720e92328f3ccbfba6f78b48"
|
||||
integrity sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.1"
|
||||
|
||||
rollup@^2.36.2, rollup@^2.44.0, rollup@^2.45.2, rollup@^2.79.1:
|
||||
version "2.79.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
|
||||
|
@ -24498,7 +24425,7 @@ timed-out@^4.0.1:
|
|||
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
|
||||
integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==
|
||||
|
||||
timekeeper@2.2.0:
|
||||
timekeeper@2.2.0, timekeeper@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368"
|
||||
integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A==
|
||||
|
|
Loading…
Reference in New Issue