Merge branch 'master' of github.com:Budibase/budibase into client-vite
This commit is contained in:
commit
e2e38ff704
|
@ -0,0 +1,28 @@
|
||||||
|
name: ReadMe GitHub Action 🦉
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rdme-openapi:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Use Node.js 20.x
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
cache: yarn
|
||||||
|
- run: yarn --frozen-lockfile
|
||||||
|
|
||||||
|
- name: update specs
|
||||||
|
run: cd packages/server && yarn specs
|
||||||
|
|
||||||
|
- name: Run `openapi` command
|
||||||
|
uses: readmeio/rdme@v8
|
||||||
|
with:
|
||||||
|
rdme: openapi specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=6728a74f5918b50036c61841
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||||
"version": "3.2.12",
|
"version": "3.2.13",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"concurrency": 20,
|
"concurrency": 20,
|
||||||
"command": {
|
"command": {
|
||||||
|
|
|
@ -5,4 +5,5 @@ package-lock.json
|
||||||
release/
|
release/
|
||||||
dist/
|
dist/
|
||||||
routify
|
routify
|
||||||
.routify/
|
.routify/
|
||||||
|
.rollup.cache
|
|
@ -4,14 +4,14 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"svelte-check": "svelte-check --no-tsconfig",
|
"build": "routify -b && NODE_OPTIONS=\"--max_old_space_size=4096\" vite build --emptyOutDir",
|
||||||
"build": "yarn svelte-check && routify -b && vite build --emptyOutDir",
|
|
||||||
"start": "routify -c rollup",
|
"start": "routify -c rollup",
|
||||||
"dev": "routify -c dev:vite",
|
"dev": "routify -c dev:vite",
|
||||||
"dev:vite": "vite --host 0.0.0.0",
|
"dev:vite": "vite --host 0.0.0.0",
|
||||||
"rollup": "rollup -c -w",
|
"rollup": "rollup -c -w",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest"
|
"test:watch": "vitest",
|
||||||
|
"check:types": "yarn svelte-check"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"globals": {
|
"globals": {
|
||||||
|
@ -89,6 +89,7 @@
|
||||||
"@babel/plugin-transform-runtime": "^7.13.10",
|
"@babel/plugin-transform-runtime": "^7.13.10",
|
||||||
"@babel/preset-env": "^7.13.12",
|
"@babel/preset-env": "^7.13.12",
|
||||||
"@rollup/plugin-replace": "^5.0.3",
|
"@rollup/plugin-replace": "^5.0.3",
|
||||||
|
"@rollup/plugin-typescript": "8.3.0",
|
||||||
"@roxi/routify": "2.18.12",
|
"@roxi/routify": "2.18.12",
|
||||||
"@sveltejs/vite-plugin-svelte": "1.4.0",
|
"@sveltejs/vite-plugin-svelte": "1.4.0",
|
||||||
"@testing-library/jest-dom": "6.4.2",
|
"@testing-library/jest-dom": "6.4.2",
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { get } from "svelte/store"
|
||||||
import { auth, navigation } from "./stores/portal"
|
import { auth, navigation } from "./stores/portal"
|
||||||
|
|
||||||
export const API = createAPIClient({
|
export const API = createAPIClient({
|
||||||
attachHeaders: headers => {
|
attachHeaders: (headers: Record<string, string>) => {
|
||||||
// Attach app ID header from store
|
// Attach app ID header from store
|
||||||
let appId = get(appStore).appId
|
let appId = get(appStore).appId
|
||||||
if (appId) {
|
if (appId) {
|
||||||
|
@ -16,13 +16,13 @@ export const API = createAPIClient({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add csrf token if authenticated
|
// Add csrf token if authenticated
|
||||||
const user = get(auth).user
|
const user: any = get(auth).user
|
||||||
if (user?.csrfToken) {
|
if (user?.csrfToken) {
|
||||||
headers["x-csrf-token"] = user.csrfToken
|
headers["x-csrf-token"] = user.csrfToken
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: error => {
|
onError: (error: any) => {
|
||||||
const { url, message, status, method, handled } = error || {}
|
const { url, message, status, method, handled } = error || {}
|
||||||
|
|
||||||
// Log any errors that we haven't manually handled
|
// Log any errors that we haven't manually handled
|
||||||
|
@ -45,14 +45,14 @@ export const API = createAPIClient({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onMigrationDetected: appId => {
|
onMigrationDetected: (appId: string) => {
|
||||||
const updatingUrl = `/builder/app/updating/${appId}`
|
const updatingUrl = `/builder/app/updating/${appId}`
|
||||||
|
|
||||||
if (window.location.pathname === updatingUrl) {
|
if (window.location.pathname === updatingUrl) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
get(navigation).goto(
|
get(navigation)?.goto(
|
||||||
`${updatingUrl}?returnUrl=${encodeURIComponent(window.location.pathname)}`
|
`${updatingUrl}?returnUrl=${encodeURIComponent(window.location.pathname)}`
|
||||||
)
|
)
|
||||||
},
|
},
|
|
@ -0,0 +1,8 @@
|
||||||
|
declare module "api" {
|
||||||
|
const API: {
|
||||||
|
getPlugins: () => Promise<any>
|
||||||
|
createPlugin: (plugin: object) => Promise<any>
|
||||||
|
uploadPlugin: (plugin: FormData) => Promise<any>
|
||||||
|
deletePlugin: (id: string) => Promise<void>
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,20 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
|
||||||
|
type GotoFuncType = (path: string) => void
|
||||||
|
|
||||||
|
interface Store {
|
||||||
|
initialisated: boolean
|
||||||
|
goto: GotoFuncType
|
||||||
|
}
|
||||||
|
|
||||||
export function createNavigationStore() {
|
export function createNavigationStore() {
|
||||||
const store = writable({
|
const store = writable<Store>({
|
||||||
initialisated: false,
|
initialisated: false,
|
||||||
goto: undefined,
|
goto: undefined as any,
|
||||||
})
|
})
|
||||||
const { set, subscribe } = store
|
const { set, subscribe } = store
|
||||||
|
|
||||||
const init = gotoFunc => {
|
const init = (gotoFunc: GotoFuncType) => {
|
||||||
if (typeof gotoFunc !== "function") {
|
if (typeof gotoFunc !== "function") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`gotoFunc must be a function, found a "${typeof gotoFunc}" instead`
|
`gotoFunc must be a function, found a "${typeof gotoFunc}" instead`
|
|
@ -1,16 +1,21 @@
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
|
import { PluginSource } from "constants/index"
|
||||||
|
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { PluginSource } from "constants"
|
|
||||||
|
interface Plugin {
|
||||||
|
_id: string
|
||||||
|
}
|
||||||
|
|
||||||
export function createPluginsStore() {
|
export function createPluginsStore() {
|
||||||
const { subscribe, set, update } = writable([])
|
const { subscribe, set, update } = writable<Plugin[]>([])
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const plugins = await API.getPlugins()
|
const plugins = await API.getPlugins()
|
||||||
set(plugins)
|
set(plugins)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deletePlugin(pluginId) {
|
async function deletePlugin(pluginId: string) {
|
||||||
await API.deletePlugin(pluginId)
|
await API.deletePlugin(pluginId)
|
||||||
update(state => {
|
update(state => {
|
||||||
state = state.filter(existing => existing._id !== pluginId)
|
state = state.filter(existing => existing._id !== pluginId)
|
||||||
|
@ -18,8 +23,8 @@ export function createPluginsStore() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPlugin(source, url, auth = null) {
|
async function createPlugin(source: string, url: string, auth = null) {
|
||||||
let pluginData = {
|
let pluginData: any = {
|
||||||
source,
|
source,
|
||||||
url,
|
url,
|
||||||
}
|
}
|
||||||
|
@ -46,7 +51,7 @@ export function createPluginsStore() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadPlugin(file) {
|
async function uploadPlugin(file: File) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -9,15 +9,9 @@
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"incremental": true
|
"incremental": true,
|
||||||
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["./src/**/*"],
|
||||||
"./src/**/*"
|
"exclude": ["node_modules", "**/*.json", "**/*.spec.ts", "**/*.spec.js"]
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
"**/*.json",
|
|
||||||
"**/*.spec.ts",
|
|
||||||
"**/*.spec.js"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import replace from "@rollup/plugin-replace"
|
||||||
import { defineConfig, loadEnv } from "vite"
|
import { defineConfig, loadEnv } from "vite"
|
||||||
import { viteStaticCopy } from "vite-plugin-static-copy"
|
import { viteStaticCopy } from "vite-plugin-static-copy"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
import typescript from "@rollup/plugin-typescript"
|
||||||
|
|
||||||
const ignoredWarnings = [
|
const ignoredWarnings = [
|
||||||
"unused-export-let",
|
"unused-export-let",
|
||||||
|
@ -35,7 +36,7 @@ export default defineConfig(({ mode }) => {
|
||||||
// Copy fonts to an additional path so that svelte's automatic
|
// Copy fonts to an additional path so that svelte's automatic
|
||||||
// prefixing of the base URL path can still resolve assets
|
// prefixing of the base URL path can still resolve assets
|
||||||
copyFonts("builder/fonts"),
|
copyFonts("builder/fonts"),
|
||||||
]
|
]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
test: {
|
test: {
|
||||||
|
@ -61,6 +62,7 @@ export default defineConfig(({ mode }) => {
|
||||||
sourcemap: !isProduction,
|
sourcemap: !isProduction,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
typescript({ outDir: "../server/builder/dist" }),
|
||||||
svelte({
|
svelte({
|
||||||
hot: !isProduction,
|
hot: !isProduction,
|
||||||
emitCss: true,
|
emitCss: true,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 25dd40ee12b048307b558ebcedb36548d6e042cd
|
Subproject commit 21a6a658e0765208fb6ebf21864497f2739dcb99
|
|
@ -2166,7 +2166,6 @@
|
||||||
"query": {
|
"query": {
|
||||||
"description": "Search parameters for view",
|
"description": "Search parameters for view",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"logicalOperator": {
|
"logicalOperator": {
|
||||||
"description": "When using groups this defines whether all of the filters must match, or only one of them.",
|
"description": "When using groups this defines whether all of the filters must match, or only one of them.",
|
||||||
|
@ -2449,7 +2448,6 @@
|
||||||
"query": {
|
"query": {
|
||||||
"description": "Search parameters for view",
|
"description": "Search parameters for view",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"logicalOperator": {
|
"logicalOperator": {
|
||||||
"description": "When using groups this defines whether all of the filters must match, or only one of them.",
|
"description": "When using groups this defines whether all of the filters must match, or only one of them.",
|
||||||
|
@ -2743,7 +2741,6 @@
|
||||||
"query": {
|
"query": {
|
||||||
"description": "Search parameters for view",
|
"description": "Search parameters for view",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"logicalOperator": {
|
"logicalOperator": {
|
||||||
"description": "When using groups this defines whether all of the filters must match, or only one of them.",
|
"description": "When using groups this defines whether all of the filters must match, or only one of them.",
|
||||||
|
|
|
@ -1802,7 +1802,6 @@ components:
|
||||||
query:
|
query:
|
||||||
description: Search parameters for view
|
description: Search parameters for view
|
||||||
type: object
|
type: object
|
||||||
required: []
|
|
||||||
properties:
|
properties:
|
||||||
logicalOperator:
|
logicalOperator:
|
||||||
description: When using groups this defines whether all of the filters must
|
description: When using groups this defines whether all of the filters must
|
||||||
|
@ -2012,7 +2011,6 @@ components:
|
||||||
query:
|
query:
|
||||||
description: Search parameters for view
|
description: Search parameters for view
|
||||||
type: object
|
type: object
|
||||||
required: []
|
|
||||||
properties:
|
properties:
|
||||||
logicalOperator:
|
logicalOperator:
|
||||||
description: When using groups this defines whether all of the filters must
|
description: When using groups this defines whether all of the filters must
|
||||||
|
@ -2229,7 +2227,6 @@ components:
|
||||||
query:
|
query:
|
||||||
description: Search parameters for view
|
description: Search parameters for view
|
||||||
type: object
|
type: object
|
||||||
required: []
|
|
||||||
properties:
|
properties:
|
||||||
logicalOperator:
|
logicalOperator:
|
||||||
description: When using groups this defines whether all of the filters must
|
description: When using groups this defines whether all of the filters must
|
||||||
|
|
|
@ -142,7 +142,6 @@ layeredFilterGroup.items.properties.groups = filterGroup
|
||||||
const viewQuerySchema = {
|
const viewQuerySchema = {
|
||||||
description: "Search parameters for view",
|
description: "Search parameters for view",
|
||||||
type: "object",
|
type: "object",
|
||||||
required: [],
|
|
||||||
properties: {
|
properties: {
|
||||||
logicalOperator,
|
logicalOperator,
|
||||||
onEmptyFilter: {
|
onEmptyFilter: {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import { basicTableWithAttachmentField } from "../../tests/utilities/structures"
|
import { basicTableWithAttachmentField } from "../../tests/utilities/structures"
|
||||||
import { objectStore } from "@budibase/backend-core"
|
import { objectStore } from "@budibase/backend-core"
|
||||||
|
import { createAutomationBuilder } from "./utilities/AutomationTestBuilder"
|
||||||
|
|
||||||
async function uploadTestFile(filename: string) {
|
async function uploadTestFile(filename: string) {
|
||||||
let bucket = "testbucket"
|
let bucket = "testbucket"
|
||||||
|
@ -13,6 +14,7 @@ async function uploadTestFile(filename: string) {
|
||||||
|
|
||||||
return presignedUrl
|
return presignedUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("test the create row action", () => {
|
describe("test the create row action", () => {
|
||||||
let table: any
|
let table: any
|
||||||
let row: any
|
let row: any
|
||||||
|
@ -31,30 +33,78 @@ describe("test the create row action", () => {
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
it("should be able to run the action", async () => {
|
||||||
const res = await setup.runStep(config, setup.actions.CREATE_ROW.stepId, {
|
const result = await createAutomationBuilder({
|
||||||
row,
|
name: "Test Create Row Flow",
|
||||||
|
appId: config.getAppId(),
|
||||||
|
config,
|
||||||
})
|
})
|
||||||
expect(res.id).toBeDefined()
|
.appAction({ fields: { status: "new" } })
|
||||||
expect(res.revision).toBeDefined()
|
.serverLog({ text: "Starting create row flow" }, { stepName: "StartLog" })
|
||||||
expect(res.success).toEqual(true)
|
.createRow({ row }, { stepName: "CreateRow" })
|
||||||
const gottenRow = await config.api.row.get(table._id, res.id)
|
.serverLog(
|
||||||
|
{ text: "Row created with ID: {{ stepsByName.CreateRow.row._id }}" },
|
||||||
|
{ stepName: "CreationLog" }
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(result.steps[1].outputs.success).toBeDefined()
|
||||||
|
expect(result.steps[1].outputs.id).toBeDefined()
|
||||||
|
expect(result.steps[1].outputs.revision).toBeDefined()
|
||||||
|
const gottenRow = await config.api.row.get(
|
||||||
|
table._id,
|
||||||
|
result.steps[1].outputs.id
|
||||||
|
)
|
||||||
expect(gottenRow.name).toEqual("test")
|
expect(gottenRow.name).toEqual("test")
|
||||||
expect(gottenRow.description).toEqual("test")
|
expect(gottenRow.description).toEqual("test")
|
||||||
|
expect(result.steps[2].outputs.message).toContain(
|
||||||
|
"Row created with ID: " + result.steps[1].outputs.id
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return an error (not throw) when bad info provided", async () => {
|
it("should return an error (not throw) when bad info provided", async () => {
|
||||||
const res = await setup.runStep(config, setup.actions.CREATE_ROW.stepId, {
|
const result = await createAutomationBuilder({
|
||||||
row: {
|
name: "Test Create Row Error Flow",
|
||||||
tableId: "invalid",
|
appId: config.getAppId(),
|
||||||
invalid: "invalid",
|
config,
|
||||||
},
|
|
||||||
})
|
})
|
||||||
expect(res.success).toEqual(false)
|
.appAction({ fields: { status: "error" } })
|
||||||
|
.serverLog({ text: "Starting error test flow" }, { stepName: "StartLog" })
|
||||||
|
.createRow(
|
||||||
|
{
|
||||||
|
row: {
|
||||||
|
tableId: "invalid",
|
||||||
|
invalid: "invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ stepName: "CreateRow" }
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(result.steps[1].outputs.success).toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should check invalid inputs return an error", async () => {
|
it("should check invalid inputs return an error", async () => {
|
||||||
const res = await setup.runStep(config, setup.actions.CREATE_ROW.stepId, {})
|
const result = await createAutomationBuilder({
|
||||||
expect(res.success).toEqual(false)
|
name: "Test Create Row Invalid Flow",
|
||||||
|
appId: config.getAppId(),
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
.appAction({ fields: { status: "invalid" } })
|
||||||
|
.serverLog({ text: "Testing invalid input" }, { stepName: "StartLog" })
|
||||||
|
.createRow({ row: {} }, { stepName: "CreateRow" })
|
||||||
|
.filter({
|
||||||
|
field: "{{ stepsByName.CreateRow.success }}",
|
||||||
|
condition: "equal",
|
||||||
|
value: true,
|
||||||
|
})
|
||||||
|
.serverLog(
|
||||||
|
{ text: "This log should not appear" },
|
||||||
|
{ stepName: "SkippedLog" }
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(result.steps[1].outputs.success).toEqual(false)
|
||||||
|
expect(result.steps.length).toBeLessThan(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should check that an attachment field is sent to storage and parsed", async () => {
|
it("should check that an attachment field is sent to storage and parsed", async () => {
|
||||||
|
@ -76,13 +126,33 @@ describe("test the create row action", () => {
|
||||||
]
|
]
|
||||||
|
|
||||||
attachmentRow.file_attachment = attachmentObject
|
attachmentRow.file_attachment = attachmentObject
|
||||||
const res = await setup.runStep(config, setup.actions.CREATE_ROW.stepId, {
|
const result = await createAutomationBuilder({
|
||||||
row: attachmentRow,
|
name: "Test Create Row Attachment Flow",
|
||||||
|
appId: config.getAppId(),
|
||||||
|
config,
|
||||||
})
|
})
|
||||||
|
.appAction({ fields: { type: "attachment" } })
|
||||||
|
.serverLog(
|
||||||
|
{ text: "Processing attachment upload" },
|
||||||
|
{ stepName: "StartLog" }
|
||||||
|
)
|
||||||
|
.createRow({ row: attachmentRow }, { stepName: "CreateRow" })
|
||||||
|
.filter({
|
||||||
|
field: "{{ stepsByName.CreateRow.success }}",
|
||||||
|
condition: "equal",
|
||||||
|
value: true,
|
||||||
|
})
|
||||||
|
.serverLog(
|
||||||
|
{
|
||||||
|
text: "Attachment uploaded with key: {{ stepsByName.CreateRow.row.file_attachment.0.key }}",
|
||||||
|
},
|
||||||
|
{ stepName: "UploadLog" }
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
|
||||||
expect(res.success).toEqual(true)
|
expect(result.steps[1].outputs.success).toEqual(true)
|
||||||
expect(res.row.file_attachment[0]).toHaveProperty("key")
|
expect(result.steps[1].outputs.row.file_attachment[0]).toHaveProperty("key")
|
||||||
let s3Key = res.row.file_attachment[0].key
|
let s3Key = result.steps[1].outputs.row.file_attachment[0].key
|
||||||
|
|
||||||
const client = objectStore.ObjectStore(objectStore.ObjectStoreBuckets.APPS)
|
const client = objectStore.ObjectStore(objectStore.ObjectStoreBuckets.APPS)
|
||||||
|
|
||||||
|
@ -111,13 +181,53 @@ describe("test the create row action", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentRow.single_file_attachment = attachmentObject
|
attachmentRow.single_file_attachment = attachmentObject
|
||||||
const res = await setup.runStep(config, setup.actions.CREATE_ROW.stepId, {
|
const result = await createAutomationBuilder({
|
||||||
row: attachmentRow,
|
name: "Test Create Row Single Attachment Flow",
|
||||||
|
appId: config.getAppId(),
|
||||||
|
config,
|
||||||
})
|
})
|
||||||
|
.appAction({ fields: { type: "single-attachment" } })
|
||||||
|
.serverLog(
|
||||||
|
{ text: "Processing single attachment" },
|
||||||
|
{ stepName: "StartLog" }
|
||||||
|
)
|
||||||
|
.createRow({ row: attachmentRow }, { stepName: "CreateRow" })
|
||||||
|
.branch({
|
||||||
|
success: {
|
||||||
|
steps: stepBuilder =>
|
||||||
|
stepBuilder
|
||||||
|
.serverLog(
|
||||||
|
{ text: "Single attachment processed" },
|
||||||
|
{ stepName: "ProcessLog" }
|
||||||
|
)
|
||||||
|
.serverLog(
|
||||||
|
{
|
||||||
|
text: "File key: {{ stepsByName.CreateRow.row.single_file_attachment.key }}",
|
||||||
|
},
|
||||||
|
{ stepName: "KeyLog" }
|
||||||
|
),
|
||||||
|
condition: {
|
||||||
|
equal: { "{{ stepsByName.CreateRow.success }}": true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
steps: stepBuilder =>
|
||||||
|
stepBuilder.serverLog(
|
||||||
|
{ text: "Failed to process attachment" },
|
||||||
|
{ stepName: "ErrorLog" }
|
||||||
|
),
|
||||||
|
condition: {
|
||||||
|
equal: { "{{ stepsByName.CreateRow.success }}": false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
expect(res.success).toEqual(true)
|
expect(result.steps[1].outputs.success).toEqual(true)
|
||||||
expect(res.row.single_file_attachment).toHaveProperty("key")
|
expect(result.steps[1].outputs.row.single_file_attachment).toHaveProperty(
|
||||||
let s3Key = res.row.single_file_attachment.key
|
"key"
|
||||||
|
)
|
||||||
|
let s3Key = result.steps[1].outputs.row.single_file_attachment.key
|
||||||
|
|
||||||
const client = objectStore.ObjectStore(objectStore.ObjectStoreBuckets.APPS)
|
const client = objectStore.ObjectStore(objectStore.ObjectStoreBuckets.APPS)
|
||||||
|
|
||||||
|
@ -146,13 +256,50 @@ describe("test the create row action", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentRow.single_file_attachment = attachmentObject
|
attachmentRow.single_file_attachment = attachmentObject
|
||||||
const res = await setup.runStep(config, setup.actions.CREATE_ROW.stepId, {
|
const result = await createAutomationBuilder({
|
||||||
row: attachmentRow,
|
name: "Test Create Row Invalid Attachment Flow",
|
||||||
|
appId: config.getAppId(),
|
||||||
|
config,
|
||||||
})
|
})
|
||||||
|
.appAction({ fields: { type: "invalid-attachment" } })
|
||||||
|
.serverLog(
|
||||||
|
{ text: "Testing invalid attachment keys" },
|
||||||
|
{ stepName: "StartLog" }
|
||||||
|
)
|
||||||
|
.createRow({ row: attachmentRow }, { stepName: "CreateRow" })
|
||||||
|
.branch({
|
||||||
|
success: {
|
||||||
|
steps: stepBuilder =>
|
||||||
|
stepBuilder.serverLog(
|
||||||
|
{ text: "Unexpected success" },
|
||||||
|
{ stepName: "UnexpectedLog" }
|
||||||
|
),
|
||||||
|
condition: {
|
||||||
|
equal: { "{{ stepsByName.CreateRow.success }}": true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
steps: stepBuilder =>
|
||||||
|
stepBuilder
|
||||||
|
.serverLog(
|
||||||
|
{ text: "Expected error occurred" },
|
||||||
|
{ stepName: "ErrorLog" }
|
||||||
|
)
|
||||||
|
.serverLog(
|
||||||
|
{ text: "Error: {{ stepsByName.CreateRow.response }}" },
|
||||||
|
{ stepName: "ErrorDetailsLog" }
|
||||||
|
),
|
||||||
|
condition: {
|
||||||
|
equal: { "{{ stepsByName.CreateRow.success }}": false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
expect(res.success).toEqual(false)
|
expect(result.steps[1].outputs.success).toEqual(false)
|
||||||
expect(res.response).toEqual(
|
expect(result.steps[1].outputs.response).toEqual(
|
||||||
'Error: Attachments must have both "url" and "filename" keys. You have provided: wrongKey, anotherWrongKey'
|
'Error: Attachments must have both "url" and "filename" keys. You have provided: wrongKey, anotherWrongKey'
|
||||||
)
|
)
|
||||||
|
expect(result.steps[2].outputs.status).toEqual("No branch condition met")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,52 +1,65 @@
|
||||||
|
import { createAutomationBuilder } from "./utilities/AutomationTestBuilder"
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
|
|
||||||
describe("test the delete row action", () => {
|
describe("test the delete row action", () => {
|
||||||
let table: any
|
let table: any,
|
||||||
let row: any
|
row: any,
|
||||||
let inputs: any
|
config = setup.getConfig()
|
||||||
let config = setup.getConfig()
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeAll(async () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
table = await config.createTable()
|
table = await config.createTable()
|
||||||
row = await config.createRow()
|
row = await config.createRow()
|
||||||
inputs = {
|
|
||||||
tableId: table._id,
|
|
||||||
id: row._id,
|
|
||||||
revision: row._rev,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
it("should be able to run the delete row action", async () => {
|
||||||
const res = await setup.runStep(
|
const builder = createAutomationBuilder({
|
||||||
config,
|
name: "Delete Row Automation",
|
||||||
setup.actions.DELETE_ROW.stepId,
|
})
|
||||||
inputs
|
|
||||||
)
|
|
||||||
expect(res.success).toEqual(true)
|
|
||||||
expect(res.response).toBeDefined()
|
|
||||||
expect(res.row._id).toEqual(row._id)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("check usage quota attempts", async () => {
|
await builder
|
||||||
await setup.runInProd(async () => {
|
.appAction({ fields: {} })
|
||||||
await setup.runStep(config, setup.actions.DELETE_ROW.stepId, inputs)
|
.deleteRow({
|
||||||
|
tableId: table._id,
|
||||||
|
id: row._id,
|
||||||
|
revision: row._rev,
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
|
await config.api.row.get(table._id, row._id, {
|
||||||
|
status: 404,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should check invalid inputs return an error", async () => {
|
it("should check invalid inputs return an error", async () => {
|
||||||
const res = await setup.runStep(config, setup.actions.DELETE_ROW.stepId, {})
|
const builder = createAutomationBuilder({
|
||||||
expect(res.success).toEqual(false)
|
name: "Invalid Inputs Automation",
|
||||||
|
})
|
||||||
|
|
||||||
|
const results = await builder
|
||||||
|
.appAction({ fields: {} })
|
||||||
|
.deleteRow({ tableId: "", id: "", revision: "" })
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(results.steps[0].outputs.success).toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return an error when table doesn't exist", async () => {
|
it("should return an error when table doesn't exist", async () => {
|
||||||
const res = await setup.runStep(config, setup.actions.DELETE_ROW.stepId, {
|
const builder = createAutomationBuilder({
|
||||||
tableId: "invalid",
|
name: "Nonexistent Table Automation",
|
||||||
id: "invalid",
|
|
||||||
revision: "invalid",
|
|
||||||
})
|
})
|
||||||
expect(res.success).toEqual(false)
|
|
||||||
|
const results = await builder
|
||||||
|
.appAction({ fields: {} })
|
||||||
|
.deleteRow({
|
||||||
|
tableId: "invalid",
|
||||||
|
id: "invalid",
|
||||||
|
revision: "invalid",
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(results.steps[0].outputs.success).toEqual(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,58 +8,83 @@ import {
|
||||||
Table,
|
Table,
|
||||||
TableSourceType,
|
TableSourceType,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
import { createAutomationBuilder } from "./utilities/AutomationTestBuilder"
|
||||||
|
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import * as uuid from "uuid"
|
import * as uuid from "uuid"
|
||||||
|
|
||||||
describe("test the update row action", () => {
|
describe("test the update row action", () => {
|
||||||
let table: Table, row: Row, inputs: any
|
let table: Table,
|
||||||
let config = setup.getConfig()
|
row: Row,
|
||||||
|
config = setup.getConfig()
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
table = await config.createTable()
|
table = await config.createTable()
|
||||||
row = await config.createRow()
|
row = await config.createRow()
|
||||||
inputs = {
|
|
||||||
rowId: row._id,
|
|
||||||
row: {
|
|
||||||
...row,
|
|
||||||
name: "Updated name",
|
|
||||||
// put a falsy option in to be removed
|
|
||||||
description: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
it("should be able to run the action", async () => {
|
it("should be able to run the update row action", async () => {
|
||||||
const res = await setup.runStep(
|
const builder = createAutomationBuilder({
|
||||||
config,
|
name: "Update Row Automation",
|
||||||
setup.actions.UPDATE_ROW.stepId,
|
})
|
||||||
inputs
|
|
||||||
|
const results = await builder
|
||||||
|
.appAction({ fields: {} })
|
||||||
|
.updateRow({
|
||||||
|
rowId: row._id!,
|
||||||
|
row: {
|
||||||
|
...row,
|
||||||
|
name: "Updated name",
|
||||||
|
description: "",
|
||||||
|
},
|
||||||
|
meta: {},
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(results.steps[0].outputs.success).toEqual(true)
|
||||||
|
const updatedRow = await config.api.row.get(
|
||||||
|
table._id!,
|
||||||
|
results.steps[0].outputs.id
|
||||||
)
|
)
|
||||||
expect(res.success).toEqual(true)
|
|
||||||
const updatedRow = await config.api.row.get(table._id!, res.id)
|
|
||||||
expect(updatedRow.name).toEqual("Updated name")
|
expect(updatedRow.name).toEqual("Updated name")
|
||||||
expect(updatedRow.description).not.toEqual("")
|
expect(updatedRow.description).not.toEqual("")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should check invalid inputs return an error", async () => {
|
it("should check invalid inputs return an error", async () => {
|
||||||
const res = await setup.runStep(config, setup.actions.UPDATE_ROW.stepId, {})
|
const builder = createAutomationBuilder({
|
||||||
expect(res.success).toEqual(false)
|
name: "Invalid Inputs Automation",
|
||||||
|
})
|
||||||
|
|
||||||
|
const results = await builder
|
||||||
|
.appAction({ fields: {} })
|
||||||
|
.updateRow({ meta: {}, row: {}, rowId: "" })
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(results.steps[0].outputs.success).toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return an error when table doesn't exist", async () => {
|
it("should return an error when table doesn't exist", async () => {
|
||||||
const res = await setup.runStep(config, setup.actions.UPDATE_ROW.stepId, {
|
const builder = createAutomationBuilder({
|
||||||
row: { _id: "invalid" },
|
name: "Nonexistent Table Automation",
|
||||||
rowId: "invalid",
|
|
||||||
})
|
})
|
||||||
expect(res.success).toEqual(false)
|
|
||||||
|
const results = await builder
|
||||||
|
.appAction({ fields: {} })
|
||||||
|
.updateRow({
|
||||||
|
row: { _id: "invalid" },
|
||||||
|
rowId: "invalid",
|
||||||
|
meta: {},
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
|
||||||
|
expect(results.steps[0].outputs.success).toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not overwrite links if those links are not set", async () => {
|
it("should not overwrite links if those links are not set", async () => {
|
||||||
let linkField: FieldSchema = {
|
const linkField: FieldSchema = {
|
||||||
type: FieldType.LINK,
|
type: FieldType.LINK,
|
||||||
name: "",
|
name: "",
|
||||||
fieldName: "",
|
fieldName: "",
|
||||||
|
@ -71,7 +96,7 @@ describe("test the update row action", () => {
|
||||||
tableId: InternalTable.USER_METADATA,
|
tableId: InternalTable.USER_METADATA,
|
||||||
}
|
}
|
||||||
|
|
||||||
let table = await config.api.table.save({
|
const table = await config.api.table.save({
|
||||||
name: uuid.v4(),
|
name: uuid.v4(),
|
||||||
type: "table",
|
type: "table",
|
||||||
sourceType: TableSourceType.INTERNAL,
|
sourceType: TableSourceType.INTERNAL,
|
||||||
|
@ -82,23 +107,22 @@ describe("test the update row action", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
let user1 = await config.createUser()
|
const user1 = await config.createUser()
|
||||||
let user2 = await config.createUser()
|
const user2 = await config.createUser()
|
||||||
|
|
||||||
let row = await config.api.row.save(table._id!, {
|
const row = await config.api.row.save(table._id!, {
|
||||||
user1: [{ _id: user1._id }],
|
user1: [{ _id: user1._id }],
|
||||||
user2: [{ _id: user2._id }],
|
user2: [{ _id: user2._id }],
|
||||||
})
|
})
|
||||||
|
|
||||||
let getResp = await config.api.row.get(table._id!, row._id!)
|
const builder = createAutomationBuilder({
|
||||||
expect(getResp.user1[0]._id).toEqual(user1._id)
|
name: "Link Preservation Automation",
|
||||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
})
|
||||||
|
|
||||||
let stepResp = await setup.runStep(
|
const results = await builder
|
||||||
config,
|
.appAction({ fields: {} })
|
||||||
setup.actions.UPDATE_ROW.stepId,
|
.updateRow({
|
||||||
{
|
rowId: row._id!,
|
||||||
rowId: row._id,
|
|
||||||
row: {
|
row: {
|
||||||
_id: row._id,
|
_id: row._id,
|
||||||
_rev: row._rev,
|
_rev: row._rev,
|
||||||
|
@ -106,17 +130,19 @@ describe("test the update row action", () => {
|
||||||
user1: [user2._id],
|
user1: [user2._id],
|
||||||
user2: "",
|
user2: "",
|
||||||
},
|
},
|
||||||
}
|
meta: {},
|
||||||
)
|
})
|
||||||
expect(stepResp.success).toEqual(true)
|
.run()
|
||||||
|
|
||||||
getResp = await config.api.row.get(table._id!, row._id!)
|
expect(results.steps[0].outputs.success).toEqual(true)
|
||||||
|
|
||||||
|
const getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
expect(getResp.user1[0]._id).toEqual(user2._id)
|
expect(getResp.user1[0]._id).toEqual(user2._id)
|
||||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
expect(getResp.user2[0]._id).toEqual(user2._id)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should overwrite links if those links are not set and we ask it do", async () => {
|
it("should overwrite links if those links are not set and we ask it to", async () => {
|
||||||
let linkField: FieldSchema = {
|
const linkField: FieldSchema = {
|
||||||
type: FieldType.LINK,
|
type: FieldType.LINK,
|
||||||
name: "",
|
name: "",
|
||||||
fieldName: "",
|
fieldName: "",
|
||||||
|
@ -128,7 +154,7 @@ describe("test the update row action", () => {
|
||||||
tableId: InternalTable.USER_METADATA,
|
tableId: InternalTable.USER_METADATA,
|
||||||
}
|
}
|
||||||
|
|
||||||
let table = await config.api.table.save({
|
const table = await config.api.table.save({
|
||||||
name: uuid.v4(),
|
name: uuid.v4(),
|
||||||
type: "table",
|
type: "table",
|
||||||
sourceType: TableSourceType.INTERNAL,
|
sourceType: TableSourceType.INTERNAL,
|
||||||
|
@ -139,23 +165,22 @@ describe("test the update row action", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
let user1 = await config.createUser()
|
const user1 = await config.createUser()
|
||||||
let user2 = await config.createUser()
|
const user2 = await config.createUser()
|
||||||
|
|
||||||
let row = await config.api.row.save(table._id!, {
|
const row = await config.api.row.save(table._id!, {
|
||||||
user1: [{ _id: user1._id }],
|
user1: [{ _id: user1._id }],
|
||||||
user2: [{ _id: user2._id }],
|
user2: [{ _id: user2._id }],
|
||||||
})
|
})
|
||||||
|
|
||||||
let getResp = await config.api.row.get(table._id!, row._id!)
|
const builder = createAutomationBuilder({
|
||||||
expect(getResp.user1[0]._id).toEqual(user1._id)
|
name: "Link Overwrite Automation",
|
||||||
expect(getResp.user2[0]._id).toEqual(user2._id)
|
})
|
||||||
|
|
||||||
let stepResp = await setup.runStep(
|
const results = await builder
|
||||||
config,
|
.appAction({ fields: {} })
|
||||||
setup.actions.UPDATE_ROW.stepId,
|
.updateRow({
|
||||||
{
|
rowId: row._id!,
|
||||||
rowId: row._id,
|
|
||||||
row: {
|
row: {
|
||||||
_id: row._id,
|
_id: row._id,
|
||||||
_rev: row._rev,
|
_rev: row._rev,
|
||||||
|
@ -170,11 +195,12 @@ describe("test the update row action", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
)
|
.run()
|
||||||
expect(stepResp.success).toEqual(true)
|
|
||||||
|
|
||||||
getResp = await config.api.row.get(table._id!, row._id!)
|
expect(results.steps[0].outputs.success).toEqual(true)
|
||||||
|
|
||||||
|
const getResp = await config.api.row.get(table._id!, row._id!)
|
||||||
expect(getResp.user1[0]._id).toEqual(user2._id)
|
expect(getResp.user1[0]._id).toEqual(user2._id)
|
||||||
expect(getResp.user2).toBeUndefined()
|
expect(getResp.user2).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue