Merge branch 'develop' into subdomain-tenancy

This commit is contained in:
Rory Powell 2022-11-15 16:18:59 +00:00
commit ad81adb30f
89 changed files with 3529 additions and 4809 deletions

View File

@ -16,7 +16,8 @@
"dist",
"public",
"*.spec.js",
"bundle.js"
"bundle.js",
"coverage"
],
"plugins": ["svelte3"],
"extends": ["eslint:recommended"],

View File

@ -58,7 +58,7 @@ jobs:
- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
files: ./packages/server/coverage/clover.xml
files: ./packages/server/coverage/clover.xml,./packages/worker/coverage/clover.xml,./packages/backend-core/coverage/clover.xml
name: codecov-umbrella
verbose: true

View File

@ -1,5 +1,5 @@
{
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"npmClient": "yarn",
"packages": [
"packages/*"

View File

@ -18,7 +18,7 @@
"rimraf": "^3.0.2",
"rollup-plugin-replace": "^2.2.0",
"svelte": "^3.38.2",
"typescript": "4.5.5"
"typescript": "4.7.3"
},
"scripts": {
"setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev",

View File

@ -0,0 +1,15 @@
const mockS3 = {
headBucket: jest.fn().mockReturnThis(),
deleteObject: jest.fn().mockReturnThis(),
deleteObjects: jest.fn().mockReturnThis(),
createBucket: jest.fn().mockReturnThis(),
listObjects: jest.fn().mockReturnThis(),
promise: jest.fn().mockReturnThis(),
catch: jest.fn(),
}
const AWS = {
S3: jest.fn(() => mockS3),
}
export default AWS

View File

@ -0,0 +1 @@
jest.mock("node-fetch", () => jest.fn())

View File

@ -0,0 +1,20 @@
import { Config } from "@jest/types"
const config: Config.InitialOptions = {
preset: "ts-jest",
testEnvironment: "node",
setupFiles: ["./tests/jestSetup.ts"],
collectCoverageFrom: ["src/**/*.{js,ts}"],
coverageReporters: ["lcov", "json", "clover"],
}
if (!process.env.CI) {
// use sources when not in CI
config.moduleNameMapper = {
"@budibase/types": "<rootDir>/../types/src",
}
} else {
console.log("Running tests with compiled dependency sources")
}
export default config

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
@ -16,11 +16,11 @@
"prepack": "cp package.json dist",
"build": "tsc -p tsconfig.build.json",
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
"test": "jest",
"test": "jest --coverage",
"test:watch": "jest --watchAll"
},
"dependencies": {
"@budibase/types": "2.1.22-alpha.0",
"@budibase/types": "2.1.22-alpha.3",
"@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2",
"aws-sdk": "2.1030.0",
@ -52,16 +52,6 @@
"uuid": "8.3.2",
"zlib": "1.0.5"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"moduleNameMapper": {
"@budibase/types": "<rootDir>/../types/src"
},
"setupFiles": [
"./scripts/jestSetup.ts"
]
},
"devDependencies": {
"@types/chance": "1.1.3",
"@types/ioredis": "4.28.0",
@ -77,12 +67,14 @@
"@types/uuid": "8.3.4",
"chance": "1.1.3",
"ioredis-mock": "5.8.0",
"jest": "27.5.1",
"jest": "28.1.1",
"koa": "2.7.0",
"nodemon": "2.0.16",
"pouchdb-adapter-memory": "7.2.2",
"timekeeper": "2.2.0",
"ts-jest": "27.1.5",
"ts-jest": "28.0.4",
"ts-node": "10.8.1",
"tsconfig-paths": "4.0.0",
"typescript": "4.7.3"
},
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"

View File

@ -1,12 +0,0 @@
import env from "../src/environment"
import { mocks } from "../tests/utilities"
// mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests
import tk from "timekeeper"
tk.freeze(mocks.date.MOCK_DATE)
env._set("SELF_HOSTED", "1")
env._set("NODE_ENV", "jest")
env._set("JWT_SECRET", "test-jwtsecret")
env._set("LOG_LEVEL", "silent")

View File

@ -1,4 +1,4 @@
require("../../../tests/utilities/TestConfiguration")
require("../../../tests")
const { Writethrough } = require("../writethrough")
const { dangerousGetDB } = require("../../db")
const tk = require("timekeeper")

View File

@ -1,4 +1,4 @@
import "../../../tests/utilities/TestConfiguration"
import "../../../tests"
import * as context from ".."
import { DEFAULT_TENANT_ID } from "../../constants"
import env from "../../environment"

View File

@ -33,7 +33,7 @@ const checkInitialised = () => {
}
}
export async function init(opts?: PouchOptions) {
export function init(opts?: PouchOptions) {
Pouch = pouch.getPouch(opts)
initialised = true
}

View File

@ -1,5 +1,6 @@
import PouchDB from "pouchdb"
import env from "../environment"
import { PouchOptions } from "@budibase/types"
export const getUrlInfo = (url = env.COUCH_DB_URL) => {
let cleanUrl, username, password, host
@ -82,7 +83,7 @@ export const getCouchInfo = () => {
* This should be rarely used outside of the main application config.
* Exposed for exceptional cases such as in-memory views.
*/
export const getPouch = (opts: any = {}) => {
export const getPouch = (opts: PouchOptions = {}) => {
let { url, cookie } = getCouchInfo()
let POUCH_DB_DEFAULTS = {
prefix: url,

View File

@ -1,4 +1,4 @@
require("../../../tests/utilities/TestConfiguration")
require("../../../tests")
const { dangerousGetDB } = require("../")
describe("db", () => {

View File

@ -1,4 +1,4 @@
require("../../../tests/utilities/TestConfiguration")
require("../../../tests")
const getUrlInfo = require("../pouch").getUrlInfo
describe("pouch", () => {

View File

@ -1,4 +1,4 @@
require("../../../tests/utilities/TestConfiguration");
require("../../../tests");
const {
generateAppID,
getDevelopmentAppID,

View File

@ -1,4 +1,4 @@
import "../../../../../tests/utilities/TestConfiguration"
import "../../../../../tests"
import PosthogProcessor from "../PosthogProcessor"
import { Event, IdentityType, Hosting } from "@budibase/types"
const tk = require("timekeeper")

View File

@ -1,7 +1,6 @@
// Mock data
const mockFetch = require("node-fetch")
const { data } = require("./utilities/mock-data")
const issuer = "mockIssuer"
const sub = "mockSub"
const profile = {
@ -39,8 +38,6 @@ describe("oidc", () => {
const mockStrategy = require("@techpass/passport-openidconnect").Strategy
// mock the request to retrieve the oidc configuration
jest.mock("node-fetch")
const mockFetch = require("node-fetch")
mockFetch.mockReturnValue({
ok: true,
json: () => oidcConfigUrlResponse

View File

@ -1,4 +1,4 @@
require("../../../../tests/utilities/TestConfiguration")
require("../../../../tests")
const { authenticateThirdParty } = require("../third-party-common")
const { data } = require("./utilities/mock-data")
const { DEFAULT_TENANT_ID } = require("../../../constants")

View File

@ -1,4 +1,4 @@
require("../../../tests/utilities/TestConfiguration")
require("../../../tests")
const { runMigrations, getMigrationsDoc } = require("../index")
const { dangerousGetDB } = require("../../db")
const {

View File

@ -29,6 +29,7 @@ class InMemoryQueue {
_messages: any[]
_emitter: EventEmitter
_runCount: number
_addCount: number
/**
* The constructor the queue, exactly the same as that of Bulls.
* @param {string} name The name of the queue which is being configured.
@ -41,6 +42,7 @@ class InMemoryQueue {
this._messages = []
this._emitter = new events.EventEmitter()
this._runCount = 0
this._addCount = 0
}
/**
@ -81,6 +83,7 @@ class InMemoryQueue {
throw "Queue only supports carrying JSON."
}
this._messages.push(newJob(this._name, msg))
this._addCount++
this._emitter.emit("message")
}
@ -128,12 +131,9 @@ class InMemoryQueue {
}
async waitForCompletion() {
const currentCount = this._runCount
let increased = false
do {
await timeout(50)
increased = this._runCount > currentCount
} while (!increased)
} while (this._addCount < this._runCount)
}
}

View File

@ -1,5 +1,4 @@
require("../../tests/utilities/TestConfiguration")
const { structures } = require("../../tests/utilities")
const { structures } = require("../../tests")
const utils = require("../utils")
const events = require("../events")
const { doInTenant, DEFAULT_TENANT_ID }= require("../context")

View File

@ -0,0 +1,23 @@
import env from "../src/environment"
import { mocks } from "./utilities"
// mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests
import tk from "timekeeper"
tk.freeze(mocks.date.MOCK_DATE)
env._set("SELF_HOSTED", "1")
env._set("NODE_ENV", "jest")
env._set("JWT_SECRET", "test-jwtsecret")
env._set("LOG_LEVEL", "silent")
env._set("MINIO_URL", "http://localhost")
env._set("MINIO_ACCESS_KEY", "test")
env._set("MINIO_SECRET_KEY", "test")
global.console.log = jest.fn() // console.log are ignored in tests
if (!process.env.CI) {
// set a longer timeout in dev for debugging
// 100 seconds
jest.setTimeout(100000)
}

View File

@ -1 +0,0 @@
require("./db")

View File

@ -1,6 +0,0 @@
const core = require("../../src/index")
const dbConfig = {
inMemory: true,
allDbs: true,
}
core.init({ db: dbConfig })

View File

@ -0,0 +1,9 @@
import * as db from "../../src/db"
const dbConfig = {
inMemory: true,
}
export const init = () => {
db.init(dbConfig)
}

View File

@ -1,3 +1,6 @@
export * as mocks from "./mocks"
export * as structures from "./structures"
export { generator } from "./structures"
import * as dbConfig from "./db"
dbConfig.init()

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "2.1.22-alpha.0",
"@budibase/string-templates": "2.1.22-alpha.3",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2",

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ import filterTests from "../support/filterTests"
const interact = require('../support/interact')
filterTests(["smoke", "all"], () => {
context("Create a Table", () => {
xcontext("Create a Table", () => {
before(() => {
cy.login()
cy.createTestApp()

View File

@ -440,7 +440,7 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => {
// Creates an internal Budibase DB table
if (!initialTable) {
cy.navigateToDataSection()
cy.get(`[data-cy="new-table"]`, { timeout: 2000 }).click()
cy.get(`[data-cy="new-datasource"]`, { timeout: 2000 }).click()
}
cy.wait(2000)
cy.get(".item", { timeout: 2000 })
@ -781,7 +781,7 @@ Cypress.Commands.add("selectExternalDatasource", datasourceName => {
cy.navigateToDataSection()
// Open Datasource modal
cy.get(".nav").within(() => {
cy.get(".add-button").click()
cy.get("[data-cy='new-datasource']").click()
})
// Clicks specified datasource & continue
cy.get(".item-list", { timeout: 1000 }).contains(datasourceName).click()

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"license": "GPL-3.0",
"private": true,
"scripts": {
@ -71,10 +71,10 @@
}
},
"dependencies": {
"@budibase/bbui": "2.1.22-alpha.0",
"@budibase/client": "2.1.22-alpha.0",
"@budibase/frontend-core": "2.1.22-alpha.0",
"@budibase/string-templates": "2.1.22-alpha.0",
"@budibase/bbui": "2.1.22-alpha.3",
"@budibase/client": "2.1.22-alpha.3",
"@budibase/frontend-core": "2.1.22-alpha.3",
"@budibase/string-templates": "2.1.22-alpha.3",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
@ -117,9 +117,9 @@
"start-server-and-test": "^1.12.1",
"svelte": "^3.48.0",
"svelte-jester": "^1.3.2",
"ts-node": "^10.4.0",
"ts-node": "10.8.1",
"tsconfig-paths": "4.0.0",
"typescript": "^4.5.5",
"typescript": "4.7.3",
"vite": "^3.0.8"
},
"gitHead": "115189f72a850bfb52b65ec61d932531bf327072"

View File

@ -1,7 +1,7 @@
<script>
import AutomationList from "./AutomationList.svelte"
import CreateAutomationModal from "./CreateAutomationModal.svelte"
import { Icon, Modal, Tabs, Tab } from "@budibase/bbui"
import { Modal, Tabs, Tab, Button, Layout } from "@budibase/bbui"
export let modal
export let webhookModal
@ -10,23 +10,18 @@
<div class="nav">
<Tabs selected="Automations">
<Tab title="Automations">
<Layout paddingX="L" paddingY="L" gap="S">
<Button cta wide on:click={modal.show}>Add automation</Button>
</Layout>
<AutomationList />
<Modal bind:this={modal}>
<CreateAutomationModal {webhookModal} />
</Modal>
</Tab>
</Tabs>
<div class="add-button">
<Icon hoverable name="AddCircle" on:click={modal.show} />
</div>
</div>
<style>
.add-button {
position: absolute;
top: var(--spacing-l);
right: var(--spacing-xl);
}
.nav {
overflow-y: auto;
background: var(--background);

View File

@ -76,7 +76,7 @@
</Body>
<Input
bind:value={name}
on:change={() => (nameTouched = true)}
on:input={() => (nameTouched = true)}
bind:error={nameError}
label="Name"
/>
@ -124,11 +124,14 @@
padding: var(--spectrum-alias-item-padding-s);
background: var(--spectrum-alias-background-color-secondary);
transition: 0.3s all;
border: solid var(--spectrum-alias-border-color);
border-radius: 5px;
box-sizing: border-box;
border-width: 2px;
}
.item:hover {
background: var(--spectrum-alias-background-color-tertiary);
}
.selected {
background: var(--spectrum-alias-background-color-tertiary);
}

View File

@ -18,6 +18,7 @@
export let meta
export let value = defaultValue || (meta.type === "boolean" ? false : "")
export let readonly
export let error
const resolveTimeStamp = timestamp => {
if (!timestamp) {
@ -50,6 +51,7 @@
/>
{:else if type === "datetime"}
<DatePicker
{error}
{label}
timeOnly={isTimeStamp}
enableTime={!meta?.dateOnly}
@ -57,18 +59,23 @@
bind:value
/>
{:else if type === "attachment"}
<Dropzone {label} bind:value />
<Dropzone {label} {error} bind:value />
{:else if type === "boolean"}
<Toggle text={label} bind:value data-cy="{meta.name}-input" />
<Toggle text={label} {error} bind:value data-cy="{meta.name}-input" />
{:else if type === "array" && meta.constraints.inclusion.length !== 0}
<Multiselect bind:value {label} options={meta.constraints.inclusion} />
<Multiselect
bind:value
{error}
{label}
options={meta.constraints.inclusion}
/>
{:else if type === "link"}
<LinkedRowSelector bind:linkedRows={value} schema={meta} />
<LinkedRowSelector {error} bind:linkedRows={value} schema={meta} />
{:else if type === "longform"}
{#if meta.useRichText}
<RichTextField {label} height="150px" bind:value />
<RichTextField {error} {label} height="150px" bind:value />
{:else}
<TextArea {label} height="150px" bind:value />
<TextArea {error} {label} height="150px" bind:value />
{/if}
{:else if type === "json"}
<Label>{label}</Label>
@ -77,12 +84,14 @@
mode="json"
on:change={({ detail }) => (value = detail.value)}
value={stringVal}
{error}
/>
{:else}
<Input
{label}
data-cy="{meta.name}-input"
{type}
{error}
bind:value
disabled={readonly}
/>

View File

@ -19,7 +19,7 @@
$: text = `${item}${selectedRows?.length === 1 ? "" : "s"}`
</script>
<Button icon="Delete" size="s" primary quiet on:click={modal.show}>
<Button icon="Delete" size="s" warning quiet on:click={modal.show}>
Delete
{selectedRows.length}
{text}

View File

@ -5,7 +5,6 @@
import RowFieldControl from "../RowFieldControl.svelte"
import { API } from "api"
import { ModalContent } from "@budibase/bbui"
import ErrorsBox from "components/common/ErrorsBox.svelte"
import { FIELDS } from "constants/backend"
const FORMULA_TYPE = FIELDS.FORMULA.type
@ -32,13 +31,15 @@
if (error.handled) {
const response = error.json
if (response?.errors) {
errors = Object.entries(response.errors)
.map(([key, error]) => ({ dataPath: key, message: error }))
.flat()
} else if (error.status === 400 && response?.validationErrors) {
errors = Object.keys(response.validationErrors).map(field => ({
message: `${field} ${response.validationErrors[field][0]}`,
}))
errors = response.errors
} else if (response?.validationErrors) {
const mappedErrors = {}
for (let field in response.validationErrors) {
mappedErrors[
field
] = `${field} ${response.validationErrors[field][0]}`
}
errors = mappedErrors
}
} else {
notifications.error("Failed to save row")
@ -54,11 +55,10 @@
confirmText={creating ? "Create Row" : "Save Row"}
onConfirm={saveRow}
>
<ErrorsBox {errors} />
{#each tableSchema as [key, meta]}
{#if !meta.autocolumn && meta.type !== FORMULA_TYPE}
<div>
<RowFieldControl {meta} bind:value={row[key]} />
<RowFieldControl error={errors[key]} {meta} bind:value={row[key]} />
</div>
{/if}
{/each}

View File

@ -35,7 +35,6 @@
var(--spectrum-alias-item-padding-m);
background: var(--spectrum-alias-background-color-secondary);
transition: background 0.13s ease-out;
border: solid var(--spectrum-alias-border-color);
border-radius: 5px;
box-sizing: border-box;
border-width: 2px;

View File

@ -201,7 +201,6 @@
var(--spectrum-alias-item-padding-m);
background: var(--spectrum-alias-background-color-secondary);
transition: background 0.13s ease-out;
border: solid var(--spectrum-alias-border-color);
border-radius: 5px;
box-sizing: border-box;
border-width: 2px;

View File

@ -1,6 +1,6 @@
<script>
import { redirect, params } from "@roxi/routify"
import { Icon, Tabs, Tab } from "@budibase/bbui"
import { Button, Tabs, Tab, Layout } from "@budibase/bbui"
import { BUDIBASE_INTERNAL_DB } from "constants"
import DatasourceNavigator from "components/backend/DatasourceNavigator/DatasourceNavigator.svelte"
import CreateDatasourceModal from "components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte"
@ -23,18 +23,15 @@
<div class="nav">
<Tabs {selected} on:select={selectFirstDatasource}>
<Tab title="Sources">
<DatasourceNavigator />
<Layout paddingX="L" paddingY="L" gap="S">
<Button dataCy={`new-datasource`} cta wide on:click={modal.show}
>Add source</Button
>
</Layout>
<CreateDatasourceModal bind:modal />
<DatasourceNavigator />
</Tab>
</Tabs>
<div
class="add-button"
data-cy={`new-${isExternal ? "datasource" : "table"}`}
>
{#if modal}
<Icon hoverable name="AddCircle" on:click={modal.show} />
{/if}
</div>
</div>
<div class="content">
<slot />

View File

@ -7,7 +7,8 @@
"cardsblock",
"repeaterblock",
"formblock",
"chartblock"
"chartblock",
"rowexplorer"
]
},
{
@ -84,4 +85,4 @@
"donut"
]
}
]
]

View File

@ -923,17 +923,12 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@cspotcode/source-map-consumer@0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==
"@cspotcode/source-map-support@0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@cspotcode/source-map-consumer" "0.8.0"
"@jridgewell/trace-mapping" "0.3.9"
"@cypress/request@^2.88.10":
version "2.88.10"
@ -1182,6 +1177,24 @@
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"@jridgewell/resolve-uri@^3.0.3":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@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"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@ -5935,12 +5948,12 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
ts-node@^10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7"
integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==
ts-node@10.8.1:
version "10.8.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.1.tgz#ea2bd3459011b52699d7e88daa55a45a1af4f066"
integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==
dependencies:
"@cspotcode/source-map-support" "0.7.0"
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
@ -5951,6 +5964,7 @@ ts-node@^10.4.0:
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
tsconfig-paths@4.0.0:
@ -6023,10 +6037,10 @@ typedarray-to-buffer@^3.1.5:
dependencies:
is-typedarray "^1.0.0"
typescript@^4.5.5:
version "4.5.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
typescript@4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0"
@ -6111,6 +6125,11 @@ uuid@^8.3.0, uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
v8-to-istanbul@^7.0.0:
version "7.1.2"
resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1"

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {
@ -26,9 +26,9 @@
"outputPath": "build"
},
"dependencies": {
"@budibase/backend-core": "2.1.22-alpha.0",
"@budibase/string-templates": "2.1.22-alpha.0",
"@budibase/types": "2.1.22-alpha.0",
"@budibase/backend-core": "2.1.22-alpha.3",
"@budibase/string-templates": "2.1.22-alpha.3",
"@budibase/types": "2.1.22-alpha.3",
"axios": "0.21.2",
"chalk": "4.1.0",
"cli-progress": "3.11.2",

View File

@ -5186,5 +5186,88 @@
"suffix": "repeater"
}
]
},
"rowexplorer": {
"block": true,
"name": "Row Explorer Block",
"icon": "PersonalizationField",
"size": {
"width": 600,
"height": 400
},
"settings": [
{
"type": "table",
"label": "Table",
"key": "dataSource",
"required": true
},
{
"type": "text",
"label": "Height",
"key": "height",
"defaultValue": "426px"
},
{
"section": true,
"name": "Cards",
"settings": [
{
"type": "field",
"label": "Search Field",
"key": "cardSearchField",
"nested": true
},
{
"type": "text",
"key": "cardTitle",
"label": "Title",
"nested": true,
"defaultValue": "Title"
},
{
"type": "text",
"key": "cardSubtitle",
"label": "Subtitle",
"nested": true,
"defaultValue": "Subtitle"
},
{
"type": "text",
"key": "cardDescription",
"label": "Description",
"nested": true,
"defaultValue": "Description"
},
{
"type": "text",
"key": "cardImageURL",
"label": "Image URL",
"nested": true
}
]
},
{
"section": true,
"name": "Details",
"settings": [
{
"type": "text",
"key": "detailTitle",
"label": "Title"
},
{
"type": "multifield",
"label": "Fields",
"key": "detailFields",
"nested": true
}
]
}
],
"context": {
"type": "schema",
"suffix": "repeater"
}
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/client",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
"@budibase/bbui": "2.1.22-alpha.0",
"@budibase/frontend-core": "2.1.22-alpha.0",
"@budibase/string-templates": "2.1.22-alpha.0",
"@budibase/bbui": "2.1.22-alpha.3",
"@budibase/frontend-core": "2.1.22-alpha.3",
"@budibase/string-templates": "2.1.22-alpha.3",
"@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3",

View File

@ -0,0 +1,252 @@
<script>
import Block from "components/Block.svelte"
import BlockComponent from "components/BlockComponent.svelte"
import { makePropSafe as safe } from "@budibase/string-templates"
import { generate } from "shortid"
export let dataSource
export let height
export let cardTitle
export let cardSubtitle
export let cardDescription
export let cardImageURL
export let cardSearchField
export let detailFields
export let detailTitle
const stateKey = generate()
let listDataProviderId
let listRepeaterId
</script>
<Block>
<BlockComponent
type="container"
props={{
direction: "row",
gap: "M",
}}
styles={{
custom: `
height: ${height} !important;
`,
}}
>
<BlockComponent
type="dataprovider"
order={0}
bind:id={listDataProviderId}
props={{
dataSource,
paginate: true,
limit: 10,
filter: [
{
id: 0,
field: cardSearchField,
operator: "fuzzy",
type: "string",
value: `{{ ${safe("state")}.${safe(stateKey + "-search")} }}`,
valueType: "Binding",
noValue: false,
},
],
}}
styles={{
custom: `
flex: 3;
overflow: scroll;
{{#if (and ${safe("state")}.${safe(stateKey)} ${safe(
"device"
)}.${safe("mobile")}) }}
display: none;
{{/if}}
`,
}}
>
<BlockComponent
type="form"
order={0}
styles={{
normal: {
"margin-bottom": "12px",
},
}}
>
<BlockComponent
type="stringfield"
props={{
placeholder: "Search...",
field: `${stateKey}-search`,
onChange: [
{
parameters: {
key: `${stateKey}-search`,
type: "set",
persist: null,
value: `{{ ${safe("eventContext")}.${safe("value")} }}`,
},
"##eventHandlerType": "Update State",
id: 0,
},
],
}}
/>
</BlockComponent>
<BlockComponent
type="repeater"
order={1}
bind:id={listRepeaterId}
context="repeater"
props={{
dataProvider: `{{ literal ${safe(listDataProviderId)} }}`,
direction: "column",
gap: "S",
noRowsMessage: "No data",
}}
>
<BlockComponent
type="spectrumcard"
props={{
title: cardTitle,
subtitle: cardSubtitle,
description: cardDescription,
imageURL: cardImageURL,
horizontal: true,
buttonOnClick: [
{
parameters: {
key: stateKey,
type: "set",
persist: null,
value: `{{ ${safe(listRepeaterId)}.${safe("_id")} }}`,
},
"##eventHandlerType": "Update State",
id: 0,
},
],
}}
styles={{
normal: {
width: "auto",
},
}}
/>
</BlockComponent>
</BlockComponent>
<BlockComponent
type="container"
order={1}
props={{
hAlign: "center",
vAlign: "middle",
size: "grow",
direction: "column",
}}
styles={{
custom: `
padding: 20px;
background-color: var(--spectrum-global-color-gray-50));
border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px;
flex: 4;
{{#if (or ${safe("state")}.${safe(stateKey)} ${safe("device")}.${safe(
"mobile"
)}) }}
display: none;
{{/if}}
`,
}}
>
<BlockComponent
type="icon"
order={0}
props={{
icon: "ri-list-check-2",
size: "ri-2x",
color: "var(--spectrum-global-color-gray-700)",
}}
styles={{
normal: {
"margin-bottom": "12px",
},
}}
/>
<BlockComponent
type="text"
order={1}
props={{
text: "Select a row to view its fields",
color: "var(--spectrum-global-color-gray-700)",
}}
/>
</BlockComponent>
<BlockComponent
type="container"
order={2}
props={{
hAlign: "center",
vAlign: "top",
size: "grow",
direction: "column",
}}
styles={{
custom: `
background-color: var(--spectrum-global-color-gray-50));
border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px;
padding: 20px;
overflow-y: scroll;
flex: 4;
{{#if (isFalsey ${safe("state")}.${safe(stateKey)}) }}
display: none;
{{/if}}
`,
}}
>
<BlockComponent
type="button"
order={0}
props={{
text: "← Back",
onClick: [
{
parameters: {
key: stateKey,
type: "set",
persist: null,
value: "",
},
"##eventHandlerType": "Update State",
id: 0,
},
],
}}
styles={{
custom: `
align-self: flex-end;
margin-bottom: 16px;
{{#if (not ${safe("device")}.${safe("mobile")}) }}
display: none;
{{/if}}
`,
}}
/>
<BlockComponent
type="formblock"
order={1}
props={{
showSaveButton: true,
dataSource,
actionType: "Update",
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
fields: detailFields,
title: detailTitle,
}}
/>
</BlockComponent>
</BlockComponent>
</Block>

View File

@ -3,3 +3,4 @@ export { default as cardsblock } from "./CardsBlock.svelte"
export { default as repeaterblock } from "./RepeaterBlock.svelte"
export { default as formblock } from "./FormBlock.svelte"
export { default as chartblock } from "./ChartBlock.svelte"
export { default as rowexplorer } from "./RowExplorer.svelte"

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
{
"name": "@budibase/frontend-core",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase",
"license": "MPL-2.0",
"svelte": "src/index.js",
"dependencies": {
"@budibase/bbui": "2.1.22-alpha.0",
"@budibase/bbui": "2.1.22-alpha.3",
"lodash": "^4.17.21",
"svelte": "^3.46.2"
}

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/sdk",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Budibase Public API SDK",
"author": "Budibase",
"license": "MPL-2.0",

View File

@ -0,0 +1,32 @@
import { Config } from "@jest/types"
import * as fs from "fs"
const config: Config.InitialOptions = {
preset: "ts-jest",
testEnvironment: "node",
setupFiles: ["./src/tests/jestSetup.ts"],
collectCoverageFrom: [
"src/**/*.{js,ts}",
// The use of coverage with couchdb view functions breaks tests
"!src/db/views/staticViews.*",
],
coverageReporters: ["lcov", "json", "clover"],
}
if (!process.env.CI) {
// use sources when not in CI
config.moduleNameMapper = {
"@budibase/backend-core/(.*)": "<rootDir>/../backend-core/$1",
"@budibase/backend-core": "<rootDir>/../backend-core/src",
"@budibase/types": "<rootDir>/../types/src",
}
// add pro sources if they exist
if (fs.existsSync("../../../budibase-pro")) {
config.moduleNameMapper["@budibase/pro"] =
"<rootDir>/../../../budibase-pro/packages/pro/src"
}
} else {
console.log("Running tests with compiled dependency sources")
}
export default config

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Budibase Web Server",
"main": "src/index.ts",
"repository": {
@ -36,40 +36,6 @@
"env:account:enable": "node scripts/account.js enable",
"env:account:disable": "node scripts/account.js disable"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"moduleNameMapper": {
"@budibase/backend-core/(.*)": "<rootDir>/../backend-core/$1",
"@budibase/backend-core": "<rootDir>/../backend-core/src",
"@budibase/types": "<rootDir>/../types/src"
},
"setupFiles": [
"./scripts/jestSetup.js"
],
"collectCoverageFrom": [
"src/**/*.js",
"!**/node_modules/**",
"!src/db/views/*.js",
"!src/api/controllers/deploy/**/*.js",
"!src/*.js",
"!src/api/controllers/static/**/*",
"!src/db/dynamoClient.js",
"!src/utilities/usageQuota.js",
"!src/api/routes/tests/**/*",
"!src/db/tests/**/*",
"!src/tests/**/*",
"!src/automations/tests/**/*",
"!src/utilities/fileProcessor.js",
"!src/utilities/fileSystem/**/*",
"!src/utilities/redis.js"
],
"coverageReporters": [
"lcov",
"json",
"clover"
]
},
"keywords": [
"budibase"
],
@ -77,11 +43,11 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "2.1.22-alpha.0",
"@budibase/client": "2.1.22-alpha.0",
"@budibase/pro": "2.1.22-alpha.0",
"@budibase/string-templates": "2.1.22-alpha.0",
"@budibase/types": "2.1.22-alpha.0",
"@budibase/backend-core": "2.1.22-alpha.3",
"@budibase/client": "2.1.22-alpha.3",
"@budibase/pro": "2.1.22-alpha.3",
"@budibase/string-templates": "2.1.22-alpha.3",
"@budibase/types": "2.1.22-alpha.3",
"@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0",
@ -179,7 +145,7 @@
"eslint": "6.8.0",
"ioredis-mock": "7.2.0",
"is-wsl": "2.2.0",
"jest": "27.5.1",
"jest": "28.1.1",
"jest-openapi": "0.14.2",
"nodemon": "2.0.15",
"openapi-types": "9.3.1",
@ -190,10 +156,10 @@
"supertest": "4.0.2",
"swagger-jsdoc": "6.1.0",
"timekeeper": "2.2.0",
"ts-jest": "27.1.3",
"ts-node": "10.5.0",
"ts-jest": "28.0.4",
"ts-node": "10.8.1",
"tsconfig-paths": "4.0.0",
"typescript": "4.6.2",
"typescript": "4.7.3",
"update-dotenv": "1.1.1"
},
"optionalDependencies": {

View File

@ -1,5 +1,5 @@
const { tmpdir } = require("os")
const env = require("../src/environment")
import env from "../environment"
env._set("SELF_HOSTED", "1")
env._set("NODE_ENV", "jest")
@ -8,8 +8,11 @@ env._set("CLIENT_ID", "test-client-id")
env._set("BUDIBASE_DIR", tmpdir("budibase-unittests"))
env._set("LOG_LEVEL", "silent")
env._set("PORT", 0)
env._set("MINIO_URL", "http://localhost")
env._set("MINIO_ACCESS_KEY", "test")
env._set("MINIO_SECRET_KEY", "test")
const { mocks } = require("@budibase/backend-core/tests")
import { mocks } from "@budibase/backend-core/tests"
// mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests
@ -17,3 +20,9 @@ const tk = require("timekeeper")
tk.freeze(mocks.date.MOCK_DATE)
global.console.log = jest.fn() // console.log are ignored in tests
if (!process.env.CI) {
// set a longer timeout in dev for debugging
// 100 seconds
jest.setTimeout(100000)
}

View File

@ -116,7 +116,9 @@ class TestConfiguration {
if (this.server) {
this.server.close()
}
cleanup(this.allApps.map(app => app.appId))
if (this.allApps) {
cleanup(this.allApps.map(app => app.appId))
}
}
// UTILS

View File

@ -8,7 +8,8 @@
"paths": {
"@budibase/types": ["../types/src"],
"@budibase/backend-core": ["../backend-core/src"],
"@budibase/backend-core/*": ["../backend-core/*"]
"@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/pro": ["../../../budibase-pro/packages/pro/src"]
}
},
"ts-node": {
@ -17,6 +18,7 @@
"references": [
{ "path": "../types" },
{ "path": "../backend-core" },
{ "path": "../../../budibase-pro/packages/pro" }
],
"include": [
"src/**/*",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/string-templates",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs",
"module": "dist/bundle.mjs",
@ -44,7 +44,7 @@
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.5.5"
"typescript": "4.7.3"
},
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
}

View File

@ -4337,10 +4337,10 @@ typeof-article@^0.1.1:
dependencies:
kind-of "^3.1.0"
typescript@^4.5.5:
version "4.5.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
typescript@4.7.3:
version "4.7.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
uglify-js@^3.1.4:
version "3.14.3"

View File

@ -1,6 +1,6 @@
{
"name": "@budibase/types",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Budibase types",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -40,6 +40,7 @@ export interface AppBackupMetadata {
}
export interface AppBackup extends Document, AppBackupMetadata {
_id: string
filename?: string
}

View File

@ -24,3 +24,9 @@ interface BulkDocResponse {
id: string
rev: string
}
export interface PutResponse {
ok: boolean
id: string
rev: string
}

View File

@ -1,10 +1,10 @@
import PouchDB from "pouchdb"
export type PouchOptions = {
inMemory: boolean
replication: boolean
onDisk: boolean
find: boolean
inMemory?: boolean
replication?: boolean
onDisk?: boolean
find?: boolean
}
export enum SortOption {

View File

@ -0,0 +1,15 @@
const mockS3 = {
headBucket: jest.fn().mockReturnThis(),
deleteObject: jest.fn().mockReturnThis(),
deleteObjects: jest.fn().mockReturnThis(),
createBucket: jest.fn().mockReturnThis(),
listObjects: jest.fn().mockReturnThis(),
promise: jest.fn().mockReturnThis(),
catch: jest.fn(),
}
const AWS = {
S3: jest.fn(() => mockS3),
}
export default AWS

View File

@ -0,0 +1 @@
jest.mock("node-fetch", () => jest.fn())

View File

@ -0,0 +1,28 @@
import { Config } from "@jest/types"
import * as fs from "fs"
const config: Config.InitialOptions = {
preset: "ts-jest",
testEnvironment: "node",
setupFiles: ["./src/tests/jestSetup.ts"],
collectCoverageFrom: ["src/**/*.{js,ts}"],
coverageReporters: ["lcov", "json", "clover"],
}
if (!process.env.CI) {
// use sources when not in CI
config.moduleNameMapper = {
"@budibase/backend-core/(.*)": "<rootDir>/../backend-core/$1",
"@budibase/backend-core": "<rootDir>/../backend-core/src",
"@budibase/types": "<rootDir>/../types/src",
}
// add pro sources if they exist
if (fs.existsSync("../../../budibase-pro")) {
config.moduleNameMapper["@budibase/pro"] =
"<rootDir>/../../../budibase-pro/packages/pro/src"
}
} else {
console.log("Running tests with compiled dependency sources")
}
export default config

View File

@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
"version": "2.1.22-alpha.0",
"version": "2.1.22-alpha.3",
"description": "Budibase background service",
"main": "src/index.ts",
"repository": {
@ -22,7 +22,7 @@
"build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION",
"dev:stack:init": "node ./scripts/dev/manage.js init",
"dev:builder": "npm run dev:stack:init && nodemon",
"test": "jest --runInBand",
"test": "jest --coverage --runInBand",
"test:watch": "jest --watch",
"env:multi:enable": "node scripts/multiTenancy.js enable",
"env:multi:disable": "node scripts/multiTenancy.js disable",
@ -36,10 +36,10 @@
"author": "Budibase",
"license": "GPL-3.0",
"dependencies": {
"@budibase/backend-core": "2.1.22-alpha.0",
"@budibase/pro": "2.1.22-alpha.0",
"@budibase/string-templates": "2.1.22-alpha.0",
"@budibase/types": "2.1.22-alpha.0",
"@budibase/backend-core": "2.1.22-alpha.3",
"@budibase/pro": "2.1.22-alpha.3",
"@budibase/string-templates": "2.1.22-alpha.3",
"@budibase/types": "2.1.22-alpha.3",
"@koa/router": "8.0.8",
"@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2",
@ -79,30 +79,18 @@
"@typescript-eslint/parser": "5.12.0",
"copyfiles": "2.4.1",
"eslint": "6.8.0",
"jest": "27.4.7",
"jest": "28.1.1",
"nodemon": "2.0.15",
"pouchdb-adapter-memory": "7.2.2",
"prettier": "2.3.1",
"rimraf": "3.0.2",
"supertest": "6.2.2",
"timekeeper": "2.2.0",
"ts-jest": "27.1.3",
"ts-node": "10.4.0",
"ts-jest": "28.0.4",
"ts-node": "10.8.1",
"tsconfig-paths": "4.0.0",
"typescript": "4.5.5",
"typescript": "4.7.3",
"update-dotenv": "1.1.1"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"moduleNameMapper": {
"@budibase/backend-core/(.*)": "<rootDir>/../backend-core/$1",
"@budibase/backend-core": "<rootDir>/../backend-core/src",
"@budibase/types": "<rootDir>/../types/src"
},
"setupFiles": [
"./scripts/jestSetup.js"
]
},
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
}

View File

@ -1,11 +0,0 @@
const core = require("@budibase/backend-core")
const env = require("../environment")
exports.init = () => {
const dbConfig = {}
if (env.isTest() && !env.COUCH_DB_URL) {
dbConfig.inMemory = true
dbConfig.allDbs = true
}
core.init({ db: dbConfig })
}

View File

@ -0,0 +1,10 @@
import core from "@budibase/backend-core"
import env from "../environment"
export const init = () => {
const dbConfig: any = {}
if (env.isTest() && !env.COUCH_DB_URL) {
dbConfig.inMemory = true
}
core.init({ db: dbConfig })
}

View File

@ -13,7 +13,7 @@ import { Scope } from "@sentry/node"
import { Event } from "@sentry/types/dist/event"
import Application from "koa"
import { bootstrap } from "global-agent"
import db from "./db"
import * as db from "./db"
db.init()
const Koa = require("koa")
const destroyable = require("server-destroy")

View File

@ -1,5 +1,5 @@
import "./mocks"
import dbConfig from "../db"
import * as dbConfig from "../db"
dbConfig.init()
import env from "../environment"
import controllers from "./controllers"

View File

@ -1,7 +1,7 @@
import mocks from "./mocks"
import { generator } from "@budibase/backend-core/tests"
import TestConfiguration from "./TestConfiguration"
import structures from "./structures"
import mocks from "./mocks"
import API from "./api"
import { v4 as uuid } from "uuid"

View File

@ -1,15 +1,18 @@
const env = require("../src/environment")
import env from "../environment"
env._set("SELF_HOSTED", "0")
env._set("NODE_ENV", "jest")
env._set("JWT_SECRET", "test-jwtsecret")
env._set("LOG_LEVEL", "silent")
env._set("MULTI_TENANCY", true)
env._set("MINIO_URL", "http://localhost")
env._set("MINIO_ACCESS_KEY", "test")
env._set("MINIO_SECRET_KEY", "test")
env._set("PLATFORM_URL", "http://localhost:10000")
env._set("INTERNAL_API_KEY", "test")
env._set("DISABLE_ACCOUNT_PORTAL", false)
const { mocks } = require("@budibase/backend-core/tests")
import { mocks } from "@budibase/backend-core/tests"
// mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests

View File

@ -1,17 +1,17 @@
{
"compilerOptions": {
"target": "es6",
"skipLibCheck": true,
"module": "commonjs",
"lib": ["es2020"],
"allowJs": true,
"outDir": "dist",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"types": [ "node", "jest" ],
"outDir": "dist",
"skipLibCheck": true
"types": [ "node", "jest"],
},
"include": [
"src/**/*"
@ -20,7 +20,7 @@
"node_modules",
"dist",
"src/tests",
"**/*.spec.ts",
"**/*.spec.js"
"**/*.spec.js",
"**/*.spec.ts"
]
}

View File

@ -8,7 +8,8 @@
"paths": {
"@budibase/types": ["../types/src"],
"@budibase/backend-core": ["../backend-core/src"],
"@budibase/backend-core/*": ["../backend-core/*"]
"@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/pro": ["../../../budibase-pro/packages/pro/src"]
}
},
"ts-node": {
@ -17,6 +18,7 @@
"references": [
{ "path": "../types" },
{ "path": "../backend-core" },
{ "path": "../../../budibase-pro/packages/pro" }
],
"include": [
"src/**/*",

File diff suppressed because it is too large Load Diff

View File

@ -41,13 +41,13 @@
"@types/node-fetch": "2.6.2",
"chance": "1.1.8",
"env-cmd": "^10.1.0",
"jest": "28.0.2",
"jest": "28.1.1",
"prettier": "2.7.1",
"start-server-and-test": "1.14.0",
"timekeeper": "2.2.0",
"ts-jest": "28.0.8",
"ts-node": "10.9.1",
"tsconfig-paths": "4.1.0",
"ts-node": "10.8.1",
"tsconfig-paths": "4.0.0",
"typescript": "4.7.3"
},
"dependencies": {

View File

@ -2,7 +2,6 @@ import { Application } from "@budibase/server/api/controllers/public/mapping/typ
import { App } from "@budibase/types"
import { Response } from "node-fetch"
import InternalAPIClient from "./InternalAPIClient"
import FormData from "form-data"
import { RouteConfig } from "../fixtures/types/routing"
import { AppPackageResponse } from "../fixtures/types/appPackage"
import { DeployConfig } from "../fixtures/types/deploy"
@ -11,32 +10,52 @@ import { UnpublishAppResponse } from "../fixtures/types/unpublishAppResponse"
export default class AppApi {
api: InternalAPIClient
constructor(apiClient: InternalAPIClient) {
this.api = apiClient
}
async fetch(): Promise<[Response, Application[]]> {
async fetchEmptyAppList(): Promise<[Response, Application[]]> {
const response = await this.api.get(`/applications?status=all`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json.length).toEqual(0)
return [response, json]
}
async fetchAllApplications(): Promise<[Response, Application[]]> {
const response = await this.api.get(`/applications?status=all`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json.length).toBeGreaterThanOrEqual(1)
return [response, json]
}
async canRender(): Promise<[Response, boolean]> {
const response = await this.api.get("/routing/client")
const json = await response.json()
return [response, Object.keys(json.routes).length > 0]
const publishedAppRenders = Object.keys(json.routes).length > 0
expect(response).toHaveStatusCode(200)
expect(publishedAppRenders).toBe(true)
return [response, publishedAppRenders]
}
async getAppPackage(appId: string): Promise<[Response, AppPackageResponse]> {
const response = await this.api.get(`/applications/${appId}/appPackage`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json.application.appId).toEqual(appId)
return [response, json]
}
async publish(): Promise<[Response, DeployConfig]> {
async publish(appUrl: string): Promise<[Response, DeployConfig]> {
const response = await this.api.post("/deploy")
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json).toEqual({
_id: expect.any(String),
appUrl: appUrl,
status: "SUCCESS",
})
return [response, json]
}
@ -57,9 +76,11 @@ export default class AppApi {
async sync(appId: string): Promise<[Response, responseMessage]> {
const response = await this.api.post(`/applications/${appId}/sync`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
return [response, json]
}
// TODO
async updateClient(
appId: string,
body: any
@ -72,21 +93,41 @@ export default class AppApi {
return [response, json]
}
async revert(appId: string): Promise<[Response, responseMessage]> {
async revertPublished(appId: string): Promise<[Response, responseMessage]> {
const response = await this.api.post(`/dev/${appId}/revert`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json).toEqual({
message: "Reverted changes successfully.",
})
return [response, json]
}
async revertUnpublished(appId: string): Promise<[Response, responseMessage]> {
const response = await this.api.post(`/dev/${appId}/revert`)
const json = await response.json()
expect(response).toHaveStatusCode(400)
expect(json).toEqual({
message: "App has not yet been deployed",
status: 400,
})
return [response, json]
}
async delete(appId: string): Promise<[Response, any]> {
const response = await this.api.del(`/applications/${appId}`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
return [response, json]
}
async update(appId: string, body: any): Promise<[Response, Application]> {
async update(appId: string, oldName: string, body: any): Promise<[Response, Application]> {
const response = await this.api.put(`/applications/${appId}`, { body })
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json.name).not.toEqual(oldName)
return [response, json]
}
@ -96,9 +137,17 @@ export default class AppApi {
return [response, json]
}
async getRoutes(): Promise<[Response, RouteConfig]> {
async getRoutes(screenExists?: boolean): Promise<[Response, RouteConfig]> {
const response = await this.api.get(`/routing`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
if (screenExists) {
expect(json.routes["/test"]).toBeTruthy()
} else {
expect(json.routes["/test"]).toBeUndefined()
}
return [response, json]
}

View File

@ -4,25 +4,36 @@ import InternalAPIClient from "./InternalAPIClient"
export default class RowsApi {
api: InternalAPIClient
rowAdded: boolean
constructor(apiClient: InternalAPIClient) {
this.api = apiClient
this.rowAdded = false
}
async getAll(tableId: string): Promise<[Response, Row[]]> {
const response = await this.api.get(`/${tableId}/rows`)
const json = await response.json()
if (this.rowAdded) {
expect(response).toHaveStatusCode(200)
expect(json.length).toEqual(1)
}
return [response, json]
}
async add(tableId: string, body: any): Promise<[Response, Row]> {
const response = await this.api.post(`/${tableId}/rows`, { body })
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json._id).toBeDefined()
expect(json._rev).toBeDefined()
expect(json.tableId).toEqual(tableId)
this.rowAdded = true
return [response, json]
}
async delete(tableId: string, body: any): Promise<[Response, Row[]]> {
const response = await this.api.del(`/${tableId}/rows/`, { body })
const json = await response.json()
expect(response).toHaveStatusCode(200)
return [response, json]
}
}

View File

@ -12,12 +12,16 @@ export default class ScreenApi {
async create(body: any): Promise<[Response, Screen]> {
const response = await this.api.post(`/screens`, { body })
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json._id).toBeDefined()
expect(json.routing.roleId).toBe(body.routing.roleId)
return [response, json]
}
async delete(screenId: string, rev: string): Promise<[Response, Screen]> {
const response = await this.api.del(`/screens/${screenId}/${rev}`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
return [response, json]
}
}

View File

@ -21,12 +21,21 @@ export default class TablesApi {
async getTableById(id: string): Promise<[Response, Table]> {
const response = await this.api.get(`/tables/${id}`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json._id).toEqual(id)
return [response, json]
}
async save(body: any): Promise<[Response, Table]> {
async save(body: any, columnAdded?: boolean): Promise<[Response, Table]> {
const response = await this.api.post(`/tables`, { body })
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json._id).toBeDefined()
expect(json._rev).toBeDefined()
if (columnAdded) {
expect(json.schema.TestColumn).toBeDefined()
}
return [response, json]
}
@ -36,6 +45,10 @@ export default class TablesApi {
): Promise<[Response, responseMessage]> {
const response = await this.api.del(`/tables/${id}/${revId}`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json.message).toEqual(
`Table ${id} deleted.`
)
return [response, json]
}
}

View File

@ -1,11 +1,12 @@
import generator from "../../generator"
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
const generate = (
overrides: Partial<Application> = {}
): Partial<Application> => ({
name: generator.word(),
url: `/${generator.word()}`,
name: generator.word() + generator.hash(),
url: `/${generator.word() + generator.hash()}`,
...overrides,
})

View File

@ -0,0 +1,159 @@
import TestConfiguration from "../../../config/internal-api/TestConfiguration"
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
import { db } from "@budibase/backend-core"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
import generateApp from "../../../config/internal-api/fixtures/applications"
import generator from "../../../config/generator"
import generateScreen from "../../../config/internal-api/fixtures/screens"
describe("Internal API - Application creation, update, publish and delete", () => {
const api = new InternalAPIClient()
const config = new TestConfiguration<Application>(api)
beforeAll(async () => {
await config.beforeAll()
})
afterAll(async () => {
await config.afterAll()
})
async function createAppFromTemplate() {
return config.applications.create({
name: generator.word(),
url: `/${generator.word()}`,
useTemplate: "true",
templateName: "Near Miss Register",
templateKey: "app/near-miss-register",
templateFile: undefined,
})
}
it("Get applications without applications", async () => {
await config.applications.fetchEmptyAppList()
})
it("Get all Applications after creating an application", async () => {
await config.applications.create({
...generateApp(),
useTemplate: false,
})
await config.applications.fetchAllApplications()
})
it("Get application details", async () => {
const app = await config.applications.create({
...generateApp(),
useTemplate: false,
})
config.applications.api.appId = app.appId
const [appPackageResponse, appPackageJson] = await config.applications.getAppPackage(<string>app.appId)
expect(appPackageJson.application.name).toEqual(app.name)
expect(appPackageJson.application.version).toEqual(app.version)
expect(appPackageJson.application.url).toEqual(app.url)
expect(appPackageJson.application.tenantId).toEqual(app.tenantId)
expect(appPackageJson.application.status).toEqual(app.status)
})
it("Publish app", async () => {
// create the app
const appName = generator.word()
const app = await createAppFromTemplate()
config.applications.api.appId = app.appId
// check preview renders
await config.applications.canRender()
// publish app
await config.applications.publish(<string>app.url)
// check published app renders
config.applications.api.appId = db.getProdAppID(app.appId)
await config.applications.canRender()
// unpublish app
await config.applications.unpublish(<string>app.appId)
})
it("POST - Sync application before deployment", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
const [syncResponse, sync] = await config.applications.sync(
<string>app.appId
)
expect(sync).toEqual({
message: "App sync not required, app not deployed.",
})
})
it("POST - Sync application after deployment", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
// publish app
await config.applications.publish(<string>app.url)
const [syncResponse, sync] = await config.applications.sync(
<string>app.appId
)
expect(sync).toEqual({
message: "App sync completed successfully.",
})
})
it("PUT - Update an application", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
await config.applications.update(
<string>app.appId,
<string>app.name,
{
name: generator.word(),
}
)
})
it("POST - Revert Changes without changes", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
await config.applications.revertUnpublished(
<string>app.appId
)
})
it("POST - Revert Changes", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
// publish app
await config.applications.publish(<string>app.url)
// Change/add component to the app
await config.screen.create(
generateScreen("BASIC")
)
// // Revert the app to published state
await config.applications.revertPublished(
<string>app.appId
)
// Check screen is removed
await config.applications.getRoutes()
})
it("DELETE - Delete an application", async () => {
const app = await config.applications.create(generateApp())
await config.applications.delete(<string>app.appId)
})
})

View File

@ -1,266 +0,0 @@
import TestConfiguration from "../../../config/internal-api/TestConfiguration"
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
import { db } from "@budibase/backend-core"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
import generateApp from "../../../config/internal-api/fixtures/applications"
import generator from "../../../config/generator"
import generateScreen from "../../../config/internal-api/fixtures/screens"
import {
generateTable,
generateNewColumnForTable,
} from "../../../config/internal-api/fixtures/table"
import { generateNewRowForTable } from "../../../config/internal-api/fixtures/rows"
describe("Internal API - /applications endpoints", () => {
const api = new InternalAPIClient()
const config = new TestConfiguration<Application>(api)
beforeAll(async () => {
await config.beforeAll()
})
afterAll(async () => {
await config.afterAll()
})
async function createAppFromTemplate() {
return config.applications.create({
name: generator.word(),
url: `/${generator.word()}`,
useTemplate: "true",
templateName: "Near Miss Register",
templateKey: "app/near-miss-register",
templateFile: undefined,
})
}
it("GET - fetch applications", async () => {
await config.applications.create({
...generateApp(),
useTemplate: false,
})
const [response, apps] = await config.applications.fetch()
expect(response).toHaveStatusCode(200)
expect(apps.length).toBeGreaterThanOrEqual(1)
})
it("POST - Create an application", async () => {
config.applications.create(generateApp())
})
it("POST - Publish application", async () => {
// create app
const app = await config.applications.create(generateApp())
// publish app
config.applications.api.appId = app.appId
const [publishResponse, publish] = await config.applications.publish()
expect(publishResponse).toHaveStatusCode(200)
expect(publish).toEqual({
_id: expect.any(String),
appUrl: app.url,
status: "SUCCESS",
})
})
it("Publish app flow", async () => {
// create the app
const appName = generator.word()
const app = await createAppFromTemplate()
config.applications.api.appId = app.appId
// check preview renders
const [previewResponse, previewRenders] =
await config.applications.canRender()
expect(previewResponse).toHaveStatusCode(200)
expect(previewRenders).toBe(true)
// publish app
await config.applications.publish()
// check published app renders
config.applications.api.appId = db.getProdAppID(app.appId)
const [publishedAppResponse, publishedAppRenders] =
await config.applications.canRender()
expect(publishedAppRenders).toBe(true)
// unpublish app
await config.applications.unpublish(<string>app.appId)
})
it("POST - Sync application before deployment", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
const [syncResponse, sync] = await config.applications.sync(
<string>app.appId
)
expect(syncResponse).toHaveStatusCode(200)
expect(sync).toEqual({
message: "App sync not required, app not deployed.",
})
})
it("POST - Sync application after deployment", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
// publish app
await config.applications.publish()
const [syncResponse, sync] = await config.applications.sync(
<string>app.appId
)
expect(syncResponse).toHaveStatusCode(200)
expect(sync).toEqual({
message: "App sync completed successfully.",
})
})
it("PUT - Update an application", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
const [updateResponse, updatedApp] = await config.applications.update(
<string>app.appId,
{
name: generator.word(),
}
)
expect(updateResponse).toHaveStatusCode(200)
expect(updatedApp.name).not.toEqual(app.name)
})
it("POST - Revert Changes without changes", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
const [revertResponse, revert] = await config.applications.revert(
<string>app.appId
)
expect(revertResponse).toHaveStatusCode(400)
expect(revert).toEqual({
message: "App has not yet been deployed",
status: 400,
})
})
it("POST - Revert Changes", async () => {
const app = await config.applications.create(generateApp())
config.applications.api.appId = app.appId
// publish app
const [publishResponse, publish] = await config.applications.publish()
expect(publishResponse).toHaveStatusCode(200)
expect(publish.status).toEqual("SUCCESS")
// Change/add component to the app
const [screenResponse, screen] = await config.applications.addScreentoApp(
generateScreen("BASIC")
)
expect(screenResponse).toHaveStatusCode(200)
expect(screen._id).toBeDefined()
// // Revert the app to published state
const [revertResponse, revert] = await config.applications.revert(
<string>app.appId
)
expect(revertResponse).toHaveStatusCode(200)
expect(revert).toEqual({
message: "Reverted changes successfully.",
})
// Check screen is removed
const [routesResponse, routes] = await config.applications.getRoutes()
expect(routesResponse).toHaveStatusCode(200)
expect(routes.routes["/test"]).toBeUndefined()
})
it("DELETE - Delete an application", async () => {
const app = await config.applications.create(generateApp())
const [deleteResponse] = await config.applications.delete(<string>app.appId)
expect(deleteResponse).toHaveStatusCode(200)
})
it("Operations on Tables", async () => {
// create the app
const appName = generator.word()
const app = await createAppFromTemplate()
config.applications.api.appId = app.appId
// Get current tables: expect 2 in this template
await config.tables.getAll(2)
// Add new table
const [createdTableResponse, createdTableData] = await config.tables.save(
generateTable()
)
expect(createdTableResponse).toHaveStatusCode(200)
expect(createdTableData._id).toBeDefined()
expect(createdTableData._rev).toBeDefined()
//Table was added
await config.tables.getAll(3)
//Get information about the table
const [tableInfoResponse, tableInfo] = await config.tables.getTableById(
<string>createdTableData._id
)
expect(tableInfoResponse).toHaveStatusCode(200)
expect(tableInfo._id).toEqual(createdTableData._id)
//Add Column to table
const newColumn = generateNewColumnForTable(createdTableData)
const [addColumnResponse, addColumnData] = await config.tables.save(
newColumn
)
expect(addColumnResponse).toHaveStatusCode(200)
expect(addColumnData._id).toEqual(createdTableData._id)
expect(addColumnData.schema.TestColumn).toBeDefined()
//Add Row to table
const newRow = generateNewRowForTable(<string>addColumnData._id)
const [addRowResponse, addRowData] = await config.rows.add(
<string>addColumnData._id,
newRow
)
console.log(addRowData)
expect(addRowResponse).toHaveStatusCode(200)
expect(addRowData._id).toBeDefined()
expect(addRowData._rev).toBeDefined()
expect(addRowData.tableId).toEqual(addColumnData._id)
//Get Row from table
const [getRowResponse, getRowData] = await config.rows.getAll(
<string>addColumnData._id
)
expect(getRowResponse).toHaveStatusCode(200)
expect(getRowData.length).toEqual(1)
//Delete Row from table
const rowToDelete = {
rows: [getRowData[0]],
}
const [deleteRowResponse, deleteRowData] = await config.rows.delete(
<string>addColumnData._id,
rowToDelete
)
expect(deleteRowResponse).toHaveStatusCode(200)
expect(deleteRowData[0]._id).toEqual(getRowData[0]._id)
//Delete the table
const [deleteTableResponse, deleteTable] = await config.tables.delete(
<string>addColumnData._id,
<string>addColumnData._rev
)
expect(deleteTableResponse).toHaveStatusCode(200)
expect(deleteTable.message).toEqual(
`Table ${createdTableData._id} deleted.`
)
//Table was deleted
await config.tables.getAll(2)
})
})

View File

@ -29,8 +29,6 @@ describe("Internal API - /screens endpoints", () => {
const [response, screen] = await config.screen.create(
generateScreen(roleArray[role])
)
expect(response).toHaveStatusCode(200)
expect(screen.routing.roleId).toEqual(roleArray[role])
}
})
@ -40,14 +38,12 @@ describe("Internal API - /screens endpoints", () => {
// Create Screen
appConfig.applications.api.appId = app.appId
const [response, screen] = await config.screen.create(
await config.screen.create(
generateScreen("BASIC")
)
// Check screen exists
const [routesResponse, routes] = await appConfig.applications.getRoutes()
expect(routesResponse).toHaveStatusCode(200)
expect(routes.routes["/test"]).toBeTruthy()
await appConfig.applications.getRoutes(true)
})
it("DELETE - Delete a screen", async () => {
@ -61,7 +57,7 @@ describe("Internal API - /screens endpoints", () => {
)
// Delete Screen
const [response] = await config.screen.delete(screen._id!, screen._rev!)
expect(response).toHaveStatusCode(200)
await config.screen.delete(screen._id!, screen._rev!)
})
})

View File

@ -0,0 +1,95 @@
import TestConfiguration from "../../../config/internal-api/TestConfiguration"
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
import generator from "../../../config/generator"
import {
generateTable,
generateNewColumnForTable,
} from "../../../config/internal-api/fixtures/table"
import { generateNewRowForTable } from "../../../config/internal-api/fixtures/rows"
describe("Internal API - Application creation, update, publish and delete", () => {
const api = new InternalAPIClient()
const config = new TestConfiguration<Application>(api)
beforeAll(async () => {
await config.beforeAll()
})
afterAll(async () => {
await config.afterAll()
})
async function createAppFromTemplate() {
return config.applications.create({
name: generator.word(),
url: `/${generator.word()}`,
useTemplate: "true",
templateName: "Near Miss Register",
templateKey: "app/near-miss-register",
templateFile: undefined,
})
}
it("Operations on Tables", async () => {
// create the app
const appName = generator.word()
const app = await createAppFromTemplate()
config.applications.api.appId = app.appId
// Get current tables: expect 2 in this template
await config.tables.getAll(2)
// Add new table
const [createdTableResponse, createdTableData] = await config.tables.save(
generateTable()
)
//Table was added
await config.tables.getAll(3)
//Get information about the table
await config.tables.getTableById(
<string>createdTableData._id
)
//Add Column to table
const newColumn = generateNewColumnForTable(createdTableData)
const [addColumnResponse, addColumnData] = await config.tables.save(
newColumn,
true
)
//Add Row to table
const newRow = generateNewRowForTable(<string>addColumnData._id)
await config.rows.add(
<string>addColumnData._id,
newRow
)
//Get Row from table
const [getRowResponse, getRowData] = await config.rows.getAll(
<string>addColumnData._id
)
//Delete Row from table
const rowToDelete = {
rows: [getRowData[0]],
}
const [deleteRowResponse, deleteRowData] = await config.rows.delete(
<string>addColumnData._id,
rowToDelete
)
expect(deleteRowData[0]._id).toEqual(getRowData[0]._id)
//Delete the table
const [deleteTableResponse, deleteTable] = await config.tables.delete(
<string>addColumnData._id,
<string>addColumnData._rev
)
//Table was deleted
await config.tables.getAll(2)
})
})

View File

@ -1,7 +1,7 @@
#!/bin/bash
#!/bin/bash
if [[ -z "${CI}" ]]; then
echo 'Cannot run insall.sh unless in CI'
echo 'Cannot run install.sh unless in CI'
exit 0
fi

View File

@ -1,5 +1,8 @@
#!/bin/bash
# Fail when any command fails
set -e
if [[ -z "${CI}" ]]; then
echo 'Cannot run release.sh unless in CI'
exit 0
@ -59,22 +62,6 @@ git push
lerna publish $VERSION --yes --force-publish --dist-tag $TAG
#############################################
# POST-PUBLISH - PRO #
#############################################
# Revert build changes on packages/pro/package.json
cd packages/pro
jq '.main = "src/index.ts" | .types = "src/index.ts"' package.json > package.json.tmp && mv package.json.tmp package.json
# Go back to pro repo root
cd -
# Commit and push changes
git add packages/pro/package.json
git commit -m "Prep next development iteration"
git push
#############################################
# POST-PUBLISH - BUDIBASE #
#############################################

View File

@ -1,4 +1,8 @@
#!/bin/bash
# Fail when any command fails
set -e
cd ../
if [[ -d "budibase-pro" ]]; then
cd budibase-pro