Merge branch 'develop' of github.com:Budibase/budibase into side-panel
This commit is contained in:
commit
abedb5d9eb
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
"types": "dist/src/index.d.ts",
|
"types": "dist/src/index.d.ts",
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
"test:watch": "jest --watchAll"
|
"test:watch": "jest --watchAll"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/types": "2.1.32-alpha.3",
|
"@budibase/types": "2.1.32-alpha.9",
|
||||||
"@shopify/jest-koa-mocks": "5.0.1",
|
"@shopify/jest-koa-mocks": "5.0.1",
|
||||||
"@techpass/passport-openidconnect": "0.3.2",
|
"@techpass/passport-openidconnect": "0.3.2",
|
||||||
"aws-sdk": "2.1030.0",
|
"aws-sdk": "2.1030.0",
|
||||||
|
|
|
@ -171,7 +171,7 @@ export function getGlobalUserParams(globalId: any, otherProps: any = {}) {
|
||||||
/**
|
/**
|
||||||
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
||||||
*/
|
*/
|
||||||
export function getUserMetadataParams(userId?: string, otherProps = {}) {
|
export function getUserMetadataParams(userId?: string | null, otherProps = {}) {
|
||||||
return getRowParams(InternalTable.USER_METADATA, userId, otherProps)
|
return getRowParams(InternalTable.USER_METADATA, userId, otherProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ export function getTemplateParams(
|
||||||
* Generates a new role ID.
|
* Generates a new role ID.
|
||||||
* @returns {string} The new role ID which the role doc can be stored under.
|
* @returns {string} The new role ID which the role doc can be stored under.
|
||||||
*/
|
*/
|
||||||
export function generateRoleID(id: any) {
|
export function generateRoleID(id?: any) {
|
||||||
return `${DocumentType.ROLE}${SEPARATOR}${id || newid()}`
|
return `${DocumentType.ROLE}${SEPARATOR}${id || newid()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import { DEFAULT_TENANT_ID } from "../constants"
|
import { DEFAULT_TENANT_ID } from "../constants"
|
||||||
import { doWithDB } from "../db"
|
import {
|
||||||
import { DocumentType, StaticDatabases } from "../db/constants"
|
DocumentType,
|
||||||
import { getAllApps } from "../db/utils"
|
StaticDatabases,
|
||||||
|
getAllApps,
|
||||||
|
getGlobalDBName,
|
||||||
|
doWithDB,
|
||||||
|
} from "../db"
|
||||||
import environment from "../environment"
|
import environment from "../environment"
|
||||||
import { doInTenant, getTenantIds, getTenantId } from "../tenancy"
|
import { doInTenant, getTenantIds, getTenantId } from "../tenancy"
|
||||||
import { getGlobalDBName } from "../db/tenancy"
|
|
||||||
import * as context from "../context"
|
import * as context from "../context"
|
||||||
import { DEFINITIONS } from "."
|
import { DEFINITIONS } from "."
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -390,7 +390,7 @@ export const uploadDirectory = async (
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.downloadTarballDirect = async (
|
export const downloadTarballDirect = async (
|
||||||
url: string,
|
url: string,
|
||||||
path: string,
|
path: string,
|
||||||
headers = {}
|
headers = {}
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import { BuiltinPermissionID, PermissionLevel } from "./permissions"
|
import { BuiltinPermissionID, PermissionLevel } from "./permissions"
|
||||||
import {
|
import { generateRoleID, getRoleParams, DocumentType, SEPARATOR } from "../db"
|
||||||
generateRoleID,
|
|
||||||
getRoleParams,
|
|
||||||
DocumentType,
|
|
||||||
SEPARATOR,
|
|
||||||
} from "../db/utils"
|
|
||||||
import { getAppDB } from "../context"
|
import { getAppDB } from "../context"
|
||||||
import { doWithDB } from "../db"
|
import { doWithDB } from "../db"
|
||||||
import { Screen, Role as RoleDoc } from "@budibase/types"
|
import { Screen, Role as RoleDoc } from "@budibase/types"
|
||||||
|
@ -30,20 +25,18 @@ const EXTERNAL_BUILTIN_ROLE_IDS = [
|
||||||
BUILTIN_IDS.PUBLIC,
|
BUILTIN_IDS.PUBLIC,
|
||||||
]
|
]
|
||||||
|
|
||||||
export class Role {
|
export class Role implements RoleDoc {
|
||||||
_id: string
|
_id: string
|
||||||
|
_rev?: string
|
||||||
name: string
|
name: string
|
||||||
permissionId?: string
|
permissionId: string
|
||||||
inherits?: string
|
inherits?: string
|
||||||
|
permissions = {}
|
||||||
|
|
||||||
constructor(id: string, name: string) {
|
constructor(id: string, name: string, permissionId: string) {
|
||||||
this._id = id
|
this._id = id
|
||||||
this.name = name
|
this.name = name
|
||||||
}
|
|
||||||
|
|
||||||
addPermission(permissionId: string) {
|
|
||||||
this.permissionId = permissionId
|
this.permissionId = permissionId
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addInheritance(inherits: string) {
|
addInheritance(inherits: string) {
|
||||||
|
@ -53,24 +46,26 @@ export class Role {
|
||||||
}
|
}
|
||||||
|
|
||||||
const BUILTIN_ROLES = {
|
const BUILTIN_ROLES = {
|
||||||
ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin")
|
ADMIN: new Role(
|
||||||
.addPermission(BuiltinPermissionID.ADMIN)
|
BUILTIN_IDS.ADMIN,
|
||||||
.addInheritance(BUILTIN_IDS.POWER),
|
"Admin",
|
||||||
POWER: new Role(BUILTIN_IDS.POWER, "Power")
|
|
||||||
.addPermission(BuiltinPermissionID.POWER)
|
|
||||||
.addInheritance(BUILTIN_IDS.BASIC),
|
|
||||||
BASIC: new Role(BUILTIN_IDS.BASIC, "Basic")
|
|
||||||
.addPermission(BuiltinPermissionID.WRITE)
|
|
||||||
.addInheritance(BUILTIN_IDS.PUBLIC),
|
|
||||||
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission(
|
|
||||||
BuiltinPermissionID.PUBLIC
|
|
||||||
),
|
|
||||||
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission(
|
|
||||||
BuiltinPermissionID.ADMIN
|
BuiltinPermissionID.ADMIN
|
||||||
),
|
).addInheritance(BUILTIN_IDS.POWER),
|
||||||
|
POWER: new Role(
|
||||||
|
BUILTIN_IDS.POWER,
|
||||||
|
"Power",
|
||||||
|
BuiltinPermissionID.POWER
|
||||||
|
).addInheritance(BUILTIN_IDS.BASIC),
|
||||||
|
BASIC: new Role(
|
||||||
|
BUILTIN_IDS.BASIC,
|
||||||
|
"Basic",
|
||||||
|
BuiltinPermissionID.WRITE
|
||||||
|
).addInheritance(BUILTIN_IDS.PUBLIC),
|
||||||
|
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public", BuiltinPermissionID.PUBLIC),
|
||||||
|
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder", BuiltinPermissionID.ADMIN),
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBuiltinRoles() {
|
export function getBuiltinRoles(): { [key: string]: RoleDoc } {
|
||||||
return cloneDeep(BUILTIN_ROLES)
|
return cloneDeep(BUILTIN_ROLES)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +99,7 @@ export function builtinRoleToNumber(id?: string) {
|
||||||
if (!role) {
|
if (!role) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
role = builtins[role.inherits]
|
role = builtins[role.inherits!]
|
||||||
count++
|
count++
|
||||||
} while (role !== null)
|
} while (role !== null)
|
||||||
return count
|
return count
|
||||||
|
@ -129,12 +124,12 @@ export async function roleToNumber(id?: string) {
|
||||||
/**
|
/**
|
||||||
* Returns whichever builtin roleID is lower.
|
* Returns whichever builtin roleID is lower.
|
||||||
*/
|
*/
|
||||||
export function lowerBuiltinRoleID(roleId1?: string, roleId2?: string) {
|
export function lowerBuiltinRoleID(roleId1?: string, roleId2?: string): string {
|
||||||
if (!roleId1) {
|
if (!roleId1) {
|
||||||
return roleId2
|
return roleId2 as string
|
||||||
}
|
}
|
||||||
if (!roleId2) {
|
if (!roleId2) {
|
||||||
return roleId1
|
return roleId1 as string
|
||||||
}
|
}
|
||||||
return builtinRoleToNumber(roleId1) > builtinRoleToNumber(roleId2)
|
return builtinRoleToNumber(roleId1) > builtinRoleToNumber(roleId2)
|
||||||
? roleId2
|
? roleId2
|
||||||
|
|
|
@ -89,6 +89,7 @@ export async function createASession(
|
||||||
userId,
|
userId,
|
||||||
}
|
}
|
||||||
await client.store(key, session, EXPIRY_SECONDS)
|
await client.store(key, session, EXPIRY_SECONDS)
|
||||||
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateSessionTTL(session: Session) {
|
export async function updateSessionTTL(session: Session) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
||||||
"@budibase/string-templates": "2.1.32-alpha.3",
|
"@budibase/string-templates": "2.1.32-alpha.9",
|
||||||
"@spectrum-css/actionbutton": "^1.0.1",
|
"@spectrum-css/actionbutton": "^1.0.1",
|
||||||
"@spectrum-css/actiongroup": "^1.0.1",
|
"@spectrum-css/actiongroup": "^1.0.1",
|
||||||
"@spectrum-css/avatar": "^3.0.2",
|
"@spectrum-css/avatar": "^3.0.2",
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<div class="skeleton">
|
||||||
|
<div class="children">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.skeleton {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
background-color: var(--spectrum-global-color-gray-300) !important;
|
||||||
|
border-radius: 7px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
animation: fadeIn 130ms ease 0s 1 normal forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.children {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(255, 255, 255, 0) 0,
|
||||||
|
rgba(255, 255, 255, 0.2) 20%,
|
||||||
|
rgba(255, 255, 255, 0.5) 60%,
|
||||||
|
rgba(255, 255, 255, 0)
|
||||||
|
);
|
||||||
|
animation: shimmer 2s infinite;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
100% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -71,7 +71,8 @@
|
||||||
visibleRowCount,
|
visibleRowCount,
|
||||||
rowCount,
|
rowCount,
|
||||||
totalRowCount,
|
totalRowCount,
|
||||||
rowHeight
|
rowHeight,
|
||||||
|
loading
|
||||||
)
|
)
|
||||||
$: sortedRows = sortRows(rows, sortColumn, sortOrder)
|
$: sortedRows = sortRows(rows, sortColumn, sortOrder)
|
||||||
$: gridStyle = getGridStyle(fields, schema, showEditColumn)
|
$: gridStyle = getGridStyle(fields, schema, showEditColumn)
|
||||||
|
@ -120,8 +121,12 @@
|
||||||
visibleRowCount,
|
visibleRowCount,
|
||||||
rowCount,
|
rowCount,
|
||||||
totalRowCount,
|
totalRowCount,
|
||||||
rowHeight
|
rowHeight,
|
||||||
|
loading
|
||||||
) => {
|
) => {
|
||||||
|
if (loading) {
|
||||||
|
return `height: ${headerHeight + visibleRowCount * rowHeight}px;`
|
||||||
|
}
|
||||||
if (!rowCount || !visibleRowCount || totalRowCount <= rowCount) {
|
if (!rowCount || !visibleRowCount || totalRowCount <= rowCount) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -278,9 +283,11 @@
|
||||||
bind:offsetHeight={height}
|
bind:offsetHeight={height}
|
||||||
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
|
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
|
||||||
>
|
>
|
||||||
{#if !loaded}
|
{#if loading}
|
||||||
<div class="loading" style={heightStyle}>
|
<div class="loading" style={heightStyle}>
|
||||||
<ProgressCircle />
|
<slot name="loadingIndicator">
|
||||||
|
<ProgressCircle />
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="spectrum-Table" style={`${heightStyle}${gridStyle}`}>
|
<div class="spectrum-Table" style={`${heightStyle}${gridStyle}`}>
|
||||||
|
@ -440,9 +447,10 @@
|
||||||
|
|
||||||
/* Loading */
|
/* Loading */
|
||||||
.loading {
|
.loading {
|
||||||
display: grid;
|
display: flex;
|
||||||
place-items: center;
|
align-items: center;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Table */
|
/* Table */
|
||||||
|
|
|
@ -4,6 +4,7 @@ import "./bbui.css"
|
||||||
import "@spectrum-css/icon/dist/index-vars.css"
|
import "@spectrum-css/icon/dist/index-vars.css"
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
export { default as Skeleton } from "./Skeleton/Skeleton.svelte"
|
||||||
export { default as Input } from "./Form/Input.svelte"
|
export { default as Input } from "./Form/Input.svelte"
|
||||||
export { default as Stepper } from "./Form/Stepper.svelte"
|
export { default as Stepper } from "./Form/Stepper.svelte"
|
||||||
export { default as TextArea } from "./Form/TextArea.svelte"
|
export { default as TextArea } from "./Form/TextArea.svelte"
|
||||||
|
|
|
@ -53,10 +53,10 @@
|
||||||
to-gfm-code-block "^0.1.1"
|
to-gfm-code-block "^0.1.1"
|
||||||
year "^0.2.1"
|
year "^0.2.1"
|
||||||
|
|
||||||
"@budibase/string-templates@2.1.22-alpha.5":
|
"@budibase/string-templates@2.1.32-alpha.0":
|
||||||
version "2.1.22-alpha.5"
|
version "2.1.32-alpha.0"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.1.22-alpha.5.tgz#3493be2ec8a3799ad1f7e470c308d9cdcd36abf6"
|
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.1.32-alpha.0.tgz#97452a80b3c3238e0d11b48ec36d2d7b8cb8e1d5"
|
||||||
integrity sha512-iFN4nccB8eIjsaU0ki7DyC+zznJaGC+cUQiAiwgO+aDm3SD6vkF443IjwL/fcmpK81/4WpEWmJPDjVuQ+vjlKQ==
|
integrity sha512-F+AkIWb8CitLt+YCJV/GQB8ECfj7GfO4I/vptIyNay9EWo0MRIU+4Xn715nBfEW5e23BQPKF3QR8S6y0IgxFbg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/handlebars-helpers" "^0.11.8"
|
"@budibase/handlebars-helpers" "^0.11.8"
|
||||||
dayjs "^1.10.4"
|
dayjs "^1.10.4"
|
||||||
|
@ -824,7 +824,7 @@ component-emitter@^1.2.1:
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||||
|
|
||||||
concat-with-sourcemaps@*, concat-with-sourcemaps@^1.1.0:
|
concat-with-sourcemaps@*, concat-with-sourcemaps@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
|
@ -2080,10 +2080,10 @@ lines-and-columns@^1.1.6:
|
||||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||||
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
|
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
|
||||||
|
|
||||||
loader-utils@^1.1.0:
|
loader-utils@1.4.1, loader-utils@^1.1.0:
|
||||||
version "1.4.2"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.1.tgz#278ad7006660bccc4d2c0c1578e17c5c78d5c0e0"
|
||||||
integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
|
integrity sha512-1Qo97Y2oKaU+Ro2xnDMR26g1BwMT29jNbem1EvcujW2jqt+j5COXyscjM7bLQkM9HaxI7pkWeW7gnI072yMI9Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
big.js "^5.2.2"
|
big.js "^5.2.2"
|
||||||
emojis-list "^3.0.0"
|
emojis-list "^3.0.0"
|
||||||
|
@ -2220,9 +2220,9 @@ mime@1.6.0:
|
||||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||||
|
|
||||||
minimatch@^3.0.4:
|
minimatch@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ filterTests(['all'], () => {
|
||||||
|
|
||||||
it("Should publish an application and correctly reflect that", () => {
|
it("Should publish an application and correctly reflect that", () => {
|
||||||
//Assuming the previous test was run and the unpublished app is open in edit mode.
|
//Assuming the previous test was run and the unpublished app is open in edit mode.
|
||||||
cy.closeModal()
|
|
||||||
cy.get(interact.TOPRIGHTNAV_BUTTON_SPECTRUM).contains("Publish").click({ force : true })
|
cy.get(interact.TOPRIGHTNAV_BUTTON_SPECTRUM).contains("Publish").click({ force : true })
|
||||||
|
|
||||||
cy.get(interact.DEPLOY_APP_MODAL).should("be.visible")
|
cy.get(interact.DEPLOY_APP_MODAL).should("be.visible")
|
||||||
|
@ -86,8 +85,7 @@ filterTests(['all'], () => {
|
||||||
.within(() => {
|
.within(() => {
|
||||||
cy.get(interact.APP_TABLE_APP_NAME).click({ force: true })
|
cy.get(interact.APP_TABLE_APP_NAME).click({ force: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.closeModal()
|
|
||||||
cy.get(interact.DEPLOYMENT_TOP_GLOBE).should("exist").click({ force: true })
|
cy.get(interact.DEPLOYMENT_TOP_GLOBE).should("exist").click({ force: true })
|
||||||
|
|
||||||
cy.get("[data-cy='publish-popover-menu']")
|
cy.get("[data-cy='publish-popover-menu']")
|
||||||
|
|
|
@ -9,9 +9,10 @@ filterTests(['smoke', 'all'], () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should add a current user binding", () => {
|
it("should add a current user binding", () => {
|
||||||
cy.searchAndAddComponent("Paragraph").then(() => {
|
cy.searchAndAddComponent("Paragraph").then(componentId => {
|
||||||
addSettingBinding("text", ["Current User", "_id"], "Current User._id")
|
addSettingBinding("text", ["Current User", "_id"], "Current User._id")
|
||||||
})
|
})
|
||||||
|
cy.deleteComponentByName("New Paragraph")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle an invalid binding", () => {
|
it("should handle an invalid binding", () => {
|
||||||
|
@ -21,6 +22,7 @@ filterTests(['smoke', 'all'], () => {
|
||||||
.type("{{}{{}{{} Current User._id {}}{}}")
|
.type("{{}{{}{{} Current User._id {}}{}}")
|
||||||
.blur()
|
.blur()
|
||||||
cy.getComponent(componentId).should("have.text", "{{{ [user].[_id] }}")
|
cy.getComponent(componentId).should("have.text", "{{{ [user].[_id] }}")
|
||||||
|
cy.deleteComponentByName("New Paragraph")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import filterTests from "../../support/filterTests"
|
import filterTests from "../../support/filterTests"
|
||||||
|
|
||||||
filterTests(["smoke", "all"], () => {
|
filterTests(["smoke", "all"], () => {
|
||||||
context("REST Datasource Testing", () => {
|
xcontext("REST Datasource Testing", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.login()
|
cy.login()
|
||||||
cy.createTestApp()
|
cy.createTestApp()
|
||||||
|
|
|
@ -440,8 +440,8 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => {
|
||||||
// Creates an internal Budibase DB table
|
// Creates an internal Budibase DB table
|
||||||
if (!initialTable) {
|
if (!initialTable) {
|
||||||
cy.navigateToDataSection()
|
cy.navigateToDataSection()
|
||||||
cy.get(`[data-cy="new-datasource"]`, { timeout: 2000 }).click()
|
|
||||||
}
|
}
|
||||||
|
cy.get(`[data-cy="new-datasource"]`, { timeout: 2000 }).click()
|
||||||
cy.wait(2000)
|
cy.wait(2000)
|
||||||
cy.get(".item", { timeout: 2000 })
|
cy.get(".item", { timeout: 2000 })
|
||||||
.contains("Budibase DB")
|
.contains("Budibase DB")
|
||||||
|
@ -458,6 +458,9 @@ Cypress.Commands.add("createTable", (tableName, initialTable) => {
|
||||||
})
|
})
|
||||||
// Ensure modal has closed and table is created
|
// Ensure modal has closed and table is created
|
||||||
cy.get(".spectrum-Modal", { timeout: 2000 }).should("not.exist")
|
cy.get(".spectrum-Modal", { timeout: 2000 }).should("not.exist")
|
||||||
|
cy.get(".nav-item", { timeout: 2000 })
|
||||||
|
.contains("Budibase DB")
|
||||||
|
.click({ force: true })
|
||||||
cy.get(".spectrum-Tabs-content", { timeout: 2000 }).should(
|
cy.get(".spectrum-Tabs-content", { timeout: 2000 }).should(
|
||||||
"contain",
|
"contain",
|
||||||
tableName
|
tableName
|
||||||
|
@ -525,7 +528,7 @@ Cypress.Commands.add("addRowMultiValue", values => {
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("selectTable", tableName => {
|
Cypress.Commands.add("selectTable", tableName => {
|
||||||
cy.expandBudibaseConnection()
|
cy.get(".nav-item").contains("Budibase DB").click()
|
||||||
cy.contains(".nav-item", tableName).click()
|
cy.contains(".nav-item", tableName).click()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -576,6 +579,18 @@ Cypress.Commands.add("searchAndAddComponent", component => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add("deleteComponentByName", componentName => {
|
||||||
|
cy.get(".body")
|
||||||
|
.eq(0)
|
||||||
|
.contains(componentName)
|
||||||
|
.siblings(".actions")
|
||||||
|
.within(() => {
|
||||||
|
cy.get(".spectrum-Icon").click({ force: true })
|
||||||
|
})
|
||||||
|
cy.get(".spectrum-Menu").contains("Delete").click()
|
||||||
|
cy.get(".spectrum-Dialog").contains("Delete Component").click()
|
||||||
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("addComponent", (category, component) => {
|
Cypress.Commands.add("addComponent", (category, component) => {
|
||||||
if (category) {
|
if (category) {
|
||||||
cy.get(`[data-cy="category-${category}"]`, { timeout: 3000 }).click({
|
cy.get(`[data-cy="category-${category}"]`, { timeout: 3000 }).click({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -71,10 +71,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.1.32-alpha.3",
|
"@budibase/bbui": "2.1.32-alpha.9",
|
||||||
"@budibase/client": "2.1.32-alpha.3",
|
"@budibase/client": "2.1.32-alpha.9",
|
||||||
"@budibase/frontend-core": "2.1.32-alpha.3",
|
"@budibase/frontend-core": "2.1.32-alpha.9",
|
||||||
"@budibase/string-templates": "2.1.32-alpha.3",
|
"@budibase/string-templates": "2.1.32-alpha.9",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -26,9 +26,9 @@
|
||||||
"outputPath": "build"
|
"outputPath": "build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "2.1.32-alpha.3",
|
"@budibase/backend-core": "2.1.32-alpha.9",
|
||||||
"@budibase/string-templates": "2.1.32-alpha.3",
|
"@budibase/string-templates": "2.1.32-alpha.9",
|
||||||
"@budibase/types": "2.1.32-alpha.3",
|
"@budibase/types": "2.1.32-alpha.9",
|
||||||
"axios": "0.21.2",
|
"axios": "0.21.2",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"cli-progress": "3.11.2",
|
"cli-progress": "3.11.2",
|
||||||
|
|
|
@ -285,7 +285,7 @@
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"size": {
|
"size": {
|
||||||
"width": 105,
|
"width": 105,
|
||||||
"height": 35
|
"height": 32
|
||||||
},
|
},
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
|
@ -684,7 +684,7 @@
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"size": {
|
"size": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
"height": 30
|
"height": 24
|
||||||
},
|
},
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
|
@ -809,7 +809,7 @@
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"size": {
|
"size": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
"height": 40
|
"height": 32
|
||||||
},
|
},
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
|
@ -2448,6 +2448,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"stringfield": {
|
"stringfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Text Field",
|
"name": "Text Field",
|
||||||
"icon": "Text",
|
"icon": "Text",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -2456,7 +2457,7 @@
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"size": {
|
"size": {
|
||||||
"width": 400,
|
"width": 400,
|
||||||
"height": 50
|
"height": 32
|
||||||
},
|
},
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
|
@ -2539,6 +2540,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"numberfield": {
|
"numberfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Number Field",
|
"name": "Number Field",
|
||||||
"icon": "123",
|
"icon": "123",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -2653,6 +2655,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"optionsfield": {
|
"optionsfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Options Picker",
|
"name": "Options Picker",
|
||||||
"icon": "Menu",
|
"icon": "Menu",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -2821,6 +2824,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"multifieldselect": {
|
"multifieldselect": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Multi-select Picker",
|
"name": "Multi-select Picker",
|
||||||
"icon": "ViewList",
|
"icon": "ViewList",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -2983,12 +2987,13 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"booleanfield": {
|
"booleanfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Checkbox",
|
"name": "Checkbox",
|
||||||
"icon": "SelectBox",
|
"icon": "SelectBox",
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"size": {
|
"size": {
|
||||||
"width": 400,
|
"width": 20,
|
||||||
"height": 50
|
"height": 20
|
||||||
},
|
},
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
|
@ -3140,6 +3145,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"datetimefield": {
|
"datetimefield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Date Picker",
|
"name": "Date Picker",
|
||||||
"icon": "Date",
|
"icon": "Date",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -3221,6 +3227,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"codescanner": {
|
"codescanner": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Barcode/QR Scanner",
|
"name": "Barcode/QR Scanner",
|
||||||
"icon": "Camera",
|
"icon": "Camera",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -3386,6 +3393,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"attachmentfield": {
|
"attachmentfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Attachment",
|
"name": "Attachment",
|
||||||
"icon": "Attach",
|
"icon": "Attach",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -3444,6 +3452,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"relationshipfield": {
|
"relationshipfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Relationship Picker",
|
"name": "Relationship Picker",
|
||||||
"icon": "TaskList",
|
"icon": "TaskList",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -3507,6 +3516,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"jsonfield": {
|
"jsonfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "JSON Field",
|
"name": "JSON Field",
|
||||||
"icon": "Brackets",
|
"icon": "Brackets",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -3708,6 +3718,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Table",
|
"name": "Table",
|
||||||
"icon": "Table",
|
"icon": "Table",
|
||||||
"illegalChildren": [
|
"illegalChildren": [
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -19,9 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.1.32-alpha.3",
|
"@budibase/bbui": "2.1.32-alpha.9",
|
||||||
"@budibase/frontend-core": "2.1.32-alpha.3",
|
"@budibase/frontend-core": "2.1.32-alpha.9",
|
||||||
"@budibase/string-templates": "2.1.32-alpha.3",
|
"@budibase/string-templates": "2.1.32-alpha.9",
|
||||||
"@spectrum-css/button": "^3.0.3",
|
"@spectrum-css/button": "^3.0.3",
|
||||||
"@spectrum-css/card": "^3.0.3",
|
"@spectrum-css/card": "^3.0.3",
|
||||||
"@spectrum-css/divider": "^1.0.3",
|
"@spectrum-css/divider": "^1.0.3",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
// to render this part of the block, taking advantage of binding enrichment
|
// to render this part of the block, taking advantage of binding enrichment
|
||||||
$: id = `${block.id}-${context ?? rand}`
|
$: id = `${block.id}-${context ?? rand}`
|
||||||
$: instance = {
|
$: instance = {
|
||||||
|
_blockElementHasChildren: $$slots?.default ?? false,
|
||||||
_component: `@budibase/standard-components/${type}`,
|
_component: `@budibase/standard-components/${type}`,
|
||||||
_id: id,
|
_id: id,
|
||||||
_instanceName: name || type[0].toUpperCase() + type.slice(1),
|
_instanceName: name || type[0].toUpperCase() + type.slice(1),
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
import Placeholder from "components/app/Placeholder.svelte"
|
import Placeholder from "components/app/Placeholder.svelte"
|
||||||
import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte"
|
import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte"
|
||||||
import ComponentPlaceholder from "components/app/ComponentPlaceholder.svelte"
|
import ComponentPlaceholder from "components/app/ComponentPlaceholder.svelte"
|
||||||
|
import Skeleton from "components/app/Skeleton.svelte"
|
||||||
|
|
||||||
export let instance = {}
|
export let instance = {}
|
||||||
export let isLayout = false
|
export let isLayout = false
|
||||||
|
@ -38,6 +39,7 @@
|
||||||
|
|
||||||
// Get parent contexts
|
// Get parent contexts
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
|
const loading = getContext("loading")
|
||||||
const insideScreenslot = !!getContext("screenslot")
|
const insideScreenslot = !!getContext("screenslot")
|
||||||
|
|
||||||
// Create component context
|
// Create component context
|
||||||
|
@ -471,9 +473,21 @@
|
||||||
componentStore.actions.unregisterInstance(id)
|
componentStore.actions.unregisterInstance(id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$: showSkeleton =
|
||||||
|
$loading &&
|
||||||
|
definition.name !== "Screenslot" &&
|
||||||
|
children.length === 0 &&
|
||||||
|
!instance._blockElementHasChildren &&
|
||||||
|
definition.skeleton !== false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if constructor && initialSettings && (visible || inSelectedPath) && !builderHidden}
|
{#if showSkeleton}
|
||||||
|
<Skeleton
|
||||||
|
height={initialSettings?.height || definition?.size?.height || 0}
|
||||||
|
width={initialSettings?.width || definition?.size?.width || 0}
|
||||||
|
/>
|
||||||
|
{:else if constructor && initialSettings && (visible || inSelectedPath) && !builderHidden}
|
||||||
<!-- The ID is used as a class because getElementsByClassName is O(1) -->
|
<!-- The ID is used as a class because getElementsByClassName is O(1) -->
|
||||||
<!-- and the performance matters for the selection indicators -->
|
<!-- and the performance matters for the selection indicators -->
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { writable } from "svelte/store"
|
||||||
import { setContext, getContext, onMount } from "svelte"
|
import { setContext, getContext, onMount } from "svelte"
|
||||||
import Router, { querystring } from "svelte-spa-router"
|
import Router, { querystring } from "svelte-spa-router"
|
||||||
import { routeStore, stateStore } from "stores"
|
import { routeStore, stateStore } from "stores"
|
||||||
|
@ -9,6 +10,9 @@
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
setContext("screenslot", true)
|
setContext("screenslot", true)
|
||||||
|
|
||||||
|
const loading = writable(false)
|
||||||
|
setContext("loading", loading)
|
||||||
|
|
||||||
// Only wrap this as an array to take advantage of svelte keying,
|
// Only wrap this as an array to take advantage of svelte keying,
|
||||||
// to ensure the svelte-spa-router is fully remounted when route config
|
// to ensure the svelte-spa-router is fully remounted when route config
|
||||||
// changes
|
// changes
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { writable } from "svelte/store"
|
||||||
import { ProgressCircle, Pagination } from "@budibase/bbui"
|
import { setContext, getContext } from "svelte"
|
||||||
|
import { Pagination } from "@budibase/bbui"
|
||||||
import { fetchData, LuceneUtils } from "@budibase/frontend-core"
|
import { fetchData, LuceneUtils } from "@budibase/frontend-core"
|
||||||
|
|
||||||
export let dataSource
|
export let dataSource
|
||||||
|
@ -10,6 +11,8 @@
|
||||||
export let limit
|
export let limit
|
||||||
export let paginate
|
export let paginate
|
||||||
|
|
||||||
|
const loading = writable(false)
|
||||||
|
|
||||||
const { styleable, Provider, ActionTypes, API } = getContext("sdk")
|
const { styleable, Provider, ActionTypes, API } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
|
@ -77,9 +80,13 @@
|
||||||
sortColumn: $fetch.sortColumn,
|
sortColumn: $fetch.sortColumn,
|
||||||
sortOrder: $fetch.sortOrder,
|
sortOrder: $fetch.sortOrder,
|
||||||
},
|
},
|
||||||
loaded: $fetch.loaded,
|
limit: limit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parentLoading = getContext("loading")
|
||||||
|
setContext("loading", loading)
|
||||||
|
$: loading.set($parentLoading || !$fetch.loaded)
|
||||||
|
|
||||||
const createFetch = datasource => {
|
const createFetch = datasource => {
|
||||||
return fetchData({
|
return fetchData({
|
||||||
API,
|
API,
|
||||||
|
@ -127,23 +134,17 @@
|
||||||
|
|
||||||
<div use:styleable={$component.styles} class="container">
|
<div use:styleable={$component.styles} class="container">
|
||||||
<Provider {actions} data={dataContext}>
|
<Provider {actions} data={dataContext}>
|
||||||
{#if !$fetch.loaded}
|
<slot />
|
||||||
<div class="loading">
|
{#if paginate && $fetch.supportsPagination}
|
||||||
<ProgressCircle />
|
<div class="pagination">
|
||||||
|
<Pagination
|
||||||
|
page={$fetch.pageNumber + 1}
|
||||||
|
hasPrevPage={$fetch.hasPrevPage}
|
||||||
|
hasNextPage={$fetch.hasNextPage}
|
||||||
|
goToPrevPage={fetch.prevPage}
|
||||||
|
goToNextPage={fetch.nextPage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
|
||||||
<slot />
|
|
||||||
{#if paginate && $fetch.supportsPagination}
|
|
||||||
<div class="pagination">
|
|
||||||
<Pagination
|
|
||||||
page={$fetch.pageNumber + 1}
|
|
||||||
hasPrevPage={$fetch.hasPrevPage}
|
|
||||||
hasNextPage={$fetch.hasNextPage}
|
|
||||||
goToPrevPage={fetch.prevPage}
|
|
||||||
goToNextPage={fetch.nextPage}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
</Provider>
|
</Provider>
|
||||||
</div>
|
</div>
|
||||||
|
@ -155,13 +156,6 @@
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.loading {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100px;
|
|
||||||
}
|
|
||||||
.pagination {
|
.pagination {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -12,22 +12,25 @@
|
||||||
|
|
||||||
const { Provider } = getContext("sdk")
|
const { Provider } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
const loading = getContext("loading")
|
||||||
|
|
||||||
$: rows = dataProvider?.rows ?? []
|
// If the parent DataProvider is loading, fill the rows array with a number of empty objects corresponding to the DataProvider's page size; this allows skeleton loader components to be rendered further down the tree.
|
||||||
$: loaded = dataProvider?.loaded ?? true
|
$: rows = $loading
|
||||||
|
? new Array(dataProvider.limit > 20 ? 20 : dataProvider.limit).fill({})
|
||||||
|
: dataProvider?.rows
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Container {direction} {hAlign} {vAlign} {gap} wrap>
|
<Container {direction} {hAlign} {vAlign} {gap} wrap>
|
||||||
{#if $component.empty}
|
{#if $component.empty}
|
||||||
<Placeholder />
|
<Placeholder />
|
||||||
{:else if rows.length > 0}
|
{:else if !$loading && rows.length === 0}
|
||||||
|
<div class="noRows"><i class="ri-list-check-2" />{noRowsMessage}</div>
|
||||||
|
{:else}
|
||||||
{#each rows as row, index}
|
{#each rows as row, index}
|
||||||
<Provider data={{ ...row, index }}>
|
<Provider data={{ ...row, index }}>
|
||||||
<slot />
|
<slot />
|
||||||
</Provider>
|
</Provider>
|
||||||
{/each}
|
{/each}
|
||||||
{:else if loaded && noRowsMessage}
|
|
||||||
<div class="noRows"><i class="ri-list-check-2" />{noRowsMessage}</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import { Skeleton } from "@budibase/bbui"
|
||||||
|
|
||||||
|
const { styleable } = getContext("sdk")
|
||||||
|
const component = getContext("component")
|
||||||
|
|
||||||
|
export let height
|
||||||
|
export let width
|
||||||
|
|
||||||
|
let styles
|
||||||
|
|
||||||
|
$: {
|
||||||
|
styles = JSON.parse(JSON.stringify($component.styles))
|
||||||
|
|
||||||
|
if (!styles.normal.height && height) {
|
||||||
|
// The height and width props provided to this component can either be numbers or strings set by users (ex. '100%', '100px', '100'). A string of '100' wouldn't be a valid CSS property, but some of our components respect that input, so we need to handle it here also, hence the `!isNaN` check.
|
||||||
|
styles.normal.height = !isNaN(height) ? `${height}px` : height
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!styles.normal.width && width) {
|
||||||
|
styles.normal.width = !isNaN(width) ? `${width}px` : width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div use:styleable={styles}>
|
||||||
|
<Skeleton>
|
||||||
|
<slot />
|
||||||
|
</Skeleton>
|
||||||
|
</div>
|
|
@ -76,18 +76,26 @@
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
defaultValue={[]}
|
defaultValue={[]}
|
||||||
>
|
>
|
||||||
{#if fieldState}
|
<div class="minHeightWrapper">
|
||||||
<CoreDropzone
|
{#if fieldState}
|
||||||
value={fieldState.value}
|
<CoreDropzone
|
||||||
disabled={fieldState.disabled}
|
value={fieldState.value}
|
||||||
error={fieldState.error}
|
disabled={fieldState.disabled}
|
||||||
on:change={handleChange}
|
error={fieldState.error}
|
||||||
{processFiles}
|
on:change={handleChange}
|
||||||
{deleteAttachments}
|
{processFiles}
|
||||||
{handleFileTooLarge}
|
{deleteAttachments}
|
||||||
{handleTooManyFiles}
|
{handleFileTooLarge}
|
||||||
{maximum}
|
{handleTooManyFiles}
|
||||||
{extensions}
|
{maximum}
|
||||||
/>
|
{extensions}
|
||||||
{/if}
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.minHeightWrapper {
|
||||||
|
min-height: 220px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Placeholder from "../Placeholder.svelte"
|
import Placeholder from "../Placeholder.svelte"
|
||||||
import FieldGroupFallback from "./FieldGroupFallback.svelte"
|
import FieldGroupFallback from "./FieldGroupFallback.svelte"
|
||||||
|
import Skeleton from "../Skeleton.svelte"
|
||||||
import { getContext, onDestroy } from "svelte"
|
import { getContext, onDestroy } from "svelte"
|
||||||
|
|
||||||
export let label
|
export let label
|
||||||
|
@ -53,6 +54,8 @@
|
||||||
builderStore.actions.updateProp("label", e.target.textContent)
|
builderStore.actions.updateProp("label", e.target.textContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loading = getContext("loading")
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
fieldApi?.deregister()
|
fieldApi?.deregister()
|
||||||
unsubscribe?.()
|
unsubscribe?.()
|
||||||
|
@ -76,6 +79,10 @@
|
||||||
<div class="spectrum-Form-itemField">
|
<div class="spectrum-Form-itemField">
|
||||||
{#if !formContext}
|
{#if !formContext}
|
||||||
<Placeholder text="Form components need to be wrapped in a form" />
|
<Placeholder text="Form components need to be wrapped in a form" />
|
||||||
|
{:else if $loading}
|
||||||
|
<Skeleton>
|
||||||
|
<slot />
|
||||||
|
</Skeleton>
|
||||||
{:else if !fieldState}
|
{:else if !fieldState}
|
||||||
<Placeholder />
|
<Placeholder />
|
||||||
{:else if schemaType && schemaType !== type && type !== "options"}
|
{:else if schemaType && schemaType !== type && type !== "options"}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { Table } from "@budibase/bbui"
|
import { Table, Skeleton } from "@budibase/bbui"
|
||||||
import SlotRenderer from "./SlotRenderer.svelte"
|
import SlotRenderer from "./SlotRenderer.svelte"
|
||||||
import { UnsortableTypes } from "../../../constants"
|
import { UnsortableTypes } from "../../../constants"
|
||||||
import { onDestroy } from "svelte"
|
import { onDestroy } from "svelte"
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
export let compact
|
export let compact
|
||||||
export let onClick
|
export let onClick
|
||||||
|
|
||||||
|
const loading = getContext("loading")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const { styleable, getAction, ActionTypes, rowSelectionStore } =
|
const { styleable, getAction, ActionTypes, rowSelectionStore } =
|
||||||
getContext("sdk")
|
getContext("sdk")
|
||||||
|
@ -28,7 +29,6 @@
|
||||||
let selectedRows = []
|
let selectedRows = []
|
||||||
|
|
||||||
$: hasChildren = $component.children
|
$: hasChildren = $component.children
|
||||||
$: loading = dataProvider?.loading ?? false
|
|
||||||
$: data = dataProvider?.rows || []
|
$: data = dataProvider?.rows || []
|
||||||
$: fullSchema = dataProvider?.schema ?? {}
|
$: fullSchema = dataProvider?.schema ?? {}
|
||||||
$: fields = getFields(fullSchema, columns, false)
|
$: fields = getFields(fullSchema, columns, false)
|
||||||
|
@ -130,7 +130,7 @@
|
||||||
<Table
|
<Table
|
||||||
{data}
|
{data}
|
||||||
{schema}
|
{schema}
|
||||||
{loading}
|
loading={$loading}
|
||||||
{rowCount}
|
{rowCount}
|
||||||
{quiet}
|
{quiet}
|
||||||
{compact}
|
{compact}
|
||||||
|
@ -145,6 +145,9 @@
|
||||||
on:sort={onSort}
|
on:sort={onSort}
|
||||||
on:click={handleClick}
|
on:click={handleClick}
|
||||||
>
|
>
|
||||||
|
<div class="skeleton" slot="loadingIndicator">
|
||||||
|
<Skeleton />
|
||||||
|
</div>
|
||||||
<slot />
|
<slot />
|
||||||
</Table>
|
</Table>
|
||||||
{#if allowSelectRows && selectedRows.length}
|
{#if allowSelectRows && selectedRows.length}
|
||||||
|
@ -159,6 +162,11 @@
|
||||||
background-color: var(--spectrum-alias-background-color-secondary);
|
background-color: var(--spectrum-alias-background-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skeleton {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.row-count {
|
.row-count {
|
||||||
margin-top: var(--spacing-l);
|
margin-top: var(--spacing-l);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/frontend-core",
|
"name": "@budibase/frontend-core",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"description": "Budibase frontend core libraries used in builder and client",
|
"description": "Budibase frontend core libraries used in builder and client",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.1.32-alpha.3",
|
"@budibase/bbui": "2.1.32-alpha.9",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/sdk",
|
"name": "@budibase/sdk",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"description": "Budibase Public API SDK",
|
"description": "Budibase Public API SDK",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "2.1.32-alpha.3",
|
"version": "2.1.32-alpha.9",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -43,11 +43,11 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "10.0.3",
|
"@apidevtools/swagger-parser": "10.0.3",
|
||||||
"@budibase/backend-core": "2.1.32-alpha.3",
|
"@budibase/backend-core": "2.1.32-alpha.9",
|
||||||
"@budibase/client": "2.1.32-alpha.3",
|
"@budibase/client": "2.1.32-alpha.9",
|
||||||
"@budibase/pro": "2.1.32-alpha.3",
|
"@budibase/pro": "2.1.32-alpha.9",
|
||||||
"@budibase/string-templates": "2.1.32-alpha.3",
|
"@budibase/string-templates": "2.1.32-alpha.9",
|
||||||
"@budibase/types": "2.1.32-alpha.3",
|
"@budibase/types": "2.1.32-alpha.9",
|
||||||
"@bull-board/api": "3.7.0",
|
"@bull-board/api": "3.7.0",
|
||||||
"@bull-board/koa": "3.9.4",
|
"@bull-board/koa": "3.9.4",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
|
|
|
@ -3,7 +3,7 @@ const yargs = require("yargs")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const { join } = require("path")
|
const { join } = require("path")
|
||||||
require("../src/db").init()
|
require("../src/db").init()
|
||||||
const { doWithDB } = require("@budibase/backend-core/db")
|
const { db: dbCore } = require("@budibase/backend-core")
|
||||||
// load environment
|
// load environment
|
||||||
const env = require("../src/environment")
|
const env = require("../src/environment")
|
||||||
const {
|
const {
|
||||||
|
@ -48,7 +48,7 @@ yargs
|
||||||
const writeStream = fs.createWriteStream(join(exportPath, "dump.text"))
|
const writeStream = fs.createWriteStream(join(exportPath, "dump.text"))
|
||||||
// perform couch dump
|
// perform couch dump
|
||||||
|
|
||||||
await doWithDB(appId, async db => {
|
await dbCore.doWithDB(appId, async db => {
|
||||||
return db.dump(writeStream, {
|
return db.dump(writeStream, {
|
||||||
filter: doc =>
|
filter: doc =>
|
||||||
!(
|
!(
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const { StaticDatabases } = require("@budibase/backend-core/db")
|
import { db as dbCore, tenancy } from "@budibase/backend-core"
|
||||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
import { BBContext, Document } from "@budibase/types"
|
||||||
|
|
||||||
const KEYS_DOC = StaticDatabases.GLOBAL.docs.apiKeys
|
const KEYS_DOC = dbCore.StaticDatabases.GLOBAL.docs.apiKeys
|
||||||
|
|
||||||
async function getBuilderMainDoc() {
|
async function getBuilderMainDoc() {
|
||||||
const db = getGlobalDB()
|
const db = tenancy.getGlobalDB()
|
||||||
try {
|
try {
|
||||||
return await db.get(KEYS_DOC)
|
return await db.get(KEYS_DOC)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -15,24 +15,24 @@ async function getBuilderMainDoc() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setBuilderMainDoc(doc) {
|
async function setBuilderMainDoc(doc: Document) {
|
||||||
// make sure to override the ID
|
// make sure to override the ID
|
||||||
doc._id = KEYS_DOC
|
doc._id = KEYS_DOC
|
||||||
const db = getGlobalDB()
|
const db = tenancy.getGlobalDB()
|
||||||
return db.put(doc)
|
return db.put(doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
export async function fetch(ctx: BBContext) {
|
||||||
try {
|
try {
|
||||||
const mainDoc = await getBuilderMainDoc()
|
const mainDoc = await getBuilderMainDoc()
|
||||||
ctx.body = mainDoc.apiKeys ? mainDoc.apiKeys : {}
|
ctx.body = mainDoc.apiKeys ? mainDoc.apiKeys : {}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
ctx.throw(400, err)
|
ctx.throw(400, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.update = async function (ctx) {
|
export async function update(ctx: BBContext) {
|
||||||
const key = ctx.params.key
|
const key = ctx.params.key
|
||||||
const value = ctx.request.body.value
|
const value = ctx.request.body.value
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ exports.update = async function (ctx) {
|
||||||
_id: resp.id,
|
_id: resp.id,
|
||||||
_rev: resp.rev,
|
_rev: resp.rev,
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
ctx.throw(400, err)
|
ctx.throw(400, err)
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ import { InternalTables } from "../../db/utils"
|
||||||
import { getFullUser } from "../../utilities/users"
|
import { getFullUser } from "../../utilities/users"
|
||||||
import { roles, context } from "@budibase/backend-core"
|
import { roles, context } from "@budibase/backend-core"
|
||||||
import { groups } from "@budibase/pro"
|
import { groups } from "@budibase/pro"
|
||||||
import { ContextUser, User } from "@budibase/types"
|
import { ContextUser, User, Row } from "@budibase/types"
|
||||||
|
|
||||||
const PUBLIC_ROLE = roles.BUILTIN_ROLE_IDS.PUBLIC
|
const PUBLIC_ROLE = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ export async function fetchSelf(ctx: any) {
|
||||||
try {
|
try {
|
||||||
const userTable = await db.get(InternalTables.USER_METADATA)
|
const userTable = await db.get(InternalTables.USER_METADATA)
|
||||||
// specifically needs to make sure is enriched
|
// specifically needs to make sure is enriched
|
||||||
ctx.body = await outputProcessing(userTable, user)
|
ctx.body = await outputProcessing(userTable, user as Row)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
let response
|
let response
|
||||||
// user didn't exist in app, don't pretend they do
|
// user didn't exist in app, don't pretend they do
|
||||||
|
|
|
@ -1,26 +1,21 @@
|
||||||
const actions = require("../../automations/actions")
|
import actions from "../../automations/actions"
|
||||||
const triggers = require("../../automations/triggers")
|
import triggers from "../../automations/triggers"
|
||||||
const {
|
import {
|
||||||
getAutomationParams,
|
getAutomationParams,
|
||||||
generateAutomationID,
|
generateAutomationID,
|
||||||
DocumentType,
|
DocumentType,
|
||||||
} = require("../../db/utils")
|
} from "../../db/utils"
|
||||||
const {
|
import {
|
||||||
checkForWebhooks,
|
checkForWebhooks,
|
||||||
updateTestHistory,
|
updateTestHistory,
|
||||||
removeDeprecated,
|
removeDeprecated,
|
||||||
} = require("../../automations/utils")
|
} from "../../automations/utils"
|
||||||
const { deleteEntityMetadata } = require("../../utilities")
|
import { deleteEntityMetadata } from "../../utilities"
|
||||||
const { MetadataTypes } = require("../../constants")
|
import { MetadataTypes } from "../../constants"
|
||||||
const { setTestFlag, clearTestFlag } = require("../../utilities/redis")
|
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
||||||
const {
|
import { context, cache, events } from "@budibase/backend-core"
|
||||||
getAppDB,
|
import { automations } from "@budibase/pro"
|
||||||
getProdAppDB,
|
import { Automation, BBContext } from "@budibase/types"
|
||||||
doInAppContext,
|
|
||||||
} = require("@budibase/backend-core/context")
|
|
||||||
const { events } = require("@budibase/backend-core")
|
|
||||||
const { app } = require("@budibase/backend-core/cache")
|
|
||||||
const { automations } = require("@budibase/pro")
|
|
||||||
|
|
||||||
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS)
|
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS)
|
||||||
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
|
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
|
||||||
|
@ -31,7 +26,7 @@ const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS)
|
||||||
* *
|
* *
|
||||||
*************************/
|
*************************/
|
||||||
|
|
||||||
async function cleanupAutomationMetadata(automationId) {
|
async function cleanupAutomationMetadata(automationId: string) {
|
||||||
await deleteEntityMetadata(MetadataTypes.AUTOMATION_TEST_INPUT, automationId)
|
await deleteEntityMetadata(MetadataTypes.AUTOMATION_TEST_INPUT, automationId)
|
||||||
await deleteEntityMetadata(
|
await deleteEntityMetadata(
|
||||||
MetadataTypes.AUTOMATION_TEST_HISTORY,
|
MetadataTypes.AUTOMATION_TEST_HISTORY,
|
||||||
|
@ -39,7 +34,7 @@ async function cleanupAutomationMetadata(automationId) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanAutomationInputs(automation) {
|
function cleanAutomationInputs(automation: Automation) {
|
||||||
if (automation == null) {
|
if (automation == null) {
|
||||||
return automation
|
return automation
|
||||||
}
|
}
|
||||||
|
@ -63,14 +58,14 @@ function cleanAutomationInputs(automation) {
|
||||||
return automation
|
return automation
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.create = async function (ctx) {
|
export async function create(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = ctx.request.body
|
let automation = ctx.request.body
|
||||||
automation.appId = ctx.appId
|
automation.appId = ctx.appId
|
||||||
|
|
||||||
// call through to update if already exists
|
// call through to update if already exists
|
||||||
if (automation._id && automation._rev) {
|
if (automation._id && automation._rev) {
|
||||||
return exports.update(ctx)
|
return update(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
automation._id = generateAutomationID()
|
automation._id = generateAutomationID()
|
||||||
|
@ -97,17 +92,23 @@ exports.create = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNewSteps = (oldAutomation, automation) => {
|
export function getNewSteps(oldAutomation: Automation, automation: Automation) {
|
||||||
const oldStepIds = oldAutomation.definition.steps.map(s => s.id)
|
const oldStepIds = oldAutomation.definition.steps.map(s => s.id)
|
||||||
return automation.definition.steps.filter(s => !oldStepIds.includes(s.id))
|
return automation.definition.steps.filter(s => !oldStepIds.includes(s.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDeletedSteps = (oldAutomation, automation) => {
|
export function getDeletedSteps(
|
||||||
|
oldAutomation: Automation,
|
||||||
|
automation: Automation
|
||||||
|
) {
|
||||||
const stepIds = automation.definition.steps.map(s => s.id)
|
const stepIds = automation.definition.steps.map(s => s.id)
|
||||||
return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id))
|
return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleStepEvents = async (oldAutomation, automation) => {
|
export async function handleStepEvents(
|
||||||
|
oldAutomation: Automation,
|
||||||
|
automation: Automation
|
||||||
|
) {
|
||||||
// new steps
|
// new steps
|
||||||
const newSteps = getNewSteps(oldAutomation, automation)
|
const newSteps = getNewSteps(oldAutomation, automation)
|
||||||
for (let step of newSteps) {
|
for (let step of newSteps) {
|
||||||
|
@ -121,8 +122,8 @@ const handleStepEvents = async (oldAutomation, automation) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.update = async function (ctx) {
|
export async function update(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = ctx.request.body
|
let automation = ctx.request.body
|
||||||
automation.appId = ctx.appId
|
automation.appId = ctx.appId
|
||||||
const oldAutomation = await db.get(automation._id)
|
const oldAutomation = await db.get(automation._id)
|
||||||
|
@ -146,9 +147,8 @@ exports.update = async function (ctx) {
|
||||||
if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) {
|
if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) {
|
||||||
await events.automation.triggerUpdated(automation)
|
await events.automation.triggerUpdated(automation)
|
||||||
await deleteEntityMetadata(
|
await deleteEntityMetadata(
|
||||||
ctx.appId,
|
|
||||||
MetadataTypes.AUTOMATION_TEST_INPUT,
|
MetadataTypes.AUTOMATION_TEST_INPUT,
|
||||||
automation._id
|
automation._id!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,8 +165,8 @@ exports.update = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
export async function fetch(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const response = await db.allDocs(
|
const response = await db.allDocs(
|
||||||
getAutomationParams(null, {
|
getAutomationParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -175,13 +175,13 @@ exports.fetch = async function (ctx) {
|
||||||
ctx.body = response.rows.map(row => row.doc)
|
ctx.body = response.rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
export async function find(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
ctx.body = await db.get(ctx.params.id)
|
ctx.body = await db.get(ctx.params.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
export async function destroy(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const automationId = ctx.params.id
|
const automationId = ctx.params.id
|
||||||
const oldAutomation = await db.get(automationId)
|
const oldAutomation = await db.get(automationId)
|
||||||
await checkForWebhooks({
|
await checkForWebhooks({
|
||||||
|
@ -193,14 +193,14 @@ exports.destroy = async function (ctx) {
|
||||||
await events.automation.deleted(oldAutomation)
|
await events.automation.deleted(oldAutomation)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.logSearch = async function (ctx) {
|
export async function logSearch(ctx: BBContext) {
|
||||||
ctx.body = await automations.logs.logSearch(ctx.request.body)
|
ctx.body = await automations.logs.logSearch(ctx.request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.clearLogError = async function (ctx) {
|
export async function clearLogError(ctx: BBContext) {
|
||||||
const { automationId, appId } = ctx.request.body
|
const { automationId, appId } = ctx.request.body
|
||||||
await doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const db = getProdAppDB()
|
const db = context.getProdAppDB()
|
||||||
const metadata = await db.get(DocumentType.APP_METADATA)
|
const metadata = await db.get(DocumentType.APP_METADATA)
|
||||||
if (!automationId) {
|
if (!automationId) {
|
||||||
delete metadata.automationErrors
|
delete metadata.automationErrors
|
||||||
|
@ -211,20 +211,20 @@ exports.clearLogError = async function (ctx) {
|
||||||
delete metadata.automationErrors[automationId]
|
delete metadata.automationErrors[automationId]
|
||||||
}
|
}
|
||||||
await db.put(metadata)
|
await db.put(metadata)
|
||||||
await app.invalidateAppMetadata(metadata.appId, metadata)
|
await cache.app.invalidateAppMetadata(metadata.appId, metadata)
|
||||||
ctx.body = { message: `Error logs cleared.` }
|
ctx.body = { message: `Error logs cleared.` }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getActionList = async function (ctx) {
|
export async function getActionList(ctx: BBContext) {
|
||||||
ctx.body = ACTION_DEFS
|
ctx.body = ACTION_DEFS
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getTriggerList = async function (ctx) {
|
export async function getTriggerList(ctx: BBContext) {
|
||||||
ctx.body = TRIGGER_DEFS
|
ctx.body = TRIGGER_DEFS
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getDefinitionList = async function (ctx) {
|
export async function getDefinitionList(ctx: BBContext) {
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
trigger: TRIGGER_DEFS,
|
trigger: TRIGGER_DEFS,
|
||||||
action: ACTION_DEFS,
|
action: ACTION_DEFS,
|
||||||
|
@ -237,8 +237,8 @@ module.exports.getDefinitionList = async function (ctx) {
|
||||||
* *
|
* *
|
||||||
*********************/
|
*********************/
|
||||||
|
|
||||||
exports.trigger = async function (ctx) {
|
export async function trigger(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = await db.get(ctx.params.id)
|
let automation = await db.get(ctx.params.id)
|
||||||
await triggers.externalTrigger(automation, {
|
await triggers.externalTrigger(automation, {
|
||||||
...ctx.request.body,
|
...ctx.request.body,
|
||||||
|
@ -250,7 +250,7 @@ exports.trigger = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareTestInput(input) {
|
function prepareTestInput(input: any) {
|
||||||
// prepare the test parameters
|
// prepare the test parameters
|
||||||
if (input.id && input.row) {
|
if (input.id && input.row) {
|
||||||
input.row._id = input.id
|
input.row._id = input.id
|
||||||
|
@ -261,8 +261,8 @@ function prepareTestInput(input) {
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.test = async function (ctx) {
|
export async function test(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let automation = await db.get(ctx.params.id)
|
let automation = await db.get(ctx.params.id)
|
||||||
await setTestFlag(automation._id)
|
await setTestFlag(automation._id)
|
||||||
const testInput = prepareTestInput(ctx.request.body)
|
const testInput = prepareTestInput(ctx.request.body)
|
|
@ -1,14 +1,14 @@
|
||||||
const env = require("../../environment")
|
import env from "../../environment"
|
||||||
const { getAllApps, getGlobalDBName } = require("@budibase/backend-core/db")
|
import { db as dbCore, tenancy } from "@budibase/backend-core"
|
||||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
import { streamFile } from "../../utilities/fileSystem"
|
||||||
const { streamFile } = require("../../utilities/fileSystem")
|
import { stringToReadStream } from "../../utilities"
|
||||||
const { stringToReadStream } = require("../../utilities")
|
import { getDocParams, DocumentType, isDevAppID } from "../../db/utils"
|
||||||
const { getDocParams, DocumentType, isDevAppID } = require("../../db/utils")
|
import { create } from "./application"
|
||||||
const { create } = require("./application")
|
import { join } from "path"
|
||||||
const { join } = require("path")
|
import { App, BBContext, Database } from "@budibase/types"
|
||||||
const sdk = require("../../sdk")
|
import sdk from "../../sdk"
|
||||||
|
|
||||||
async function createApp(appName, appDirectory) {
|
async function createApp(appName: string, appDirectory: string) {
|
||||||
const ctx = {
|
const ctx = {
|
||||||
request: {
|
request: {
|
||||||
body: {
|
body: {
|
||||||
|
@ -25,7 +25,7 @@ async function createApp(appName, appDirectory) {
|
||||||
return create(ctx)
|
return create(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAllDocType(db, docType) {
|
async function getAllDocType(db: Database, docType: string) {
|
||||||
const response = await db.allDocs(
|
const response = await db.allDocs(
|
||||||
getDocParams(docType, null, {
|
getDocParams(docType, null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -34,19 +34,19 @@ async function getAllDocType(db, docType) {
|
||||||
return response.rows.map(row => row.doc)
|
return response.rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.exportApps = async ctx => {
|
export async function exportApps(ctx: BBContext) {
|
||||||
if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
|
if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
|
||||||
ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.")
|
ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.")
|
||||||
}
|
}
|
||||||
const apps = await getAllApps({ all: true })
|
const apps = (await dbCore.getAllApps({ all: true })) as App[]
|
||||||
const globalDBString = await sdk.backups.exportDB(getGlobalDBName(), {
|
const globalDBString = await sdk.backups.exportDB(dbCore.getGlobalDBName(), {
|
||||||
filter: doc => !doc._id.startsWith(DocumentType.USER),
|
filter: (doc: any) => !doc._id.startsWith(DocumentType.USER),
|
||||||
})
|
})
|
||||||
// only export the dev apps as they will be the latest, the user can republish the apps
|
// only export the dev apps as they will be the latest, the user can republish the apps
|
||||||
// in their self-hosted environment
|
// in their self-hosted environment
|
||||||
let appMetadata = apps
|
let appMetadata = apps
|
||||||
.filter(app => isDevAppID(app.appId || app._id))
|
.filter((app: App) => isDevAppID(app.appId || app._id))
|
||||||
.map(app => ({ appId: app.appId || app._id, name: app.name }))
|
.map((app: App) => ({ appId: (app.appId || app._id)!, name: app.name }))
|
||||||
const tmpPath = await sdk.backups.exportMultipleApps(
|
const tmpPath = await sdk.backups.exportMultipleApps(
|
||||||
appMetadata,
|
appMetadata,
|
||||||
globalDBString
|
globalDBString
|
||||||
|
@ -56,25 +56,25 @@ exports.exportApps = async ctx => {
|
||||||
ctx.body = streamFile(tmpPath)
|
ctx.body = streamFile(tmpPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hasBeenImported() {
|
async function checkHasBeenImported() {
|
||||||
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const apps = await getAllApps({ all: true })
|
const apps = await dbCore.getAllApps({ all: true })
|
||||||
return apps.length !== 0
|
return apps.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.hasBeenImported = async ctx => {
|
export async function hasBeenImported(ctx: BBContext) {
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
imported: await hasBeenImported(),
|
imported: await checkHasBeenImported(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.importApps = async ctx => {
|
export async function importApps(ctx: BBContext) {
|
||||||
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
||||||
ctx.throw(400, "Importing only allowed in self hosted environments.")
|
ctx.throw(400, "Importing only allowed in self hosted environments.")
|
||||||
}
|
}
|
||||||
const beenImported = await hasBeenImported()
|
const beenImported = await checkHasBeenImported()
|
||||||
if (beenImported || !ctx.request.files || !ctx.request.files.importFile) {
|
if (beenImported || !ctx.request.files || !ctx.request.files.importFile) {
|
||||||
ctx.throw(
|
ctx.throw(
|
||||||
400,
|
400,
|
||||||
|
@ -90,7 +90,7 @@ exports.importApps = async ctx => {
|
||||||
const globalDbImport = sdk.backups.getGlobalDBFile(tmpPath)
|
const globalDbImport = sdk.backups.getGlobalDBFile(tmpPath)
|
||||||
const appNames = sdk.backups.getListOfAppsInMulti(tmpPath)
|
const appNames = sdk.backups.getListOfAppsInMulti(tmpPath)
|
||||||
|
|
||||||
const globalDb = getGlobalDB()
|
const globalDb = tenancy.getGlobalDB()
|
||||||
// load the global db first
|
// load the global db first
|
||||||
await globalDb.load(stringToReadStream(globalDbImport))
|
await globalDb.load(stringToReadStream(globalDbImport))
|
||||||
for (let appName of appNames) {
|
for (let appName of appNames) {
|
|
@ -2,8 +2,9 @@ import { DocumentType } from "../../db/utils"
|
||||||
import { Plugin } from "@budibase/types"
|
import { Plugin } from "@budibase/types"
|
||||||
import { db as dbCore, context, tenancy } from "@budibase/backend-core"
|
import { db as dbCore, context, tenancy } from "@budibase/backend-core"
|
||||||
import { getComponentLibraryManifest } from "../../utilities/fileSystem"
|
import { getComponentLibraryManifest } from "../../utilities/fileSystem"
|
||||||
|
import { BBContext } from "@budibase/types"
|
||||||
|
|
||||||
exports.fetchAppComponentDefinitions = async function (ctx: any) {
|
export async function fetchAppComponentDefinitions(ctx: BBContext) {
|
||||||
try {
|
try {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const app = await db.get(DocumentType.APP_METADATA)
|
const app = await db.get(DocumentType.APP_METADATA)
|
||||||
|
|
|
@ -1,30 +1,29 @@
|
||||||
const {
|
import {
|
||||||
generateDatasourceID,
|
generateDatasourceID,
|
||||||
getDatasourceParams,
|
getDatasourceParams,
|
||||||
getQueryParams,
|
getQueryParams,
|
||||||
DocumentType,
|
DocumentType,
|
||||||
BudibaseInternalDB,
|
BudibaseInternalDB,
|
||||||
getTableParams,
|
getTableParams,
|
||||||
} = require("../../db/utils")
|
} from "../../db/utils"
|
||||||
const { destroy: tableDestroy } = require("./table/internal")
|
import { destroy as tableDestroy } from "./table/internal"
|
||||||
const { BuildSchemaErrors, InvalidColumns } = require("../../constants")
|
import { BuildSchemaErrors, InvalidColumns } from "../../constants"
|
||||||
const { getIntegration } = require("../../integrations")
|
import { getIntegration } from "../../integrations"
|
||||||
const { getDatasourceAndQuery } = require("./row/utils")
|
import { getDatasourceAndQuery } from "./row/utils"
|
||||||
const { invalidateDynamicVariables } = require("../../threads/utils")
|
import { invalidateDynamicVariables } from "../../threads/utils"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { db as dbCore, context, events } from "@budibase/backend-core"
|
||||||
const { events } = require("@budibase/backend-core")
|
import { BBContext, Datasource, Row } from "@budibase/types"
|
||||||
const { db: dbCore } = require("@budibase/backend-core")
|
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
export async function fetch(ctx: BBContext) {
|
||||||
// Get internal tables
|
// Get internal tables
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const internalTables = await db.allDocs(
|
const internalTables = await db.allDocs(
|
||||||
getTableParams(null, {
|
getTableParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const internal = internalTables.rows.reduce((acc, row) => {
|
const internal = internalTables.rows.reduce((acc: any, row: Row) => {
|
||||||
const sourceId = row.doc.sourceId || "bb_internal"
|
const sourceId = row.doc.sourceId || "bb_internal"
|
||||||
acc[sourceId] = acc[sourceId] || []
|
acc[sourceId] = acc[sourceId] || []
|
||||||
acc[sourceId].push(row.doc)
|
acc[sourceId].push(row.doc)
|
||||||
|
@ -60,8 +59,8 @@ exports.fetch = async function (ctx) {
|
||||||
ctx.body = [bbInternalDb, ...datasources]
|
ctx.body = [bbInternalDb, ...datasources]
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.buildSchemaFromDb = async function (ctx) {
|
export async function buildSchemaFromDb(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasource = await db.get(ctx.params.datasourceId)
|
const datasource = await db.get(ctx.params.datasourceId)
|
||||||
const tablesFilter = ctx.request.body.tablesFilter
|
const tablesFilter = ctx.request.body.tablesFilter
|
||||||
|
|
||||||
|
@ -72,7 +71,9 @@ exports.buildSchemaFromDb = async function (ctx) {
|
||||||
}
|
}
|
||||||
for (let key in tables) {
|
for (let key in tables) {
|
||||||
if (
|
if (
|
||||||
tablesFilter.some(filter => filter.toLowerCase() === key.toLowerCase())
|
tablesFilter.some(
|
||||||
|
(filter: any) => filter.toLowerCase() === key.toLowerCase()
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
datasource.entities[key] = tables[key]
|
datasource.entities[key] = tables[key]
|
||||||
}
|
}
|
||||||
|
@ -85,7 +86,7 @@ exports.buildSchemaFromDb = async function (ctx) {
|
||||||
const dbResp = await db.put(datasource)
|
const dbResp = await db.put(datasource)
|
||||||
datasource._rev = dbResp.rev
|
datasource._rev = dbResp.rev
|
||||||
|
|
||||||
const response = { datasource }
|
const response: any = { datasource }
|
||||||
if (error) {
|
if (error) {
|
||||||
response.error = error
|
response.error = error
|
||||||
}
|
}
|
||||||
|
@ -95,9 +96,9 @@ exports.buildSchemaFromDb = async function (ctx) {
|
||||||
/**
|
/**
|
||||||
* Make sure all datasource entities have a display name selected
|
* Make sure all datasource entities have a display name selected
|
||||||
*/
|
*/
|
||||||
const setDefaultDisplayColumns = datasource => {
|
function setDefaultDisplayColumns(datasource: Datasource) {
|
||||||
//
|
//
|
||||||
for (let entity of Object.values(datasource.entities)) {
|
for (let entity of Object.values(datasource.entities || {})) {
|
||||||
if (entity.primaryDisplay) {
|
if (entity.primaryDisplay) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -113,9 +114,12 @@ const setDefaultDisplayColumns = datasource => {
|
||||||
/**
|
/**
|
||||||
* Check for variables that have been updated or removed and invalidate them.
|
* Check for variables that have been updated or removed and invalidate them.
|
||||||
*/
|
*/
|
||||||
const invalidateVariables = async (existingDatasource, updatedDatasource) => {
|
async function invalidateVariables(
|
||||||
const existingVariables = existingDatasource.config.dynamicVariables
|
existingDatasource: Datasource,
|
||||||
const updatedVariables = updatedDatasource.config.dynamicVariables
|
updatedDatasource: Datasource
|
||||||
|
) {
|
||||||
|
const existingVariables: any = existingDatasource.config?.dynamicVariables
|
||||||
|
const updatedVariables: any = updatedDatasource.config?.dynamicVariables
|
||||||
const toInvalidate = []
|
const toInvalidate = []
|
||||||
|
|
||||||
if (!existingVariables) {
|
if (!existingVariables) {
|
||||||
|
@ -127,9 +131,9 @@ const invalidateVariables = async (existingDatasource, updatedDatasource) => {
|
||||||
toInvalidate.push(...existingVariables)
|
toInvalidate.push(...existingVariables)
|
||||||
} else {
|
} else {
|
||||||
// invaldate changed / removed
|
// invaldate changed / removed
|
||||||
existingVariables.forEach(existing => {
|
existingVariables.forEach((existing: any) => {
|
||||||
const unchanged = updatedVariables.find(
|
const unchanged = updatedVariables.find(
|
||||||
updated =>
|
(updated: any) =>
|
||||||
existing.name === updated.name &&
|
existing.name === updated.name &&
|
||||||
existing.queryId === updated.queryId &&
|
existing.queryId === updated.queryId &&
|
||||||
existing.value === updated.value
|
existing.value === updated.value
|
||||||
|
@ -142,8 +146,8 @@ const invalidateVariables = async (existingDatasource, updatedDatasource) => {
|
||||||
await invalidateDynamicVariables(toInvalidate)
|
await invalidateDynamicVariables(toInvalidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.update = async function (ctx) {
|
export async function update(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasourceId = ctx.params.datasourceId
|
const datasourceId = ctx.params.datasourceId
|
||||||
let datasource = await db.get(datasourceId)
|
let datasource = await db.get(datasourceId)
|
||||||
const auth = datasource.config.auth
|
const auth = datasource.config.auth
|
||||||
|
@ -171,8 +175,8 @@ exports.update = async function (ctx) {
|
||||||
ctx.body = { datasource }
|
ctx.body = { datasource }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
export async function save(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const plus = ctx.request.body.datasource.plus
|
const plus = ctx.request.body.datasource.plus
|
||||||
const fetchSchema = ctx.request.body.fetchSchema
|
const fetchSchema = ctx.request.body.fetchSchema
|
||||||
|
|
||||||
|
@ -202,15 +206,15 @@ exports.save = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = { datasource }
|
const response: any = { datasource }
|
||||||
if (schemaError) {
|
if (schemaError) {
|
||||||
response.error = schemaError
|
response.error = schemaError
|
||||||
}
|
}
|
||||||
ctx.body = response
|
ctx.body = response
|
||||||
}
|
}
|
||||||
|
|
||||||
const destroyInternalTablesBySourceId = async datasourceId => {
|
async function destroyInternalTablesBySourceId(datasourceId: string) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
|
|
||||||
// Get all internal tables
|
// Get all internal tables
|
||||||
const internalTables = await db.allDocs(
|
const internalTables = await db.allDocs(
|
||||||
|
@ -220,12 +224,15 @@ const destroyInternalTablesBySourceId = async datasourceId => {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Filter by datasource and return the docs.
|
// Filter by datasource and return the docs.
|
||||||
const datasourceTableDocs = internalTables.rows.reduce((acc, table) => {
|
const datasourceTableDocs = internalTables.rows.reduce(
|
||||||
if (table.doc.sourceId == datasourceId) {
|
(acc: any, table: any) => {
|
||||||
acc.push(table.doc)
|
if (table.doc.sourceId == datasourceId) {
|
||||||
}
|
acc.push(table.doc)
|
||||||
return acc
|
}
|
||||||
}, [])
|
return acc
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
// Destroy the tables.
|
// Destroy the tables.
|
||||||
for (const table of datasourceTableDocs) {
|
for (const table of datasourceTableDocs) {
|
||||||
|
@ -237,8 +244,8 @@ const destroyInternalTablesBySourceId = async datasourceId => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
export async function destroy(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasourceId = ctx.params.datasourceId
|
const datasourceId = ctx.params.datasourceId
|
||||||
|
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
|
@ -249,7 +256,7 @@ exports.destroy = async function (ctx) {
|
||||||
} else {
|
} else {
|
||||||
const queries = await db.allDocs(getQueryParams(datasourceId, null))
|
const queries = await db.allDocs(getQueryParams(datasourceId, null))
|
||||||
await db.bulkDocs(
|
await db.bulkDocs(
|
||||||
queries.rows.map(row => ({
|
queries.rows.map((row: any) => ({
|
||||||
_id: row.id,
|
_id: row.id,
|
||||||
_rev: row.value.rev,
|
_rev: row.value.rev,
|
||||||
_deleted: true,
|
_deleted: true,
|
||||||
|
@ -265,28 +272,28 @@ exports.destroy = async function (ctx) {
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
export async function find(ctx: BBContext) {
|
||||||
const database = getAppDB()
|
const database = context.getAppDB()
|
||||||
ctx.body = await database.get(ctx.params.datasourceId)
|
ctx.body = await database.get(ctx.params.datasourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dynamic query functionality
|
// dynamic query functionality
|
||||||
exports.query = async function (ctx) {
|
export async function query(ctx: BBContext) {
|
||||||
const queryJson = ctx.request.body
|
const queryJson = ctx.request.body
|
||||||
try {
|
try {
|
||||||
ctx.body = await getDatasourceAndQuery(queryJson)
|
ctx.body = await getDatasourceAndQuery(queryJson)
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
ctx.throw(400, err)
|
ctx.throw(400, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getErrorTables(errors, errorType) {
|
function getErrorTables(errors: any, errorType: string) {
|
||||||
return Object.entries(errors)
|
return Object.entries(errors)
|
||||||
.filter(entry => entry[1] === errorType)
|
.filter(entry => entry[1] === errorType)
|
||||||
.map(([name]) => name)
|
.map(([name]) => name)
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateError(error, newError, tables) {
|
function updateError(error: any, newError: any, tables: string[]) {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = ""
|
error = ""
|
||||||
}
|
}
|
||||||
|
@ -297,7 +304,7 @@ function updateError(error, newError, tables) {
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildSchemaHelper = async datasource => {
|
async function buildSchemaHelper(datasource: Datasource) {
|
||||||
const Connector = await getIntegration(datasource.source)
|
const Connector = await getIntegration(datasource.source)
|
||||||
|
|
||||||
// Connect to the DB and build the schema
|
// Connect to the DB and build the schema
|
|
@ -1,15 +1,20 @@
|
||||||
const newid = require("../../../db/newid")
|
import newid from "../../../db/newid"
|
||||||
const { getAppId } = require("@budibase/backend-core/context")
|
import { context } from "@budibase/backend-core"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to pass around information about the deployment that is occurring
|
* This is used to pass around information about the deployment that is occurring
|
||||||
*/
|
*/
|
||||||
class Deployment {
|
export default class Deployment {
|
||||||
|
_id: string
|
||||||
|
verification: any
|
||||||
|
status?: string
|
||||||
|
err?: any
|
||||||
|
|
||||||
constructor(id = null) {
|
constructor(id = null) {
|
||||||
this._id = id || newid()
|
this._id = id || newid()
|
||||||
}
|
}
|
||||||
|
|
||||||
setVerification(verification) {
|
setVerification(verification: any) {
|
||||||
if (!verification) {
|
if (!verification) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -20,14 +25,14 @@ class Deployment {
|
||||||
return this.verification
|
return this.verification
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(status, err = null) {
|
setStatus(status: string, err?: any) {
|
||||||
this.status = status
|
this.status = status
|
||||||
if (err) {
|
if (err) {
|
||||||
this.err = err
|
this.err = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fromJSON(json) {
|
fromJSON(json: any) {
|
||||||
if (json.verification) {
|
if (json.verification) {
|
||||||
this.setVerification(json.verification)
|
this.setVerification(json.verification)
|
||||||
}
|
}
|
||||||
|
@ -37,9 +42,9 @@ class Deployment {
|
||||||
}
|
}
|
||||||
|
|
||||||
getJSON() {
|
getJSON() {
|
||||||
const obj = {
|
const obj: any = {
|
||||||
_id: this._id,
|
_id: this._id,
|
||||||
appId: getAppId(),
|
appId: context.getAppId(),
|
||||||
status: this.status,
|
status: this.status,
|
||||||
}
|
}
|
||||||
if (this.err) {
|
if (this.err) {
|
||||||
|
@ -51,5 +56,3 @@ class Deployment {
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Deployment
|
|
|
@ -1,10 +1,10 @@
|
||||||
const { EMPTY_LAYOUT } = require("../../constants/layouts")
|
import { EMPTY_LAYOUT } from "../../constants/layouts"
|
||||||
const { generateLayoutID, getScreenParams } = require("../../db/utils")
|
import { generateLayoutID, getScreenParams } from "../../db/utils"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { events, context } from "@budibase/backend-core"
|
||||||
const { events } = require("@budibase/backend-core")
|
import { BBContext } from "@budibase/types"
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
export async function save(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let layout = ctx.request.body
|
let layout = ctx.request.body
|
||||||
|
|
||||||
if (!layout.props) {
|
if (!layout.props) {
|
||||||
|
@ -24,8 +24,8 @@ exports.save = async function (ctx) {
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
export async function destroy(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const layoutId = ctx.params.layoutId,
|
const layoutId = ctx.params.layoutId,
|
||||||
layoutRev = ctx.params.layoutRev
|
layoutRev = ctx.params.layoutRev
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
const { MetadataTypes } = require("../../constants")
|
import { MetadataTypes } from "../../constants"
|
||||||
const { generateMetadataID } = require("../../db/utils")
|
import { generateMetadataID } from "../../db/utils"
|
||||||
const { saveEntityMetadata, deleteEntityMetadata } = require("../../utilities")
|
import { saveEntityMetadata, deleteEntityMetadata } from "../../utilities"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { context } from "@budibase/backend-core"
|
||||||
|
import { BBContext } from "@budibase/types"
|
||||||
|
|
||||||
exports.getTypes = async ctx => {
|
export async function getTypes(ctx: BBContext) {
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
types: MetadataTypes,
|
types: MetadataTypes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.saveMetadata = async ctx => {
|
export async function saveMetadata(ctx: BBContext) {
|
||||||
const { type, entityId } = ctx.params
|
const { type, entityId } = ctx.params
|
||||||
if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) {
|
if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) {
|
||||||
ctx.throw(400, "Cannot save automation history type")
|
ctx.throw(400, "Cannot save automation history type")
|
||||||
|
@ -17,7 +18,7 @@ exports.saveMetadata = async ctx => {
|
||||||
ctx.body = await saveEntityMetadata(type, entityId, ctx.request.body)
|
ctx.body = await saveEntityMetadata(type, entityId, ctx.request.body)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.deleteMetadata = async ctx => {
|
export async function deleteMetadata(ctx: BBContext) {
|
||||||
const { type, entityId } = ctx.params
|
const { type, entityId } = ctx.params
|
||||||
await deleteEntityMetadata(type, entityId)
|
await deleteEntityMetadata(type, entityId)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
|
@ -25,13 +26,13 @@ exports.deleteMetadata = async ctx => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getMetadata = async ctx => {
|
export async function getMetadata(ctx: BBContext) {
|
||||||
const { type, entityId } = ctx.params
|
const { type, entityId } = ctx.params
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const id = generateMetadataID(type, entityId)
|
const id = generateMetadataID(type, entityId)
|
||||||
try {
|
try {
|
||||||
ctx.body = await db.get(id)
|
ctx.body = await db.get(id)
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
if (err.status === 404) {
|
if (err.status === 404) {
|
||||||
ctx.body = {}
|
ctx.body = {}
|
||||||
} else {
|
} else {
|
|
@ -1,18 +1,11 @@
|
||||||
const { getBuiltinPermissions } = require("@budibase/backend-core/permissions")
|
import { permissions, roles, context } from "@budibase/backend-core"
|
||||||
const {
|
import { getRoleParams } from "../../db/utils"
|
||||||
isBuiltin,
|
import {
|
||||||
getDBRoleID,
|
|
||||||
getExternalRoleID,
|
|
||||||
getBuiltinRoles,
|
|
||||||
checkForRoleResourceArray,
|
|
||||||
} = require("@budibase/backend-core/roles")
|
|
||||||
const { getRoleParams } = require("../../db/utils")
|
|
||||||
const {
|
|
||||||
CURRENTLY_SUPPORTED_LEVELS,
|
CURRENTLY_SUPPORTED_LEVELS,
|
||||||
getBasePermissions,
|
getBasePermissions,
|
||||||
} = require("../../utilities/security")
|
} from "../../utilities/security"
|
||||||
const { removeFromArray } = require("../../utilities")
|
import { removeFromArray } from "../../utilities"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { BBContext, Database, Role } from "@budibase/types"
|
||||||
|
|
||||||
const PermissionUpdateType = {
|
const PermissionUpdateType = {
|
||||||
REMOVE: "remove",
|
REMOVE: "remove",
|
||||||
|
@ -22,7 +15,7 @@ const PermissionUpdateType = {
|
||||||
const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS
|
const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS
|
||||||
|
|
||||||
// utility function to stop this repetition - permissions always stored under roles
|
// utility function to stop this repetition - permissions always stored under roles
|
||||||
async function getAllDBRoles(db) {
|
async function getAllDBRoles(db: Database) {
|
||||||
const body = await db.allDocs(
|
const body = await db.allDocs(
|
||||||
getRoleParams(null, {
|
getRoleParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -32,21 +25,25 @@ async function getAllDBRoles(db) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updatePermissionOnRole(
|
async function updatePermissionOnRole(
|
||||||
appId,
|
appId: string,
|
||||||
{ roleId, resourceId, level },
|
{
|
||||||
updateType
|
roleId,
|
||||||
|
resourceId,
|
||||||
|
level,
|
||||||
|
}: { roleId: string; resourceId: string; level: string },
|
||||||
|
updateType: string
|
||||||
) {
|
) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const remove = updateType === PermissionUpdateType.REMOVE
|
const remove = updateType === PermissionUpdateType.REMOVE
|
||||||
const isABuiltin = isBuiltin(roleId)
|
const isABuiltin = roles.isBuiltin(roleId)
|
||||||
const dbRoleId = getDBRoleID(roleId)
|
const dbRoleId = roles.getDBRoleID(roleId)
|
||||||
const dbRoles = await getAllDBRoles(db)
|
const dbRoles = await getAllDBRoles(db)
|
||||||
const docUpdates = []
|
const docUpdates = []
|
||||||
|
|
||||||
// the permission is for a built in, make sure it exists
|
// the permission is for a built in, make sure it exists
|
||||||
if (isABuiltin && !dbRoles.some(role => role._id === dbRoleId)) {
|
if (isABuiltin && !dbRoles.some(role => role._id === dbRoleId)) {
|
||||||
const builtin = getBuiltinRoles()[roleId]
|
const builtin = roles.getBuiltinRoles()[roleId]
|
||||||
builtin._id = getDBRoleID(builtin._id)
|
builtin._id = roles.getDBRoleID(builtin._id)
|
||||||
dbRoles.push(builtin)
|
dbRoles.push(builtin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,41 +87,44 @@ async function updatePermissionOnRole(
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await db.bulkDocs(docUpdates)
|
const response = await db.bulkDocs(docUpdates)
|
||||||
return response.map(resp => {
|
return response.map((resp: any) => {
|
||||||
resp._id = getExternalRoleID(resp.id)
|
resp._id = roles.getExternalRoleID(resp.id)
|
||||||
delete resp.id
|
delete resp.id
|
||||||
return resp
|
return resp
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchBuiltin = function (ctx) {
|
export function fetchBuiltin(ctx: BBContext) {
|
||||||
ctx.body = Object.values(getBuiltinPermissions())
|
ctx.body = Object.values(permissions.getBuiltinPermissions())
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchLevels = function (ctx) {
|
export function fetchLevels(ctx: BBContext) {
|
||||||
// for now only provide the read/write perms externally
|
// for now only provide the read/write perms externally
|
||||||
ctx.body = SUPPORTED_LEVELS
|
ctx.body = SUPPORTED_LEVELS
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
export async function fetch(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const roles = await getAllDBRoles(db)
|
const dbRoles: Role[] = await getAllDBRoles(db)
|
||||||
let permissions = {}
|
let permissions: any = {}
|
||||||
// create an object with structure role ID -> resource ID -> level
|
// create an object with structure role ID -> resource ID -> level
|
||||||
for (let role of roles) {
|
for (let role of dbRoles) {
|
||||||
if (!role.permissions) {
|
if (!role.permissions) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const roleId = getExternalRoleID(role._id)
|
const roleId = roles.getExternalRoleID(role._id)
|
||||||
|
if (!roleId) {
|
||||||
|
ctx.throw(400, "Unable to retrieve role")
|
||||||
|
}
|
||||||
for (let [resource, levelArr] of Object.entries(role.permissions)) {
|
for (let [resource, levelArr] of Object.entries(role.permissions)) {
|
||||||
const levels = Array.isArray(levelArr) ? [levelArr] : levelArr
|
const levels: string[] = Array.isArray(levelArr) ? levelArr : [levelArr]
|
||||||
const perms = {}
|
const perms: Record<string, string> = {}
|
||||||
levels.forEach(level => (perms[level] = roleId))
|
levels.forEach(level => (perms[level] = roleId!))
|
||||||
permissions[resource] = perms
|
permissions[resource] = perms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// apply the base permissions
|
// apply the base permissions
|
||||||
const finalPermissions = {}
|
const finalPermissions: Record<string, Record<string, string>> = {}
|
||||||
for (let [resource, permission] of Object.entries(permissions)) {
|
for (let [resource, permission] of Object.entries(permissions)) {
|
||||||
const basePerms = getBasePermissions(resource)
|
const basePerms = getBasePermissions(resource)
|
||||||
finalPermissions[resource] = Object.assign(basePerms, permission)
|
finalPermissions[resource] = Object.assign(basePerms, permission)
|
||||||
|
@ -132,33 +132,36 @@ exports.fetch = async function (ctx) {
|
||||||
ctx.body = finalPermissions
|
ctx.body = finalPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getResourcePerms = async function (ctx) {
|
export async function getResourcePerms(ctx: BBContext) {
|
||||||
const resourceId = ctx.params.resourceId
|
const resourceId = ctx.params.resourceId
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const body = await db.allDocs(
|
const body = await db.allDocs(
|
||||||
getRoleParams(null, {
|
getRoleParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
const roles = body.rows.map(row => row.doc)
|
const rolesList = body.rows.map(row => row.doc)
|
||||||
let permissions = {}
|
let permissions: Record<string, string> = {}
|
||||||
for (let level of SUPPORTED_LEVELS) {
|
for (let level of SUPPORTED_LEVELS) {
|
||||||
// update the various roleIds in the resource permissions
|
// update the various roleIds in the resource permissions
|
||||||
for (let role of roles) {
|
for (let role of rolesList) {
|
||||||
const rolePerms = checkForRoleResourceArray(role.permissions, resourceId)
|
const rolePerms = roles.checkForRoleResourceArray(
|
||||||
|
role.permissions,
|
||||||
|
resourceId
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
rolePerms &&
|
rolePerms &&
|
||||||
rolePerms[resourceId] &&
|
rolePerms[resourceId] &&
|
||||||
rolePerms[resourceId].indexOf(level) !== -1
|
rolePerms[resourceId].indexOf(level) !== -1
|
||||||
) {
|
) {
|
||||||
permissions[level] = getExternalRoleID(role._id)
|
permissions[level] = roles.getExternalRoleID(role._id)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.body = Object.assign(getBasePermissions(resourceId), permissions)
|
ctx.body = Object.assign(getBasePermissions(resourceId), permissions)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.addPermission = async function (ctx) {
|
export async function addPermission(ctx: BBContext) {
|
||||||
ctx.body = await updatePermissionOnRole(
|
ctx.body = await updatePermissionOnRole(
|
||||||
ctx.appId,
|
ctx.appId,
|
||||||
ctx.params,
|
ctx.params,
|
||||||
|
@ -166,7 +169,7 @@ exports.addPermission = async function (ctx) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.removePermission = async function (ctx) {
|
export async function removePermission(ctx: BBContext) {
|
||||||
ctx.body = await updatePermissionOnRole(
|
ctx.body = await updatePermissionOnRole(
|
||||||
ctx.appId,
|
ctx.appId,
|
||||||
ctx.params,
|
ctx.params,
|
|
@ -1,6 +1,5 @@
|
||||||
import { npmUpload, urlUpload, githubUpload, fileUpload } from "./uploaders"
|
import { npmUpload, urlUpload, githubUpload, fileUpload } from "./uploaders"
|
||||||
import { getGlobalDB } from "@budibase/backend-core/tenancy"
|
import { plugins as pluginCore, tenancy } from "@budibase/backend-core"
|
||||||
import { validate } from "@budibase/backend-core/plugins"
|
|
||||||
import { PluginType, FileType, PluginSource } from "@budibase/types"
|
import { PluginType, FileType, PluginSource } from "@budibase/types"
|
||||||
import env from "../../../environment"
|
import env from "../../../environment"
|
||||||
import { ClientAppSocket } from "../../../websocket"
|
import { ClientAppSocket } from "../../../websocket"
|
||||||
|
@ -8,7 +7,7 @@ import { db as dbCore } from "@budibase/backend-core"
|
||||||
import { plugins } from "@budibase/pro"
|
import { plugins } from "@budibase/pro"
|
||||||
|
|
||||||
export async function getPlugins(type?: PluginType) {
|
export async function getPlugins(type?: PluginType) {
|
||||||
const db = getGlobalDB()
|
const db = tenancy.getGlobalDB()
|
||||||
const response = await db.allDocs(
|
const response = await db.allDocs(
|
||||||
dbCore.getPluginParams(null, {
|
dbCore.getPluginParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -76,7 +75,7 @@ export async function create(ctx: any) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(metadata?.schema)
|
pluginCore.validate(metadata?.schema)
|
||||||
|
|
||||||
// Only allow components in cloud
|
// Only allow components in cloud
|
||||||
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
|
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
|
||||||
|
@ -121,7 +120,7 @@ export async function processUploadedPlugin(
|
||||||
source?: PluginSource
|
source?: PluginSource
|
||||||
) {
|
) {
|
||||||
const { metadata, directory } = await fileUpload(plugin)
|
const { metadata, directory } = await fileUpload(plugin)
|
||||||
validate(metadata?.schema)
|
pluginCore.validate(metadata?.schema)
|
||||||
|
|
||||||
// Only allow components in cloud
|
// Only allow components in cloud
|
||||||
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
|
if (!env.SELF_HOSTED && metadata?.schema?.type !== PluginType.COMPONENT) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { search as stringSearch, addRev } from "./utils"
|
import { search as stringSearch, addRev } from "./utils"
|
||||||
import { default as controller } from "../table"
|
import * as controller from "../table"
|
||||||
import { Table } from "../../../definitions/common"
|
import { Table } from "@budibase/types"
|
||||||
|
|
||||||
function fixTable(table: Table, params: any) {
|
function fixTable(table: Table, params: any) {
|
||||||
if (!params || !table) {
|
if (!params || !table) {
|
||||||
|
|
|
@ -5,8 +5,7 @@ import { OpenAPI2 } from "./sources/openapi2"
|
||||||
import { OpenAPI3 } from "./sources/openapi3"
|
import { OpenAPI3 } from "./sources/openapi3"
|
||||||
import { Curl } from "./sources/curl"
|
import { Curl } from "./sources/curl"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { getAppDB } from "@budibase/backend-core/context"
|
import { events, context } from "@budibase/backend-core"
|
||||||
import { events } from "@budibase/backend-core"
|
|
||||||
import { Datasource, Query } from "@budibase/types"
|
import { Datasource, Query } from "@budibase/types"
|
||||||
|
|
||||||
interface ImportResult {
|
interface ImportResult {
|
||||||
|
@ -59,7 +58,7 @@ export class RestImporter {
|
||||||
})
|
})
|
||||||
|
|
||||||
// persist queries
|
// persist queries
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const response = await db.bulkDocs(queries)
|
const response = await db.bulkDocs(queries)
|
||||||
|
|
||||||
// create index to seperate queries and errors
|
// create index to seperate queries and errors
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const { joiValidator } = require("@budibase/backend-core/auth")
|
import { auth } from "@budibase/backend-core"
|
||||||
const Joi = require("joi")
|
import Joi from "joi"
|
||||||
|
|
||||||
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
||||||
|
|
||||||
exports.queryValidation = () => {
|
export function queryValidation() {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
_id: Joi.string(),
|
_id: Joi.string(),
|
||||||
_rev: Joi.string(),
|
_rev: Joi.string(),
|
||||||
|
@ -25,14 +25,14 @@ exports.queryValidation = () => {
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.generateQueryValidation = () => {
|
export function generateQueryValidation() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(exports.queryValidation())
|
return auth.joiValidator.body(queryValidation())
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.generateQueryPreviewValidation = () => {
|
export function generateQueryPreviewValidation() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return auth.joiValidator.body(Joi.object({
|
||||||
_id: OPTIONAL_STRING,
|
_id: OPTIONAL_STRING,
|
||||||
_rev: OPTIONAL_STRING,
|
_rev: OPTIONAL_STRING,
|
||||||
readable: Joi.boolean().optional(),
|
readable: Joi.boolean().optional(),
|
|
@ -1,23 +1,21 @@
|
||||||
const {
|
import { roles, context, events } from "@budibase/backend-core"
|
||||||
Role,
|
import {
|
||||||
getRole,
|
|
||||||
isBuiltin,
|
|
||||||
getAllRoles,
|
|
||||||
} = require("@budibase/backend-core/roles")
|
|
||||||
const {
|
|
||||||
generateRoleID,
|
generateRoleID,
|
||||||
getUserMetadataParams,
|
getUserMetadataParams,
|
||||||
InternalTables,
|
InternalTables,
|
||||||
} = require("../../db/utils")
|
} from "../../db/utils"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { BBContext, Database } from "@budibase/types"
|
||||||
const { events } = require("@budibase/backend-core")
|
|
||||||
|
|
||||||
const UpdateRolesOptions = {
|
const UpdateRolesOptions = {
|
||||||
CREATED: "created",
|
CREATED: "created",
|
||||||
REMOVED: "removed",
|
REMOVED: "removed",
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateRolesOnUserTable(db, roleId, updateOption) {
|
async function updateRolesOnUserTable(
|
||||||
|
db: Database,
|
||||||
|
roleId: string,
|
||||||
|
updateOption: string
|
||||||
|
) {
|
||||||
const table = await db.get(InternalTables.USER_METADATA)
|
const table = await db.get(InternalTables.USER_METADATA)
|
||||||
const schema = table.schema
|
const schema = table.schema
|
||||||
const remove = updateOption === UpdateRolesOptions.REMOVED
|
const remove = updateOption === UpdateRolesOptions.REMOVED
|
||||||
|
@ -40,27 +38,25 @@ async function updateRolesOnUserTable(db, roleId, updateOption) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
export async function fetch(ctx: BBContext) {
|
||||||
ctx.body = await getAllRoles()
|
ctx.body = await roles.getAllRoles()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
export async function find(ctx: BBContext) {
|
||||||
ctx.body = await getRole(ctx.params.roleId)
|
ctx.body = await roles.getRole(ctx.params.roleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
export async function save(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let { _id, name, inherits, permissionId } = ctx.request.body
|
let { _id, name, inherits, permissionId } = ctx.request.body
|
||||||
let isCreate = false
|
let isCreate = false
|
||||||
if (!_id) {
|
if (!_id) {
|
||||||
_id = generateRoleID()
|
_id = generateRoleID()
|
||||||
isCreate = true
|
isCreate = true
|
||||||
} else if (isBuiltin(_id)) {
|
} else if (roles.isBuiltin(_id)) {
|
||||||
ctx.throw(400, "Cannot update builtin roles.")
|
ctx.throw(400, "Cannot update builtin roles.")
|
||||||
}
|
}
|
||||||
const role = new Role(_id, name)
|
const role = new roles.Role(_id, name, permissionId).addInheritance(inherits)
|
||||||
.addPermission(permissionId)
|
|
||||||
.addInheritance(inherits)
|
|
||||||
if (ctx.request.body._rev) {
|
if (ctx.request.body._rev) {
|
||||||
role._rev = ctx.request.body._rev
|
role._rev = ctx.request.body._rev
|
||||||
}
|
}
|
||||||
|
@ -76,17 +72,17 @@ exports.save = async function (ctx) {
|
||||||
ctx.message = `Role '${role.name}' created successfully.`
|
ctx.message = `Role '${role.name}' created successfully.`
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
export async function destroy(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const roleId = ctx.params.roleId
|
const roleId = ctx.params.roleId
|
||||||
const role = await db.get(roleId)
|
const role = await db.get(roleId)
|
||||||
if (isBuiltin(roleId)) {
|
if (roles.isBuiltin(roleId)) {
|
||||||
ctx.throw(400, "Cannot delete builtin role.")
|
ctx.throw(400, "Cannot delete builtin role.")
|
||||||
}
|
}
|
||||||
// first check no users actively attached to role
|
// first check no users actively attached to role
|
||||||
const users = (
|
const users = (
|
||||||
await db.allDocs(
|
await db.allDocs(
|
||||||
getUserMetadataParams(null, {
|
getUserMetadataParams(undefined, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
|
@ -1,40 +1,41 @@
|
||||||
const { getRoutingInfo } = require("../../utilities/routing")
|
import { getRoutingInfo } from "../../utilities/routing"
|
||||||
const {
|
import { roles } from "@budibase/backend-core"
|
||||||
getUserRoleHierarchy,
|
import { BBContext } from "@budibase/types"
|
||||||
BUILTIN_ROLE_IDS,
|
|
||||||
} = require("@budibase/backend-core/roles")
|
|
||||||
|
|
||||||
const URL_SEPARATOR = "/"
|
const URL_SEPARATOR = "/"
|
||||||
|
|
||||||
function Routing() {
|
class Routing {
|
||||||
this.json = {}
|
json: any
|
||||||
}
|
constructor() {
|
||||||
|
this.json = {}
|
||||||
Routing.prototype.getTopLevel = function (fullpath) {
|
|
||||||
if (fullpath.charAt(0) !== URL_SEPARATOR) {
|
|
||||||
fullpath = URL_SEPARATOR + fullpath
|
|
||||||
}
|
}
|
||||||
// replace the first value with the home route
|
|
||||||
return URL_SEPARATOR + fullpath.split(URL_SEPARATOR)[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
Routing.prototype.getScreensProp = function (fullpath) {
|
getTopLevel(fullpath: string) {
|
||||||
const topLevel = this.getTopLevel(fullpath)
|
if (fullpath.charAt(0) !== URL_SEPARATOR) {
|
||||||
if (!this.json[topLevel]) {
|
fullpath = URL_SEPARATOR + fullpath
|
||||||
this.json[topLevel] = {
|
|
||||||
subpaths: {},
|
|
||||||
}
|
}
|
||||||
|
// replace the first value with the home route
|
||||||
|
return URL_SEPARATOR + fullpath.split(URL_SEPARATOR)[1]
|
||||||
}
|
}
|
||||||
if (!this.json[topLevel].subpaths[fullpath]) {
|
|
||||||
this.json[topLevel].subpaths[fullpath] = {
|
|
||||||
screens: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.json[topLevel].subpaths[fullpath].screens
|
|
||||||
}
|
|
||||||
|
|
||||||
Routing.prototype.addScreenId = function (fullpath, roleId, screenId) {
|
getScreensProp(fullpath: string) {
|
||||||
this.getScreensProp(fullpath)[roleId] = screenId
|
const topLevel = this.getTopLevel(fullpath)
|
||||||
|
if (!this.json[topLevel]) {
|
||||||
|
this.json[topLevel] = {
|
||||||
|
subpaths: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.json[topLevel].subpaths[fullpath]) {
|
||||||
|
this.json[topLevel].subpaths[fullpath] = {
|
||||||
|
screens: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.json[topLevel].subpaths[fullpath].screens
|
||||||
|
}
|
||||||
|
|
||||||
|
addScreenId(fullpath: string, roleId: string, screenId: string) {
|
||||||
|
this.getScreensProp(fullpath)[roleId] = screenId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,26 +56,28 @@ async function getRoutingStructure() {
|
||||||
return { routes: routing.json }
|
return { routes: routing.json }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
export async function fetch(ctx: BBContext) {
|
||||||
ctx.body = await getRoutingStructure()
|
ctx.body = await getRoutingStructure()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.clientFetch = async ctx => {
|
export async function clientFetch(ctx: BBContext) {
|
||||||
const routing = await getRoutingStructure()
|
const routing = await getRoutingStructure()
|
||||||
let roleId = ctx.user.role._id
|
let roleId = ctx.user?.role?._id
|
||||||
const roleIds = await getUserRoleHierarchy(roleId)
|
const roleIds = (await roles.getUserRoleHierarchy(roleId, {
|
||||||
for (let topLevel of Object.values(routing.routes)) {
|
idOnly: true,
|
||||||
|
})) as string[]
|
||||||
|
for (let topLevel of Object.values(routing.routes) as any) {
|
||||||
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
||||||
let found = false
|
let found = false
|
||||||
const subpath = topLevel.subpaths[subpathKey]
|
const subpath = topLevel.subpaths[subpathKey]
|
||||||
const roleOptions = Object.keys(subpath.screens)
|
const roleOptions = Object.keys(subpath.screens)
|
||||||
if (roleOptions.length === 1 && !roleOptions[0]) {
|
if (roleOptions.length === 1 && !roleOptions[0]) {
|
||||||
subpath.screenId = subpath.screens[roleOptions[0]]
|
subpath.screenId = subpath.screens[roleOptions[0]]
|
||||||
subpath.roleId = BUILTIN_ROLE_IDS.BASIC
|
subpath.roleId = roles.BUILTIN_ROLE_IDS.BASIC
|
||||||
found = true
|
found = true
|
||||||
} else {
|
} else {
|
||||||
for (let roleId of roleIds) {
|
for (let roleId of roleIds) {
|
||||||
if (roleOptions.indexOf(roleId) !== -1) {
|
if (roleId && roleOptions.indexOf(roleId) !== -1) {
|
||||||
subpath.screenId = subpath.screens[roleId]
|
subpath.screenId = subpath.screens[roleId]
|
||||||
subpath.roleId = roleId
|
subpath.roleId = roleId
|
||||||
found = true
|
found = true
|
|
@ -27,12 +27,8 @@ import { breakExternalTableId, isSQL } from "../../../integrations/utils"
|
||||||
import { processObjectSync } from "@budibase/string-templates"
|
import { processObjectSync } from "@budibase/string-templates"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import {
|
import { processFormulas, processDates } from "../../../utilities/rowProcessor"
|
||||||
processFormulas,
|
import { context } from "@budibase/backend-core"
|
||||||
processDates,
|
|
||||||
} from "../../../utilities/rowProcessor/utils"
|
|
||||||
// @ts-ignore
|
|
||||||
import { getAppDB } from "@budibase/backend-core/context"
|
|
||||||
|
|
||||||
interface ManyRelationship {
|
interface ManyRelationship {
|
||||||
tableId?: string
|
tableId?: string
|
||||||
|
@ -444,7 +440,7 @@ module External {
|
||||||
// Process some additional data types
|
// Process some additional data types
|
||||||
let finalRowArray = Object.values(finalRows)
|
let finalRowArray = Object.values(finalRows)
|
||||||
finalRowArray = processDates(table, finalRowArray)
|
finalRowArray = processDates(table, finalRowArray)
|
||||||
finalRowArray = processFormulas(table, finalRowArray)
|
finalRowArray = processFormulas(table, finalRowArray) as Row[]
|
||||||
|
|
||||||
return finalRowArray.map((row: Row) =>
|
return finalRowArray.map((row: Row) =>
|
||||||
this.squashRelationshipColumns(table, row, relationships)
|
this.squashRelationshipColumns(table, row, relationships)
|
||||||
|
@ -673,7 +669,7 @@ module External {
|
||||||
throw "Unable to run without a table name"
|
throw "Unable to run without a table name"
|
||||||
}
|
}
|
||||||
if (!this.datasource) {
|
if (!this.datasource) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
this.datasource = await db.get(datasourceId)
|
this.datasource = await db.get(datasourceId)
|
||||||
if (!this.datasource || !this.datasource.entities) {
|
if (!this.datasource || !this.datasource.entities) {
|
||||||
throw "No tables found, fetch tables before query."
|
throw "No tables found, fetch tables before query."
|
||||||
|
|
|
@ -9,7 +9,7 @@ const {
|
||||||
breakRowIdField,
|
breakRowIdField,
|
||||||
} = require("../../../integrations/utils")
|
} = require("../../../integrations/utils")
|
||||||
const ExternalRequest = require("./ExternalRequest")
|
const ExternalRequest = require("./ExternalRequest")
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { context } = require("@budibase/backend-core")
|
||||||
const exporters = require("../view/exporters")
|
const exporters = require("../view/exporters")
|
||||||
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ exports.validate = async () => {
|
||||||
|
|
||||||
exports.exportRows = async ctx => {
|
exports.exportRows = async ctx => {
|
||||||
const { datasourceId } = breakExternalTableId(ctx.params.tableId)
|
const { datasourceId } = breakExternalTableId(ctx.params.tableId)
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format
|
||||||
const { columns } = ctx.request.body
|
const { columns } = ctx.request.body
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
|
@ -209,7 +209,7 @@ exports.fetchEnrichedRow = async ctx => {
|
||||||
const id = ctx.params.rowId
|
const id = ctx.params.rowId
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
const { datasourceId, tableName } = breakExternalTableId(tableId)
|
const { datasourceId, tableName } = breakExternalTableId(tableId)
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
if (!datasource || !datasource.entities) {
|
if (!datasource || !datasource.entities) {
|
||||||
ctx.throw(400, "Datasource has not been configured for plus API.")
|
ctx.throw(400, "Datasource has not been configured for plus API.")
|
||||||
|
|
|
@ -6,7 +6,6 @@ const {
|
||||||
DocumentType,
|
DocumentType,
|
||||||
InternalTables,
|
InternalTables,
|
||||||
} = require("../../../db/utils")
|
} = require("../../../db/utils")
|
||||||
const { getDB } = require("@budibase/backend-core/db")
|
|
||||||
const userController = require("../user")
|
const userController = require("../user")
|
||||||
const {
|
const {
|
||||||
inputProcessing,
|
inputProcessing,
|
||||||
|
@ -26,7 +25,7 @@ const {
|
||||||
getFromMemoryDoc,
|
getFromMemoryDoc,
|
||||||
} = require("../view/utils")
|
} = require("../view/utils")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
const { context, db: dbCore } = require("@budibase/backend-core")
|
||||||
const { finaliseRow, updateRelatedFormula } = require("./staticFormula")
|
const { finaliseRow, updateRelatedFormula } = require("./staticFormula")
|
||||||
const exporters = require("../view/exporters")
|
const exporters = require("../view/exporters")
|
||||||
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
||||||
|
@ -80,7 +79,7 @@ async function getRawTableData(ctx, db, tableId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.patch = async ctx => {
|
exports.patch = async ctx => {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const inputs = ctx.request.body
|
const inputs = ctx.request.body
|
||||||
const tableId = inputs.tableId
|
const tableId = inputs.tableId
|
||||||
const isUserTable = tableId === InternalTables.USER_METADATA
|
const isUserTable = tableId === InternalTables.USER_METADATA
|
||||||
|
@ -145,7 +144,7 @@ exports.patch = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let inputs = ctx.request.body
|
let inputs = ctx.request.body
|
||||||
inputs.tableId = ctx.params.tableId
|
inputs.tableId = ctx.params.tableId
|
||||||
|
|
||||||
|
@ -188,7 +187,7 @@ exports.fetchView = async ctx => {
|
||||||
return exports.fetch(ctx)
|
return exports.fetch(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const { calculation, group, field } = ctx.query
|
const { calculation, group, field } = ctx.query
|
||||||
const viewInfo = await getView(db, viewName)
|
const viewInfo = await getView(db, viewName)
|
||||||
let response
|
let response
|
||||||
|
@ -242,7 +241,7 @@ exports.fetchView = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
|
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
let table = await db.get(tableId)
|
let table = await db.get(tableId)
|
||||||
|
@ -251,7 +250,7 @@ exports.fetch = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async ctx => {
|
exports.find = async ctx => {
|
||||||
const db = getDB(ctx.appId)
|
const db = dbCore.getDB(ctx.appId)
|
||||||
const table = await db.get(ctx.params.tableId)
|
const table = await db.get(ctx.params.tableId)
|
||||||
let row = await findRow(ctx, ctx.params.tableId, ctx.params.rowId)
|
let row = await findRow(ctx, ctx.params.tableId, ctx.params.rowId)
|
||||||
row = await outputProcessing(table, row)
|
row = await outputProcessing(table, row)
|
||||||
|
@ -259,7 +258,7 @@ exports.find = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
exports.destroy = async function (ctx) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const { _id } = ctx.request.body
|
const { _id } = ctx.request.body
|
||||||
let row = await db.get(_id)
|
let row = await db.get(_id)
|
||||||
let _rev = ctx.request.body._rev || row._rev
|
let _rev = ctx.request.body._rev || row._rev
|
||||||
|
@ -295,7 +294,7 @@ exports.destroy = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.bulkDestroy = async ctx => {
|
exports.bulkDestroy = async ctx => {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
const table = await db.get(tableId)
|
const table = await db.get(tableId)
|
||||||
let { rows } = ctx.request.body
|
let { rows } = ctx.request.body
|
||||||
|
@ -338,7 +337,7 @@ exports.search = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tableId } = ctx.params
|
const { tableId } = ctx.params
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const { paginate, query, ...params } = ctx.request.body
|
const { paginate, query, ...params } = ctx.request.body
|
||||||
params.version = ctx.version
|
params.version = ctx.version
|
||||||
params.tableId = tableId
|
params.tableId = tableId
|
||||||
|
@ -371,7 +370,7 @@ exports.validate = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.exportRows = async ctx => {
|
exports.exportRows = async ctx => {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const table = await db.get(ctx.params.tableId)
|
const table = await db.get(ctx.params.tableId)
|
||||||
const rowIds = ctx.request.body.rows
|
const rowIds = ctx.request.body.rows
|
||||||
let format = ctx.query.format
|
let format = ctx.query.format
|
||||||
|
@ -408,7 +407,7 @@ exports.exportRows = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchEnrichedRow = async ctx => {
|
exports.fetchEnrichedRow = async ctx => {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
const rowId = ctx.params.rowId
|
const rowId = ctx.params.rowId
|
||||||
// need table to work out where links go in row
|
// need table to work out where links go in row
|
||||||
|
|
|
@ -1,15 +1,35 @@
|
||||||
const { SearchIndexes } = require("../../../db/utils")
|
import { SearchIndexes } from "../../../db/utils"
|
||||||
const { removeKeyNumbering } = require("./utils")
|
import { removeKeyNumbering } from "./utils"
|
||||||
const fetch = require("node-fetch")
|
import fetch from "node-fetch"
|
||||||
const { getCouchInfo } = require("@budibase/backend-core/db")
|
import { db as dbCore, context } from "@budibase/backend-core"
|
||||||
const { getAppId } = require("@budibase/backend-core/context")
|
import { SearchFilters, Row } from "@budibase/types"
|
||||||
|
|
||||||
|
type SearchParams = {
|
||||||
|
tableId: string
|
||||||
|
sort?: string
|
||||||
|
sortOrder?: string
|
||||||
|
sortType?: string
|
||||||
|
limit?: number
|
||||||
|
bookmark?: string
|
||||||
|
version?: string
|
||||||
|
rows?: Row[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to build lucene query URLs.
|
* Class to build lucene query URLs.
|
||||||
* Optionally takes a base lucene query object.
|
* Optionally takes a base lucene query object.
|
||||||
*/
|
*/
|
||||||
class QueryBuilder {
|
export class QueryBuilder {
|
||||||
constructor(base) {
|
query: SearchFilters
|
||||||
|
limit: number
|
||||||
|
sort?: string
|
||||||
|
bookmark?: string
|
||||||
|
sortOrder: string
|
||||||
|
sortType: string
|
||||||
|
includeDocs: boolean
|
||||||
|
version?: string
|
||||||
|
|
||||||
|
constructor(base?: SearchFilters) {
|
||||||
this.query = {
|
this.query = {
|
||||||
allOr: false,
|
allOr: false,
|
||||||
string: {},
|
string: {},
|
||||||
|
@ -29,49 +49,52 @@ class QueryBuilder {
|
||||||
this.sortOrder = "ascending"
|
this.sortOrder = "ascending"
|
||||||
this.sortType = "string"
|
this.sortType = "string"
|
||||||
this.includeDocs = true
|
this.includeDocs = true
|
||||||
this.version = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setVersion(version) {
|
setVersion(version?: string) {
|
||||||
this.version = version
|
if (version != null) {
|
||||||
|
this.version = version
|
||||||
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setTable(tableId) {
|
setTable(tableId: string) {
|
||||||
this.query.equal.tableId = tableId
|
this.query.equal!.tableId = tableId
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setLimit(limit) {
|
setLimit(limit?: number) {
|
||||||
if (limit != null) {
|
if (limit != null) {
|
||||||
this.limit = limit
|
this.limit = limit
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setSort(sort) {
|
setSort(sort?: string) {
|
||||||
if (sort != null) {
|
if (sort != null) {
|
||||||
this.sort = sort
|
this.sort = sort
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setSortOrder(sortOrder) {
|
setSortOrder(sortOrder?: string) {
|
||||||
if (sortOrder != null) {
|
if (sortOrder != null) {
|
||||||
this.sortOrder = sortOrder
|
this.sortOrder = sortOrder
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setSortType(sortType) {
|
setSortType(sortType?: string) {
|
||||||
if (sortType != null) {
|
if (sortType != null) {
|
||||||
this.sortType = sortType
|
this.sortType = sortType
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
setBookmark(bookmark) {
|
setBookmark(bookmark?: string) {
|
||||||
this.bookmark = bookmark
|
if (bookmark != null) {
|
||||||
|
this.bookmark = bookmark
|
||||||
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,61 +103,61 @@ class QueryBuilder {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addString(key, partial) {
|
addString(key: string, partial: string) {
|
||||||
this.query.string[key] = partial
|
this.query.string![key] = partial
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addFuzzy(key, fuzzy) {
|
addFuzzy(key: string, fuzzy: string) {
|
||||||
this.query.fuzzy[key] = fuzzy
|
this.query.fuzzy![key] = fuzzy
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addRange(key, low, high) {
|
addRange(key: string, low: string | number, high: string | number) {
|
||||||
this.query.range = {
|
this.query.range![key] = {
|
||||||
low,
|
low,
|
||||||
high,
|
high,
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addEqual(key, value) {
|
addEqual(key: string, value: any) {
|
||||||
this.query.equal[key] = value
|
this.query.equal![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addNotEqual(key, value) {
|
addNotEqual(key: string, value: any) {
|
||||||
this.query.notEqual[key] = value
|
this.query.notEqual![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addEmpty(key, value) {
|
addEmpty(key: string, value: any) {
|
||||||
this.query.empty[key] = value
|
this.query.empty![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addNotEmpty(key, value) {
|
addNotEmpty(key: string, value: any) {
|
||||||
this.query.notEmpty[key] = value
|
this.query.notEmpty![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addOneOf(key, value) {
|
addOneOf(key: string, value: any) {
|
||||||
this.query.oneOf[key] = value
|
this.query.oneOf![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addContains(key, value) {
|
addContains(key: string, value: any) {
|
||||||
this.query.contains[key] = value
|
this.query.contains![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addNotContains(key, value) {
|
addNotContains(key: string, value: any) {
|
||||||
this.query.notContains[key] = value
|
this.query.notContains![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
addContainsAny(key, value) {
|
addContainsAny(key: string, value: any) {
|
||||||
this.query.containsAny[key] = value
|
this.query.containsAny![key] = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +168,7 @@ class QueryBuilder {
|
||||||
* @param options The preprocess options
|
* @param options The preprocess options
|
||||||
* @returns {string|*}
|
* @returns {string|*}
|
||||||
*/
|
*/
|
||||||
preprocess(value, { escape, lowercase, wrap, type } = {}) {
|
preprocess(value: any, { escape, lowercase, wrap, type }: any = {}) {
|
||||||
const hasVersion = !!this.version
|
const hasVersion = !!this.version
|
||||||
// Determine if type needs wrapped
|
// Determine if type needs wrapped
|
||||||
const originalType = typeof value
|
const originalType = typeof value
|
||||||
|
@ -173,12 +196,12 @@ class QueryBuilder {
|
||||||
let query = allOr ? "" : "*:*"
|
let query = allOr ? "" : "*:*"
|
||||||
const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true }
|
const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true }
|
||||||
let tableId
|
let tableId
|
||||||
if (this.query.equal.tableId) {
|
if (this.query.equal!.tableId) {
|
||||||
tableId = this.query.equal.tableId
|
tableId = this.query.equal!.tableId
|
||||||
delete this.query.equal.tableId
|
delete this.query.equal!.tableId
|
||||||
}
|
}
|
||||||
|
|
||||||
const equal = (key, value) => {
|
const equal = (key: string, value: any) => {
|
||||||
// 0 evaluates to false, which means we would return all rows if we don't check it
|
// 0 evaluates to false, which means we would return all rows if we don't check it
|
||||||
if (!value && value !== 0) {
|
if (!value && value !== 0) {
|
||||||
return null
|
return null
|
||||||
|
@ -186,7 +209,7 @@ class QueryBuilder {
|
||||||
return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
|
return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const contains = (key, value, mode = "AND") => {
|
const contains = (key: string, value: any, mode = "AND") => {
|
||||||
if (Array.isArray(value) && value.length === 0) {
|
if (Array.isArray(value) && value.length === 0) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -202,16 +225,17 @@ class QueryBuilder {
|
||||||
return `${key}:(${statement})`
|
return `${key}:(${statement})`
|
||||||
}
|
}
|
||||||
|
|
||||||
const notContains = (key, value) => {
|
const notContains = (key: string, value: any) => {
|
||||||
|
// @ts-ignore
|
||||||
const allPrefix = allOr === "" ? "*:* AND" : ""
|
const allPrefix = allOr === "" ? "*:* AND" : ""
|
||||||
return allPrefix + "NOT " + contains(key, value)
|
return allPrefix + "NOT " + contains(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const containsAny = (key, value) => {
|
const containsAny = (key: string, value: any) => {
|
||||||
return contains(key, value, "OR")
|
return contains(key, value, "OR")
|
||||||
}
|
}
|
||||||
|
|
||||||
const oneOf = (key, value) => {
|
const oneOf = (key: string, value: any) => {
|
||||||
if (!Array.isArray(value)) {
|
if (!Array.isArray(value)) {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
value = value.split(",")
|
value = value.split(",")
|
||||||
|
@ -229,7 +253,7 @@ class QueryBuilder {
|
||||||
return `${key}:(${orStatement})`
|
return `${key}:(${orStatement})`
|
||||||
}
|
}
|
||||||
|
|
||||||
function build(structure, queryFn) {
|
function build(structure: any, queryFn: any) {
|
||||||
for (let [key, value] of Object.entries(structure)) {
|
for (let [key, value] of Object.entries(structure)) {
|
||||||
// check for new format - remove numbering if needed
|
// check for new format - remove numbering if needed
|
||||||
key = removeKeyNumbering(key)
|
key = removeKeyNumbering(key)
|
||||||
|
@ -249,7 +273,7 @@ class QueryBuilder {
|
||||||
|
|
||||||
// Construct the actual lucene search query string from JSON structure
|
// Construct the actual lucene search query string from JSON structure
|
||||||
if (this.query.string) {
|
if (this.query.string) {
|
||||||
build(this.query.string, (key, value) => {
|
build(this.query.string, (key: string, value: any) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -262,7 +286,7 @@ class QueryBuilder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.query.range) {
|
if (this.query.range) {
|
||||||
build(this.query.range, (key, value) => {
|
build(this.query.range, (key: string, value: any) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -278,7 +302,7 @@ class QueryBuilder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.query.fuzzy) {
|
if (this.query.fuzzy) {
|
||||||
build(this.query.fuzzy, (key, value) => {
|
build(this.query.fuzzy, (key: string, value: any) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -294,7 +318,7 @@ class QueryBuilder {
|
||||||
build(this.query.equal, equal)
|
build(this.query.equal, equal)
|
||||||
}
|
}
|
||||||
if (this.query.notEqual) {
|
if (this.query.notEqual) {
|
||||||
build(this.query.notEqual, (key, value) => {
|
build(this.query.notEqual, (key: string, value: any) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -302,10 +326,10 @@ class QueryBuilder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this.query.empty) {
|
if (this.query.empty) {
|
||||||
build(this.query.empty, key => `!${key}:["" TO *]`)
|
build(this.query.empty, (key: string) => `!${key}:["" TO *]`)
|
||||||
}
|
}
|
||||||
if (this.query.notEmpty) {
|
if (this.query.notEmpty) {
|
||||||
build(this.query.notEmpty, key => `${key}:["" TO *]`)
|
build(this.query.notEmpty, (key: string) => `${key}:["" TO *]`)
|
||||||
}
|
}
|
||||||
if (this.query.oneOf) {
|
if (this.query.oneOf) {
|
||||||
build(this.query.oneOf, oneOf)
|
build(this.query.oneOf, oneOf)
|
||||||
|
@ -329,7 +353,7 @@ class QueryBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildSearchBody() {
|
buildSearchBody() {
|
||||||
let body = {
|
let body: any = {
|
||||||
q: this.buildSearchQuery(),
|
q: this.buildSearchQuery(),
|
||||||
limit: Math.min(this.limit, 200),
|
limit: Math.min(this.limit, 200),
|
||||||
include_docs: this.includeDocs,
|
include_docs: this.includeDocs,
|
||||||
|
@ -346,17 +370,14 @@ class QueryBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
const appId = getAppId()
|
const appId = context.getAppId()
|
||||||
const { url, cookie } = getCouchInfo()
|
const { url, cookie } = dbCore.getCouchInfo()
|
||||||
const fullPath = `${url}/${appId}/_design/database/_search/${SearchIndexes.ROWS}`
|
const fullPath = `${url}/${appId}/_design/database/_search/${SearchIndexes.ROWS}`
|
||||||
const body = this.buildSearchBody()
|
const body = this.buildSearchBody()
|
||||||
return await runQuery(fullPath, body, cookie)
|
return await runQuery(fullPath, body, cookie)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// exported for unit testing
|
|
||||||
exports.QueryBuilder = QueryBuilder
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a lucene search query.
|
* Executes a lucene search query.
|
||||||
* @param url The query URL
|
* @param url The query URL
|
||||||
|
@ -364,7 +385,7 @@ exports.QueryBuilder = QueryBuilder
|
||||||
* @param cookie The auth cookie for CouchDB
|
* @param cookie The auth cookie for CouchDB
|
||||||
* @returns {Promise<{rows: []}>}
|
* @returns {Promise<{rows: []}>}
|
||||||
*/
|
*/
|
||||||
const runQuery = async (url, body, cookie) => {
|
const runQuery = async (url: string, body: any, cookie: string) => {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -374,11 +395,11 @@ const runQuery = async (url, body, cookie) => {
|
||||||
})
|
})
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
|
||||||
let output = {
|
let output: any = {
|
||||||
rows: [],
|
rows: [],
|
||||||
}
|
}
|
||||||
if (json.rows != null && json.rows.length > 0) {
|
if (json.rows != null && json.rows.length > 0) {
|
||||||
output.rows = json.rows.map(row => row.doc)
|
output.rows = json.rows.map((row: any) => row.doc)
|
||||||
}
|
}
|
||||||
if (json.bookmark) {
|
if (json.bookmark) {
|
||||||
output.bookmark = json.bookmark
|
output.bookmark = json.bookmark
|
||||||
|
@ -402,7 +423,7 @@ const runQuery = async (url, body, cookie) => {
|
||||||
* rows {array|null} Current results in the recursive search
|
* rows {array|null} Current results in the recursive search
|
||||||
* @returns {Promise<*[]|*>}
|
* @returns {Promise<*[]|*>}
|
||||||
*/
|
*/
|
||||||
const recursiveSearch = async (query, params) => {
|
async function recursiveSearch(query: any, params: any): Promise<any> {
|
||||||
const bookmark = params.bookmark
|
const bookmark = params.bookmark
|
||||||
const rows = params.rows || []
|
const rows = params.rows || []
|
||||||
if (rows.length >= params.limit) {
|
if (rows.length >= params.limit) {
|
||||||
|
@ -450,7 +471,10 @@ const recursiveSearch = async (query, params) => {
|
||||||
* bookmark {string} The bookmark to resume from
|
* bookmark {string} The bookmark to resume from
|
||||||
* @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
|
* @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
|
||||||
*/
|
*/
|
||||||
exports.paginatedSearch = async (query, params) => {
|
export async function paginatedSearch(
|
||||||
|
query: SearchFilters,
|
||||||
|
params: SearchParams
|
||||||
|
) {
|
||||||
let limit = params.limit
|
let limit = params.limit
|
||||||
if (limit == null || isNaN(limit) || limit < 0) {
|
if (limit == null || isNaN(limit) || limit < 0) {
|
||||||
limit = 50
|
limit = 50
|
||||||
|
@ -496,7 +520,7 @@ exports.paginatedSearch = async (query, params) => {
|
||||||
* limit {number} The desired number of results
|
* limit {number} The desired number of results
|
||||||
* @returns {Promise<{rows: *}>}
|
* @returns {Promise<{rows: *}>}
|
||||||
*/
|
*/
|
||||||
exports.fullSearch = async (query, params) => {
|
export async function fullSearch(query: SearchFilters, params: SearchParams) {
|
||||||
let limit = params.limit
|
let limit = params.limit
|
||||||
if (limit == null || isNaN(limit) || limit < 0) {
|
if (limit == null || isNaN(limit) || limit < 0) {
|
||||||
limit = 1000
|
limit = 1000
|
|
@ -1,13 +1,14 @@
|
||||||
const { getRowParams } = require("../../../db/utils")
|
import { getRowParams } from "../../../db/utils"
|
||||||
const {
|
import {
|
||||||
outputProcessing,
|
outputProcessing,
|
||||||
processAutoColumn,
|
processAutoColumn,
|
||||||
processFormulas,
|
processFormulas,
|
||||||
} = require("../../../utilities/rowProcessor")
|
} from "../../../utilities/rowProcessor"
|
||||||
const { FieldTypes, FormulaTypes } = require("../../../constants")
|
import { FieldTypes, FormulaTypes } from "../../../constants"
|
||||||
|
import { context } from "@budibase/backend-core"
|
||||||
|
import { Table, Row } from "@budibase/types"
|
||||||
const { isEqual } = require("lodash")
|
const { isEqual } = require("lodash")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function runs through a list of enriched rows, looks at the rows which
|
* This function runs through a list of enriched rows, looks at the rows which
|
||||||
|
@ -15,22 +16,22 @@ const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
* updated.
|
* updated.
|
||||||
* NOTE: this will only for affect static formulas.
|
* NOTE: this will only for affect static formulas.
|
||||||
*/
|
*/
|
||||||
exports.updateRelatedFormula = async (table, enrichedRows) => {
|
exports.updateRelatedFormula = async (table: Table, enrichedRows: Row[]) => {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
// no formula to update, we're done
|
// no formula to update, we're done
|
||||||
if (!table.relatedFormula) {
|
if (!table.relatedFormula) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let promises = []
|
let promises: Promise<any>[] = []
|
||||||
for (let enrichedRow of Array.isArray(enrichedRows)
|
for (let enrichedRow of Array.isArray(enrichedRows)
|
||||||
? enrichedRows
|
? enrichedRows
|
||||||
: [enrichedRows]) {
|
: [enrichedRows]) {
|
||||||
// the related rows by tableId
|
// the related rows by tableId
|
||||||
let relatedRows = {}
|
let relatedRows: Record<string, Row[]> = {}
|
||||||
for (let [key, field] of Object.entries(enrichedRow)) {
|
for (let [key, field] of Object.entries(enrichedRow)) {
|
||||||
const columnDefinition = table.schema[key]
|
const columnDefinition = table.schema[key]
|
||||||
if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
|
if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
|
||||||
const relatedTableId = columnDefinition.tableId
|
const relatedTableId = columnDefinition.tableId!
|
||||||
if (!relatedRows[relatedTableId]) {
|
if (!relatedRows[relatedTableId]) {
|
||||||
relatedRows[relatedTableId] = []
|
relatedRows[relatedTableId] = []
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let tableId of table.relatedFormula) {
|
for (let tableId of table.relatedFormula) {
|
||||||
let relatedTable
|
let relatedTable: Table
|
||||||
try {
|
try {
|
||||||
// no rows to update, skip
|
// no rows to update, skip
|
||||||
if (!relatedRows[tableId] || relatedRows[tableId].length === 0) {
|
if (!relatedRows[tableId] || relatedRows[tableId].length === 0) {
|
||||||
|
@ -48,7 +49,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// no error scenario, table doesn't seem to exist anymore, ignore
|
// no error scenario, table doesn't seem to exist anymore, ignore
|
||||||
}
|
}
|
||||||
for (let column of Object.values(relatedTable.schema)) {
|
for (let column of Object.values(relatedTable!.schema)) {
|
||||||
// needs updated in related rows
|
// needs updated in related rows
|
||||||
if (
|
if (
|
||||||
column.type === FieldTypes.FORMULA &&
|
column.type === FieldTypes.FORMULA &&
|
||||||
|
@ -57,7 +58,7 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
|
||||||
// re-enrich rows for all the related, don't update the related formula for them
|
// re-enrich rows for all the related, don't update the related formula for them
|
||||||
promises = promises.concat(
|
promises = promises.concat(
|
||||||
relatedRows[tableId].map(related =>
|
relatedRows[tableId].map(related =>
|
||||||
exports.finaliseRow(relatedTable, related, {
|
finaliseRow(relatedTable, related, {
|
||||||
updateFormula: false,
|
updateFormula: false,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -70,8 +71,8 @@ exports.updateRelatedFormula = async (table, enrichedRows) => {
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateAllFormulasInTable = async table => {
|
export async function updateAllFormulasInTable(table: Table) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
// start by getting the raw rows (which will be written back to DB after update)
|
// start by getting the raw rows (which will be written back to DB after update)
|
||||||
let rows = (
|
let rows = (
|
||||||
await db.allDocs(
|
await db.allDocs(
|
||||||
|
@ -88,7 +89,9 @@ exports.updateAllFormulasInTable = async table => {
|
||||||
const updatedRows = []
|
const updatedRows = []
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
// find the enriched row, if found process the formulas
|
// find the enriched row, if found process the formulas
|
||||||
const enrichedRow = enrichedRows.find(enriched => enriched._id === row._id)
|
const enrichedRow = enrichedRows.find(
|
||||||
|
(enriched: any) => enriched._id === row._id
|
||||||
|
)
|
||||||
if (enrichedRow) {
|
if (enrichedRow) {
|
||||||
const processed = processFormulas(table, cloneDeep(row), {
|
const processed = processFormulas(table, cloneDeep(row), {
|
||||||
dynamic: false,
|
dynamic: false,
|
||||||
|
@ -109,12 +112,14 @@ exports.updateAllFormulasInTable = async table => {
|
||||||
* row. The reason we need to return the enriched row is that the automation row created trigger
|
* row. The reason we need to return the enriched row is that the automation row created trigger
|
||||||
* expects the row to be totally enriched/contain all relationships.
|
* expects the row to be totally enriched/contain all relationships.
|
||||||
*/
|
*/
|
||||||
exports.finaliseRow = async (
|
export async function finaliseRow(
|
||||||
table,
|
table: Table,
|
||||||
row,
|
row: Row,
|
||||||
{ oldTable, updateFormula } = { updateFormula: true }
|
{ oldTable, updateFormula }: { oldTable?: Table; updateFormula: boolean } = {
|
||||||
) => {
|
updateFormula: true,
|
||||||
const db = getAppDB()
|
}
|
||||||
|
) {
|
||||||
|
const db = context.getAppDB()
|
||||||
row.type = "row"
|
row.type = "row"
|
||||||
// process the row before return, to include relationships
|
// process the row before return, to include relationships
|
||||||
let enrichedRow = await outputProcessing(table, cloneDeep(row), {
|
let enrichedRow = await outputProcessing(table, cloneDeep(row), {
|
||||||
|
@ -131,7 +136,7 @@ exports.finaliseRow = async (
|
||||||
if (oldTable && !isEqual(oldTable, table)) {
|
if (oldTable && !isEqual(oldTable, table)) {
|
||||||
try {
|
try {
|
||||||
await db.put(table)
|
await db.put(table)
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
if (err.status === 409) {
|
if (err.status === 409) {
|
||||||
const updatedTable = await db.get(table._id)
|
const updatedTable = await db.get(table._id)
|
||||||
let response = processAutoColumn(null, updatedTable, row, {
|
let response = processAutoColumn(null, updatedTable, row, {
|
|
@ -1,33 +1,32 @@
|
||||||
|
import { InternalTables } from "../../../db/utils"
|
||||||
|
import * as userController from "../user"
|
||||||
|
import { FieldTypes } from "../../../constants"
|
||||||
|
import { context } from "@budibase/backend-core"
|
||||||
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
||||||
|
import { BBContext, Row, Table } from "@budibase/types"
|
||||||
|
export { removeKeyNumbering } from "../../../integrations/base/utils"
|
||||||
const validateJs = require("validate.js")
|
const validateJs = require("validate.js")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const { InternalTables } = require("../../../db/utils")
|
|
||||||
const userController = require("../user")
|
|
||||||
const { FieldTypes } = require("../../../constants")
|
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
|
||||||
const { makeExternalQuery } = require("../../../integrations/base/query")
|
|
||||||
const { removeKeyNumbering } = require("../../../integrations/base/utils")
|
|
||||||
|
|
||||||
validateJs.extend(validateJs.validators.datetime, {
|
validateJs.extend(validateJs.validators.datetime, {
|
||||||
parse: function (value) {
|
parse: function (value: string) {
|
||||||
return new Date(value).getTime()
|
return new Date(value).getTime()
|
||||||
},
|
},
|
||||||
// Input is a unix timestamp
|
// Input is a unix timestamp
|
||||||
format: function (value) {
|
format: function (value: string) {
|
||||||
return new Date(value).toISOString()
|
return new Date(value).toISOString()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
exports.removeKeyNumbering = removeKeyNumbering
|
export async function getDatasourceAndQuery(json: any) {
|
||||||
|
|
||||||
exports.getDatasourceAndQuery = async json => {
|
|
||||||
const datasourceId = json.endpoint.datasourceId
|
const datasourceId = json.endpoint.datasourceId
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
return makeExternalQuery(datasource, json)
|
return makeExternalQuery(datasource, json)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.findRow = async (ctx, tableId, rowId) => {
|
export async function findRow(ctx: BBContext, tableId: string, rowId: string) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let row
|
let row
|
||||||
// TODO remove special user case in future
|
// TODO remove special user case in future
|
||||||
if (tableId === InternalTables.USER_METADATA) {
|
if (tableId === InternalTables.USER_METADATA) {
|
||||||
|
@ -45,12 +44,20 @@ exports.findRow = async (ctx, tableId, rowId) => {
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.validate = async ({ tableId, row, table }) => {
|
export async function validate({
|
||||||
|
tableId,
|
||||||
|
row,
|
||||||
|
table,
|
||||||
|
}: {
|
||||||
|
tableId?: string
|
||||||
|
row: Row
|
||||||
|
table: Table
|
||||||
|
}) {
|
||||||
if (!table) {
|
if (!table) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
table = await db.get(tableId)
|
table = await db.get(tableId)
|
||||||
}
|
}
|
||||||
const errors = {}
|
const errors: any = {}
|
||||||
for (let fieldName of Object.keys(table.schema)) {
|
for (let fieldName of Object.keys(table.schema)) {
|
||||||
const constraints = cloneDeep(table.schema[fieldName].constraints)
|
const constraints = cloneDeep(table.schema[fieldName].constraints)
|
||||||
const type = table.schema[fieldName].type
|
const type = table.schema[fieldName].type
|
||||||
|
@ -70,7 +77,7 @@ exports.validate = async ({ tableId, row, table }) => {
|
||||||
if (!Array.isArray(row[fieldName])) {
|
if (!Array.isArray(row[fieldName])) {
|
||||||
row[fieldName] = row[fieldName].split(",")
|
row[fieldName] = row[fieldName].split(",")
|
||||||
}
|
}
|
||||||
row[fieldName].map(val => {
|
row[fieldName].map((val: any) => {
|
||||||
if (
|
if (
|
||||||
!constraints.inclusion.includes(val) &&
|
!constraints.inclusion.includes(val) &&
|
||||||
constraints.inclusion.length !== 0
|
constraints.inclusion.length !== 0
|
|
@ -7,9 +7,9 @@ import {
|
||||||
roles,
|
roles,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { updateAppPackage } from "./application"
|
import { updateAppPackage } from "./application"
|
||||||
import { Plugin, ScreenProps } from "@budibase/types"
|
import { Plugin, ScreenProps, BBContext } from "@budibase/types"
|
||||||
|
|
||||||
exports.fetch = async (ctx: any) => {
|
export async function fetch(ctx: BBContext) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
|
|
||||||
const screens = (
|
const screens = (
|
||||||
|
@ -20,13 +20,17 @@ exports.fetch = async (ctx: any) => {
|
||||||
)
|
)
|
||||||
).rows.map((el: any) => el.doc)
|
).rows.map((el: any) => el.doc)
|
||||||
|
|
||||||
|
const roleId = ctx.user?.role?._id as string
|
||||||
|
if (!roleId) {
|
||||||
|
ctx.throw("Unable to retrieve users role ID.")
|
||||||
|
}
|
||||||
ctx.body = await new roles.AccessController().checkScreensAccess(
|
ctx.body = await new roles.AccessController().checkScreensAccess(
|
||||||
screens,
|
screens,
|
||||||
ctx.user.role._id
|
roleId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async (ctx: any) => {
|
export async function save(ctx: BBContext) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let screen = ctx.request.body
|
let screen = ctx.request.body
|
||||||
|
|
||||||
|
@ -92,7 +96,7 @@ exports.save = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async (ctx: any) => {
|
export async function destroy(ctx: BBContext) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const id = ctx.params.screenId
|
const id = ctx.params.screenId
|
||||||
const screen = await db.get(id)
|
const screen = await db.get(id)
|
||||||
|
@ -106,7 +110,7 @@ exports.destroy = async (ctx: any) => {
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
const findPlugins = (component: ScreenProps, foundPlugins: string[]) => {
|
function findPlugins(component: ScreenProps, foundPlugins: string[]) {
|
||||||
if (!component) {
|
if (!component) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
const ScriptRunner = require("../../utilities/scriptRunner")
|
|
||||||
|
|
||||||
exports.execute = async function (ctx) {
|
|
||||||
const { script, context } = ctx.request.body
|
|
||||||
const runner = new ScriptRunner(script, context)
|
|
||||||
ctx.body = runner.execute()
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
|
||||||
ctx.throw(501, "Not currently implemented")
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import ScriptRunner from "../../utilities/scriptRunner"
|
||||||
|
import { BBContext } from "@budibase/types"
|
||||||
|
|
||||||
|
export async function execute(ctx: BBContext) {
|
||||||
|
const { script, context } = ctx.request.body
|
||||||
|
const runner = new ScriptRunner(script, context)
|
||||||
|
ctx.body = runner.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function save(ctx: BBContext) {
|
||||||
|
ctx.throw(501, "Not currently implemented")
|
||||||
|
}
|
|
@ -17,13 +17,9 @@ const { clientLibraryPath } = require("../../../utilities")
|
||||||
const { upload, deleteFiles } = require("../../../utilities/fileSystem")
|
const { upload, deleteFiles } = require("../../../utilities/fileSystem")
|
||||||
const { attachmentsRelativeURL } = require("../../../utilities")
|
const { attachmentsRelativeURL } = require("../../../utilities")
|
||||||
const { DocumentType } = require("../../../db/utils")
|
const { DocumentType } = require("../../../db/utils")
|
||||||
const { getAppDB, getAppId } = require("@budibase/backend-core/context")
|
const { context, objectStore, utils } = require("@budibase/backend-core")
|
||||||
const { setCookie, clearCookie } = require("@budibase/backend-core/utils")
|
|
||||||
const AWS = require("aws-sdk")
|
const AWS = require("aws-sdk")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const {
|
|
||||||
downloadTarballDirect,
|
|
||||||
} = require("../../../utilities/fileSystem/utilities")
|
|
||||||
|
|
||||||
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
||||||
const response = await upload({
|
const response = await upload({
|
||||||
|
@ -48,7 +44,7 @@ export const toggleBetaUiFeature = async function (ctx: any) {
|
||||||
const cookieName = `beta:${ctx.params.feature}`
|
const cookieName = `beta:${ctx.params.feature}`
|
||||||
|
|
||||||
if (ctx.cookies.get(cookieName)) {
|
if (ctx.cookies.get(cookieName)) {
|
||||||
clearCookie(ctx, cookieName)
|
utils.clearCookie(ctx, cookieName)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: `${ctx.params.feature} disabled`,
|
message: `${ctx.params.feature} disabled`,
|
||||||
}
|
}
|
||||||
|
@ -61,11 +57,11 @@ export const toggleBetaUiFeature = async function (ctx: any) {
|
||||||
if (!fs.existsSync(builderPath)) {
|
if (!fs.existsSync(builderPath)) {
|
||||||
fs.mkdirSync(builderPath)
|
fs.mkdirSync(builderPath)
|
||||||
}
|
}
|
||||||
await downloadTarballDirect(
|
await objectStore.downloadTarballDirect(
|
||||||
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
|
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
|
||||||
builderPath
|
builderPath
|
||||||
)
|
)
|
||||||
setCookie(ctx, {}, cookieName)
|
utils.setCookie(ctx, {}, cookieName)
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: `${ctx.params.feature} enabled`,
|
message: `${ctx.params.feature} enabled`,
|
||||||
|
@ -103,9 +99,9 @@ export const deleteObjects = async function (ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serveApp = async function (ctx: any) {
|
export const serveApp = async function (ctx: any) {
|
||||||
const db = getAppDB({ skip_setup: true })
|
const db = context.getAppDB({ skip_setup: true })
|
||||||
const appInfo = await db.get(DocumentType.APP_METADATA)
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
||||||
let appId = getAppId()
|
let appId = context.getAppId()
|
||||||
|
|
||||||
if (!env.isJest()) {
|
if (!env.isJest()) {
|
||||||
const App = require("./templates/BudibaseApp.svelte").default
|
const App = require("./templates/BudibaseApp.svelte").default
|
||||||
|
@ -134,11 +130,11 @@ export const serveApp = async function (ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serveBuilderPreview = async function (ctx: any) {
|
export const serveBuilderPreview = async function (ctx: any) {
|
||||||
const db = getAppDB({ skip_setup: true })
|
const db = context.getAppDB({ skip_setup: true })
|
||||||
const appInfo = await db.get(DocumentType.APP_METADATA)
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
||||||
|
|
||||||
if (!env.isJest()) {
|
if (!env.isJest()) {
|
||||||
let appId = getAppId()
|
let appId = context.getAppId()
|
||||||
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
|
const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
|
||||||
ctx.body = await processString(previewHbs, {
|
ctx.body = await processString(previewHbs, {
|
||||||
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
|
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
|
||||||
|
@ -156,7 +152,7 @@ export const serveClientLibrary = async function (ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSignedUploadURL = async function (ctx: any) {
|
export const getSignedUploadURL = async function (ctx: any) {
|
||||||
const database = getAppDB()
|
const database = context.getAppDB()
|
||||||
|
|
||||||
// Ensure datasource is valid
|
// Ensure datasource is valid
|
||||||
let datasource
|
let datasource
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
const { FieldTypes, FormulaTypes } = require("../../../constants")
|
import { FieldTypes, FormulaTypes } from "../../../constants"
|
||||||
const { clearColumns } = require("./utils")
|
import { clearColumns } from "./utils"
|
||||||
const { doesContainStrings } = require("@budibase/string-templates")
|
import { doesContainStrings } from "@budibase/string-templates"
|
||||||
const { cloneDeep } = require("lodash/fp")
|
import { cloneDeep } from "lodash/fp"
|
||||||
const { isEqual, uniq } = require("lodash")
|
import { isEqual, uniq } from "lodash"
|
||||||
const { updateAllFormulasInTable } = require("../row/staticFormula")
|
import { updateAllFormulasInTable } from "../row/staticFormula"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { context } from "@budibase/backend-core"
|
||||||
const sdk = require("../../../sdk")
|
import { FieldSchema, Table } from "@budibase/types"
|
||||||
|
import sdk from "../../../sdk"
|
||||||
|
|
||||||
function isStaticFormula(column) {
|
function isStaticFormula(column: FieldSchema) {
|
||||||
return (
|
return (
|
||||||
column.type === FieldTypes.FORMULA &&
|
column.type === FieldTypes.FORMULA &&
|
||||||
column.formulaType === FormulaTypes.STATIC
|
column.formulaType === FormulaTypes.STATIC
|
||||||
|
@ -18,8 +19,8 @@ function isStaticFormula(column) {
|
||||||
* This retrieves the formula columns from a table schema that use a specified column name
|
* This retrieves the formula columns from a table schema that use a specified column name
|
||||||
* in the formula.
|
* in the formula.
|
||||||
*/
|
*/
|
||||||
function getFormulaThatUseColumn(table, columnNames) {
|
function getFormulaThatUseColumn(table: Table, columnNames: string[] | string) {
|
||||||
let formula = []
|
let formula: string[] = []
|
||||||
columnNames = Array.isArray(columnNames) ? columnNames : [columnNames]
|
columnNames = Array.isArray(columnNames) ? columnNames : [columnNames]
|
||||||
for (let column of Object.values(table.schema)) {
|
for (let column of Object.values(table.schema)) {
|
||||||
// not a static formula, or doesn't contain a relationship
|
// not a static formula, or doesn't contain a relationship
|
||||||
|
@ -38,7 +39,10 @@ function getFormulaThatUseColumn(table, columnNames) {
|
||||||
* This functions checks for when a related table, column or related column is deleted, if any
|
* This functions checks for when a related table, column or related column is deleted, if any
|
||||||
* tables need to have the formula column removed.
|
* tables need to have the formula column removed.
|
||||||
*/
|
*/
|
||||||
async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
|
async function checkIfFormulaNeedsCleared(
|
||||||
|
table: Table,
|
||||||
|
{ oldTable, deletion }: { oldTable?: Table; deletion?: boolean }
|
||||||
|
) {
|
||||||
// start by retrieving all tables, remove the current table from the list
|
// start by retrieving all tables, remove the current table from the list
|
||||||
const tables = (await sdk.tables.getAllInternalTables()).filter(
|
const tables = (await sdk.tables.getAllInternalTables()).filter(
|
||||||
tbl => tbl._id !== table._id
|
tbl => tbl._id !== table._id
|
||||||
|
@ -49,11 +53,14 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
|
||||||
)
|
)
|
||||||
// remove any formula columns that used related columns
|
// remove any formula columns that used related columns
|
||||||
for (let removed of removedColumns) {
|
for (let removed of removedColumns) {
|
||||||
let tableToUse = table
|
let tableToUse: Table | undefined = table
|
||||||
// if relationship, get the related table
|
// if relationship, get the related table
|
||||||
if (removed.type === FieldTypes.LINK) {
|
if (removed.type === FieldTypes.LINK) {
|
||||||
tableToUse = tables.find(table => table._id === removed.tableId)
|
tableToUse = tables.find(table => table._id === removed.tableId)
|
||||||
}
|
}
|
||||||
|
if (!tableToUse) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
const columnsToDelete = getFormulaThatUseColumn(tableToUse, removed.name)
|
const columnsToDelete = getFormulaThatUseColumn(tableToUse, removed.name)
|
||||||
if (columnsToDelete.length > 0) {
|
if (columnsToDelete.length > 0) {
|
||||||
await clearColumns(table, columnsToDelete)
|
await clearColumns(table, columnsToDelete)
|
||||||
|
@ -71,11 +78,11 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
|
||||||
// look to see if the column was used in a relationship formula,
|
// look to see if the column was used in a relationship formula,
|
||||||
// relationships won't be used for this
|
// relationships won't be used for this
|
||||||
if (relatedTable && relatedColumns && removed.type !== FieldTypes.LINK) {
|
if (relatedTable && relatedColumns && removed.type !== FieldTypes.LINK) {
|
||||||
let relatedFormulaToRemove = []
|
let relatedFormulaToRemove: string[] = []
|
||||||
for (let column of relatedColumns) {
|
for (let column of relatedColumns) {
|
||||||
relatedFormulaToRemove = relatedFormulaToRemove.concat(
|
relatedFormulaToRemove = relatedFormulaToRemove.concat(
|
||||||
getFormulaThatUseColumn(relatedTable, [
|
getFormulaThatUseColumn(relatedTable, [
|
||||||
column.fieldName,
|
column.fieldName!,
|
||||||
removed.name,
|
removed.name,
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
@ -95,13 +102,14 @@ async function checkIfFormulaNeedsCleared(table, { oldTable, deletion }) {
|
||||||
* specifically only for static formula.
|
* specifically only for static formula.
|
||||||
*/
|
*/
|
||||||
async function updateRelatedFormulaLinksOnTables(
|
async function updateRelatedFormulaLinksOnTables(
|
||||||
table,
|
table: Table,
|
||||||
{ deletion } = { deletion: false }
|
{ deletion }: { deletion?: boolean } = {}
|
||||||
) {
|
) {
|
||||||
const db = getAppDB()
|
const tableId: string = table._id!
|
||||||
|
const db = context.getAppDB()
|
||||||
// start by retrieving all tables, remove the current table from the list
|
// start by retrieving all tables, remove the current table from the list
|
||||||
const tables = (await sdk.tables.getAllInternalTables()).filter(
|
const tables = (await sdk.tables.getAllInternalTables()).filter(
|
||||||
tbl => tbl._id !== table._id
|
tbl => tbl._id !== tableId
|
||||||
)
|
)
|
||||||
// clone the tables, so we can compare at end
|
// clone the tables, so we can compare at end
|
||||||
const initialTables = cloneDeep(tables)
|
const initialTables = cloneDeep(tables)
|
||||||
|
@ -114,7 +122,7 @@ async function updateRelatedFormulaLinksOnTables(
|
||||||
if (!otherTable.relatedFormula) {
|
if (!otherTable.relatedFormula) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const index = otherTable.relatedFormula.indexOf(table._id)
|
const index = otherTable.relatedFormula.indexOf(tableId)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
otherTable.relatedFormula.splice(index, 1)
|
otherTable.relatedFormula.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
@ -133,11 +141,11 @@ async function updateRelatedFormulaLinksOnTables(
|
||||||
if (
|
if (
|
||||||
relatedTable &&
|
relatedTable &&
|
||||||
(!relatedTable.relatedFormula ||
|
(!relatedTable.relatedFormula ||
|
||||||
!relatedTable.relatedFormula.includes(table._id))
|
!relatedTable.relatedFormula.includes(tableId))
|
||||||
) {
|
) {
|
||||||
relatedTable.relatedFormula = relatedTable.relatedFormula
|
relatedTable.relatedFormula = relatedTable.relatedFormula
|
||||||
? [...relatedTable.relatedFormula, table._id]
|
? [...relatedTable.relatedFormula, tableId]
|
||||||
: [table._id]
|
: [tableId]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +158,10 @@ async function updateRelatedFormulaLinksOnTables(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkIfFormulaUpdated(table, { oldTable }) {
|
async function checkIfFormulaUpdated(
|
||||||
|
table: Table,
|
||||||
|
{ oldTable }: { oldTable?: Table }
|
||||||
|
) {
|
||||||
// look to see if any formula values have changed
|
// look to see if any formula values have changed
|
||||||
const shouldUpdate = Object.values(table.schema).find(
|
const shouldUpdate = Object.values(table.schema).find(
|
||||||
column =>
|
column =>
|
||||||
|
@ -165,7 +176,10 @@ async function checkIfFormulaUpdated(table, { oldTable }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.runStaticFormulaChecks = async (table, { oldTable, deletion }) => {
|
export async function runStaticFormulaChecks(
|
||||||
|
table: Table,
|
||||||
|
{ oldTable, deletion }: { oldTable?: Table; deletion?: boolean }
|
||||||
|
) {
|
||||||
await updateRelatedFormulaLinksOnTables(table, { deletion })
|
await updateRelatedFormulaLinksOnTables(table, { deletion })
|
||||||
await checkIfFormulaNeedsCleared(table, { oldTable, deletion })
|
await checkIfFormulaNeedsCleared(table, { oldTable, deletion })
|
||||||
if (!deletion) {
|
if (!deletion) {
|
|
@ -1,38 +1,47 @@
|
||||||
const {
|
import {
|
||||||
buildExternalTableId,
|
buildExternalTableId,
|
||||||
breakExternalTableId,
|
breakExternalTableId,
|
||||||
} = require("../../../integrations/utils")
|
} from "../../../integrations/utils"
|
||||||
const {
|
import {
|
||||||
generateForeignKey,
|
generateForeignKey,
|
||||||
generateJunctionTableName,
|
generateJunctionTableName,
|
||||||
foreignKeyStructure,
|
foreignKeyStructure,
|
||||||
hasTypeChanged,
|
hasTypeChanged,
|
||||||
} = require("./utils")
|
} from "./utils"
|
||||||
const {
|
import {
|
||||||
DataSourceOperation,
|
DataSourceOperation,
|
||||||
FieldTypes,
|
FieldTypes,
|
||||||
RelationshipTypes,
|
RelationshipTypes,
|
||||||
} = require("../../../constants")
|
} from "../../../constants"
|
||||||
const { makeExternalQuery } = require("../../../integrations/base/query")
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
||||||
|
import csvParser from "../../../utilities/csvParser"
|
||||||
|
import { handleRequest } from "../row/external"
|
||||||
|
import { events, context } from "@budibase/backend-core"
|
||||||
|
import {
|
||||||
|
Datasource,
|
||||||
|
Table,
|
||||||
|
QueryJson,
|
||||||
|
Operation,
|
||||||
|
RenameColumn,
|
||||||
|
FieldSchema,
|
||||||
|
BBContext,
|
||||||
|
TableRequest,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import sdk from "../../../sdk"
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const csvParser = require("../../../utilities/csvParser")
|
|
||||||
const { handleRequest } = require("../row/external")
|
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
|
||||||
const { events } = require("@budibase/backend-core")
|
|
||||||
const sdk = require("../../../sdk")
|
|
||||||
|
|
||||||
async function makeTableRequest(
|
async function makeTableRequest(
|
||||||
datasource,
|
datasource: Datasource,
|
||||||
operation,
|
operation: Operation,
|
||||||
table,
|
table: Table,
|
||||||
tables,
|
tables: Record<string, Table>,
|
||||||
oldTable = null,
|
oldTable?: Table,
|
||||||
renamed = null
|
renamed?: RenameColumn
|
||||||
) {
|
) {
|
||||||
const json = {
|
const json: QueryJson = {
|
||||||
endpoint: {
|
endpoint: {
|
||||||
datasourceId: datasource._id,
|
datasourceId: datasource._id!,
|
||||||
entityId: table._id,
|
entityId: table._id!,
|
||||||
operation,
|
operation,
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
|
@ -41,15 +50,19 @@ async function makeTableRequest(
|
||||||
table,
|
table,
|
||||||
}
|
}
|
||||||
if (oldTable) {
|
if (oldTable) {
|
||||||
json.meta.table = oldTable
|
json.meta!.table = oldTable
|
||||||
}
|
}
|
||||||
if (renamed) {
|
if (renamed) {
|
||||||
json.meta.renamed = renamed
|
json.meta!.renamed = renamed
|
||||||
}
|
}
|
||||||
return makeExternalQuery(datasource, json)
|
return makeExternalQuery(datasource, json)
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupRelationships(table, tables, oldTable = null) {
|
function cleanupRelationships(
|
||||||
|
table: Table,
|
||||||
|
tables: Record<string, Table>,
|
||||||
|
oldTable?: Table
|
||||||
|
) {
|
||||||
const tableToIterate = oldTable ? oldTable : table
|
const tableToIterate = oldTable ? oldTable : table
|
||||||
// clean up relationships in couch table schemas
|
// clean up relationships in couch table schemas
|
||||||
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
for (let [key, schema] of Object.entries(tableToIterate.schema)) {
|
||||||
|
@ -78,7 +91,7 @@ function cleanupRelationships(table, tables, oldTable = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDatasourceId(table) {
|
function getDatasourceId(table: Table) {
|
||||||
if (!table) {
|
if (!table) {
|
||||||
throw "No table supplied"
|
throw "No table supplied"
|
||||||
}
|
}
|
||||||
|
@ -88,7 +101,7 @@ function getDatasourceId(table) {
|
||||||
return breakExternalTableId(table._id).datasourceId
|
return breakExternalTableId(table._id).datasourceId
|
||||||
}
|
}
|
||||||
|
|
||||||
function otherRelationshipType(type) {
|
function otherRelationshipType(type?: string) {
|
||||||
if (type === RelationshipTypes.MANY_TO_MANY) {
|
if (type === RelationshipTypes.MANY_TO_MANY) {
|
||||||
return RelationshipTypes.MANY_TO_MANY
|
return RelationshipTypes.MANY_TO_MANY
|
||||||
}
|
}
|
||||||
|
@ -97,13 +110,21 @@ function otherRelationshipType(type) {
|
||||||
: RelationshipTypes.ONE_TO_MANY
|
: RelationshipTypes.ONE_TO_MANY
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateManyLinkSchema(datasource, column, table, relatedTable) {
|
function generateManyLinkSchema(
|
||||||
|
datasource: Datasource,
|
||||||
|
column: FieldSchema,
|
||||||
|
table: Table,
|
||||||
|
relatedTable: Table
|
||||||
|
): Table {
|
||||||
|
if (!table.primary || !relatedTable.primary) {
|
||||||
|
throw new Error("Unable to generate many link schema, no primary keys")
|
||||||
|
}
|
||||||
const primary = table.name + table.primary[0]
|
const primary = table.name + table.primary[0]
|
||||||
const relatedPrimary = relatedTable.name + relatedTable.primary[0]
|
const relatedPrimary = relatedTable.name + relatedTable.primary[0]
|
||||||
const jcTblName = generateJunctionTableName(column, table, relatedTable)
|
const jcTblName = generateJunctionTableName(column, table, relatedTable)
|
||||||
// first create the new table
|
// first create the new table
|
||||||
const junctionTable = {
|
const junctionTable = {
|
||||||
_id: buildExternalTableId(datasource._id, jcTblName),
|
_id: buildExternalTableId(datasource._id!, jcTblName),
|
||||||
name: jcTblName,
|
name: jcTblName,
|
||||||
primary: [primary, relatedPrimary],
|
primary: [primary, relatedPrimary],
|
||||||
constrained: [primary, relatedPrimary],
|
constrained: [primary, relatedPrimary],
|
||||||
|
@ -125,7 +146,15 @@ function generateManyLinkSchema(datasource, column, table, relatedTable) {
|
||||||
return junctionTable
|
return junctionTable
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateLinkSchema(column, table, relatedTable, type) {
|
function generateLinkSchema(
|
||||||
|
column: FieldSchema,
|
||||||
|
table: Table,
|
||||||
|
relatedTable: Table,
|
||||||
|
type: string
|
||||||
|
) {
|
||||||
|
if (!table.primary || !relatedTable.primary) {
|
||||||
|
throw new Error("Unable to generate link schema, no primary keys")
|
||||||
|
}
|
||||||
const isOneSide = type === RelationshipTypes.ONE_TO_MANY
|
const isOneSide = type === RelationshipTypes.ONE_TO_MANY
|
||||||
const primary = isOneSide ? relatedTable.primary[0] : table.primary[0]
|
const primary = isOneSide ? relatedTable.primary[0] : table.primary[0]
|
||||||
// generate a foreign key
|
// generate a foreign key
|
||||||
|
@ -136,7 +165,12 @@ function generateLinkSchema(column, table, relatedTable, type) {
|
||||||
return foreignKey
|
return foreignKey
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateRelatedSchema(linkColumn, table, relatedTable, columnName) {
|
function generateRelatedSchema(
|
||||||
|
linkColumn: FieldSchema,
|
||||||
|
table: Table,
|
||||||
|
relatedTable: Table,
|
||||||
|
columnName: string
|
||||||
|
) {
|
||||||
// generate column for other table
|
// generate column for other table
|
||||||
const relatedSchema = cloneDeep(linkColumn)
|
const relatedSchema = cloneDeep(linkColumn)
|
||||||
// swap them from the main link
|
// swap them from the main link
|
||||||
|
@ -159,21 +193,21 @@ function generateRelatedSchema(linkColumn, table, relatedTable, columnName) {
|
||||||
table.schema[columnName] = relatedSchema
|
table.schema[columnName] = relatedSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRelationshipSetup(column) {
|
function isRelationshipSetup(column: FieldSchema) {
|
||||||
return column.foreignKey || column.through
|
return column.foreignKey || column.through
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
export async function save(ctx: BBContext) {
|
||||||
const table = ctx.request.body
|
const table: TableRequest = ctx.request.body
|
||||||
const { _rename: renamed } = table
|
const renamed = table?._rename
|
||||||
// can't do this right now
|
// can't do this right now
|
||||||
delete table.dataImport
|
delete table.dataImport
|
||||||
const datasourceId = getDatasourceId(ctx.request.body)
|
const datasourceId = getDatasourceId(ctx.request.body)!
|
||||||
// table doesn't exist already, note that it is created
|
// table doesn't exist already, note that it is created
|
||||||
if (!table._id) {
|
if (!table._id) {
|
||||||
table.created = true
|
table.created = true
|
||||||
}
|
}
|
||||||
let tableToSave = {
|
let tableToSave: TableRequest = {
|
||||||
type: "table",
|
type: "table",
|
||||||
_id: buildExternalTableId(datasourceId, table.name),
|
_id: buildExternalTableId(datasourceId, table.name),
|
||||||
...table,
|
...table,
|
||||||
|
@ -188,10 +222,10 @@ exports.save = async function (ctx) {
|
||||||
ctx.throw(400, "A column type has changed.")
|
ctx.throw(400, "A column type has changed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
const oldTables = cloneDeep(datasource.entities)
|
const oldTables = cloneDeep(datasource.entities)
|
||||||
const tables = datasource.entities
|
const tables: Record<string, Table> = datasource.entities
|
||||||
|
|
||||||
const extraTablesToUpdate = []
|
const extraTablesToUpdate = []
|
||||||
|
|
||||||
|
@ -203,8 +237,11 @@ exports.save = async function (ctx) {
|
||||||
const relatedTable = Object.values(tables).find(
|
const relatedTable = Object.values(tables).find(
|
||||||
table => table._id === schema.tableId
|
table => table._id === schema.tableId
|
||||||
)
|
)
|
||||||
const relatedColumnName = schema.fieldName
|
if (!relatedTable) {
|
||||||
const relationType = schema.relationshipType
|
continue
|
||||||
|
}
|
||||||
|
const relatedColumnName = schema.fieldName!
|
||||||
|
const relationType = schema.relationshipType!
|
||||||
if (relationType === RelationshipTypes.MANY_TO_MANY) {
|
if (relationType === RelationshipTypes.MANY_TO_MANY) {
|
||||||
const junctionTable = generateManyLinkSchema(
|
const junctionTable = generateManyLinkSchema(
|
||||||
datasource,
|
datasource,
|
||||||
|
@ -244,9 +281,7 @@ exports.save = async function (ctx) {
|
||||||
|
|
||||||
cleanupRelationships(tableToSave, tables, oldTable)
|
cleanupRelationships(tableToSave, tables, oldTable)
|
||||||
|
|
||||||
const operation = oldTable
|
const operation = oldTable ? Operation.UPDATE_TABLE : Operation.CREATE_TABLE
|
||||||
? DataSourceOperation.UPDATE_TABLE
|
|
||||||
: DataSourceOperation.CREATE_TABLE
|
|
||||||
await makeTableRequest(
|
await makeTableRequest(
|
||||||
datasource,
|
datasource,
|
||||||
operation,
|
operation,
|
||||||
|
@ -258,9 +293,7 @@ exports.save = async function (ctx) {
|
||||||
// update any extra tables (like foreign keys in other tables)
|
// update any extra tables (like foreign keys in other tables)
|
||||||
for (let extraTable of extraTablesToUpdate) {
|
for (let extraTable of extraTablesToUpdate) {
|
||||||
const oldExtraTable = oldTables[extraTable.name]
|
const oldExtraTable = oldTables[extraTable.name]
|
||||||
let op = oldExtraTable
|
let op = oldExtraTable ? Operation.UPDATE_TABLE : Operation.CREATE_TABLE
|
||||||
? DataSourceOperation.UPDATE_TABLE
|
|
||||||
: DataSourceOperation.CREATE_TABLE
|
|
||||||
await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable)
|
await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,18 +313,20 @@ exports.save = async function (ctx) {
|
||||||
return tableToSave
|
return tableToSave
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
export async function destroy(ctx: BBContext) {
|
||||||
const tableToDelete = await sdk.tables.getTable(ctx.params.tableId)
|
const tableToDelete: TableRequest = await sdk.tables.getTable(
|
||||||
|
ctx.params.tableId
|
||||||
|
)
|
||||||
if (!tableToDelete || !tableToDelete.created) {
|
if (!tableToDelete || !tableToDelete.created) {
|
||||||
ctx.throw(400, "Cannot delete tables which weren't created in Budibase.")
|
ctx.throw(400, "Cannot delete tables which weren't created in Budibase.")
|
||||||
}
|
}
|
||||||
const datasourceId = getDatasourceId(tableToDelete)
|
const datasourceId = getDatasourceId(tableToDelete)
|
||||||
|
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
const tables = datasource.entities
|
const tables = datasource.entities
|
||||||
|
|
||||||
const operation = DataSourceOperation.DELETE_TABLE
|
const operation = Operation.DELETE_TABLE
|
||||||
await makeTableRequest(datasource, operation, tableToDelete, tables)
|
await makeTableRequest(datasource, operation, tableToDelete, tables)
|
||||||
|
|
||||||
cleanupRelationships(tableToDelete, tables)
|
cleanupRelationships(tableToDelete, tables)
|
||||||
|
@ -302,7 +337,7 @@ exports.destroy = async function (ctx) {
|
||||||
return tableToDelete
|
return tableToDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.bulkImport = async function (ctx) {
|
export async function bulkImport(ctx: BBContext) {
|
||||||
const table = await sdk.tables.getTable(ctx.params.tableId)
|
const table = await sdk.tables.getTable(ctx.params.tableId)
|
||||||
const { dataImport } = ctx.request.body
|
const { dataImport } = ctx.request.body
|
||||||
if (!dataImport || !dataImport.schema || !dataImport.csvString) {
|
if (!dataImport || !dataImport.schema || !dataImport.csvString) {
|
|
@ -1,13 +1,13 @@
|
||||||
const internal = require("./internal")
|
import * as internal from "./internal"
|
||||||
const external = require("./external")
|
import * as external from "./external"
|
||||||
const csvParser = require("../../../utilities/csvParser")
|
import csvParser from "../../../utilities/csvParser"
|
||||||
const { isExternalTable, isSQL } = require("../../../integrations/utils")
|
import { isExternalTable, isSQL } from "../../../integrations/utils"
|
||||||
const { getDatasourceParams } = require("../../../db/utils")
|
import { getDatasourceParams } from "../../../db/utils"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { context, events } from "@budibase/backend-core"
|
||||||
const { events } = require("@budibase/backend-core")
|
import { Table, BBContext } from "@budibase/types"
|
||||||
const sdk = require("../../../sdk")
|
import sdk from "../../../sdk"
|
||||||
|
|
||||||
function pickApi({ tableId, table }) {
|
function pickApi({ tableId, table }: { tableId?: string; table?: Table }) {
|
||||||
if (table && !tableId) {
|
if (table && !tableId) {
|
||||||
tableId = table._id
|
tableId = table._id
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ function pickApi({ tableId, table }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// covers both internal and external
|
// covers both internal and external
|
||||||
exports.fetch = async function (ctx) {
|
export async function fetch(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
|
|
||||||
const internal = await sdk.tables.getAllInternalTables()
|
const internal = await sdk.tables.getAllInternalTables()
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ exports.fetch = async function (ctx) {
|
||||||
const external = externalTables.rows.flatMap(tableDoc => {
|
const external = externalTables.rows.flatMap(tableDoc => {
|
||||||
let entities = tableDoc.doc.entities
|
let entities = tableDoc.doc.entities
|
||||||
if (entities) {
|
if (entities) {
|
||||||
return Object.values(entities).map(entity => ({
|
return Object.values(entities).map((entity: any) => ({
|
||||||
...entity,
|
...entity,
|
||||||
type: "external",
|
type: "external",
|
||||||
sourceId: tableDoc.doc._id,
|
sourceId: tableDoc.doc._id,
|
||||||
|
@ -48,12 +48,12 @@ exports.fetch = async function (ctx) {
|
||||||
ctx.body = [...internal, ...external]
|
ctx.body = [...internal, ...external]
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
export async function find(ctx: BBContext) {
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
ctx.body = await sdk.tables.getTable(tableId)
|
ctx.body = await sdk.tables.getTable(tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
export async function save(ctx: BBContext) {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const table = ctx.request.body
|
const table = ctx.request.body
|
||||||
const importFormat =
|
const importFormat =
|
||||||
|
@ -74,7 +74,7 @@ exports.save = async function (ctx) {
|
||||||
ctx.body = savedTable
|
ctx.body = savedTable
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
export async function destroy(ctx: BBContext) {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
const deletedTable = await pickApi({ tableId }).destroy(ctx)
|
const deletedTable = await pickApi({ tableId }).destroy(ctx)
|
||||||
|
@ -86,7 +86,7 @@ exports.destroy = async function (ctx) {
|
||||||
ctx.body = { message: `Table ${tableId} deleted.` }
|
ctx.body = { message: `Table ${tableId} deleted.` }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.bulkImport = async function (ctx) {
|
export async function bulkImport(ctx: BBContext) {
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
await pickApi({ tableId }).bulkImport(ctx)
|
await pickApi({ tableId }).bulkImport(ctx)
|
||||||
// right now we don't trigger anything for bulk import because it
|
// right now we don't trigger anything for bulk import because it
|
||||||
|
@ -96,7 +96,7 @@ exports.bulkImport = async function (ctx) {
|
||||||
ctx.body = { message: `Bulk rows created.` }
|
ctx.body = { message: `Bulk rows created.` }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.validateCSVSchema = async function (ctx) {
|
export async function validateCSVSchema(ctx: BBContext) {
|
||||||
// tableId being specified means its an import to an existing table
|
// tableId being specified means its an import to an existing table
|
||||||
const { csvString, schema = {}, tableId } = ctx.request.body
|
const { csvString, schema = {}, tableId } = ctx.request.body
|
||||||
let existingTable
|
let existingTable
|
|
@ -133,7 +133,7 @@ export async function save(ctx: any) {
|
||||||
tableToSave._rev = result.rev
|
tableToSave._rev = result.rev
|
||||||
}
|
}
|
||||||
// has to run after, make sure it has _id
|
// has to run after, make sure it has _id
|
||||||
await runStaticFormulaChecks(tableToSave, { oldTable, deletion: null })
|
await runStaticFormulaChecks(tableToSave, { oldTable, deletion: false })
|
||||||
return tableToSave
|
return tableToSave
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +176,6 @@ export async function destroy(ctx: any) {
|
||||||
|
|
||||||
// has to run after, make sure it has _id
|
// has to run after, make sure it has _id
|
||||||
await runStaticFormulaChecks(tableToDelete, {
|
await runStaticFormulaChecks(tableToDelete, {
|
||||||
oldTable: null,
|
|
||||||
deletion: true,
|
deletion: true,
|
||||||
})
|
})
|
||||||
await cleanupAttachments(tableToDelete, {
|
await cleanupAttachments(tableToDelete, {
|
||||||
|
|
|
@ -13,28 +13,28 @@ import {
|
||||||
} from "../../../constants"
|
} from "../../../constants"
|
||||||
import { getViews, saveView } from "../view/utils"
|
import { getViews, saveView } from "../view/utils"
|
||||||
import viewTemplate from "../view/viewBuilder"
|
import viewTemplate from "../view/viewBuilder"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { quotas } from "@budibase/pro"
|
import { quotas } from "@budibase/pro"
|
||||||
import { events } from "@budibase/backend-core"
|
import { events, context } from "@budibase/backend-core"
|
||||||
|
import { Database } from "@budibase/types"
|
||||||
|
|
||||||
export async function clearColumns(table: any, columnNames: any) {
|
export async function clearColumns(table: any, columnNames: any) {
|
||||||
const db = getAppDB()
|
const db: Database = context.getAppDB()
|
||||||
const rows = await db.allDocs(
|
const rows = await db.allDocs(
|
||||||
getRowParams(table._id, null, {
|
getRowParams(table._id, null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
return db.bulkDocs(
|
return (await db.bulkDocs(
|
||||||
rows.rows.map(({ doc }: any) => {
|
rows.rows.map(({ doc }: any) => {
|
||||||
columnNames.forEach((colName: any) => delete doc[colName])
|
columnNames.forEach((colName: any) => delete doc[colName])
|
||||||
return doc
|
return doc
|
||||||
})
|
})
|
||||||
)
|
)) as { id: string; _rev?: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkForColumnUpdates(oldTable: any, updatedTable: any) {
|
export async function checkForColumnUpdates(oldTable: any, updatedTable: any) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let updatedRows = []
|
let updatedRows = []
|
||||||
const rename = updatedTable._rename
|
const rename = updatedTable._rename
|
||||||
let deletedColumns: any = []
|
let deletedColumns: any = []
|
||||||
|
@ -133,7 +133,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
// Populate the table with rows imported from CSV in a bulk update
|
// Populate the table with rows imported from CSV in a bulk update
|
||||||
const data = await transform({
|
const data = await transform({
|
||||||
...dataImport,
|
...dataImport,
|
||||||
|
@ -150,7 +150,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleSearchIndexes(table: any) {
|
export async function handleSearchIndexes(table: any) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
// create relevant search indexes
|
// create relevant search indexes
|
||||||
if (table.indexes && table.indexes.length > 0) {
|
if (table.indexes && table.indexes.length > 0) {
|
||||||
const currentIndexes = await db.getIndexes()
|
const currentIndexes = await db.getIndexes()
|
||||||
|
@ -214,7 +214,7 @@ class TableSaveFunctions {
|
||||||
rows: any
|
rows: any
|
||||||
|
|
||||||
constructor({ user, oldTable, dataImport }: any) {
|
constructor({ user, oldTable, dataImport }: any) {
|
||||||
this.db = getAppDB()
|
this.db = context.getAppDB()
|
||||||
this.user = user
|
this.user = user
|
||||||
this.oldTable = oldTable
|
this.oldTable = oldTable
|
||||||
this.dataImport = dataImport
|
this.dataImport = dataImport
|
||||||
|
@ -338,7 +338,7 @@ export function generateJunctionTableName(
|
||||||
return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`
|
return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function foreignKeyStructure(keyName: any, meta = null) {
|
export function foreignKeyStructure(keyName: any, meta?: any) {
|
||||||
const structure: any = {
|
const structure: any = {
|
||||||
type: FieldTypes.NUMBER,
|
type: FieldTypes.NUMBER,
|
||||||
constraints: {},
|
constraints: {},
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
const fetch = require("node-fetch")
|
import nodeFetch from "node-fetch"
|
||||||
const { downloadTemplate } = require("../../utilities/fileSystem")
|
import { downloadTemplate as dlTemplate } from "../../utilities/fileSystem"
|
||||||
const env = require("../../environment")
|
import env from "../../environment"
|
||||||
|
import { BBContext } from "@budibase/types"
|
||||||
|
|
||||||
// development flag, can be used to test against templates exported locally
|
// development flag, can be used to test against templates exported locally
|
||||||
const DEFAULT_TEMPLATES_BUCKET =
|
const DEFAULT_TEMPLATES_BUCKET =
|
||||||
"prod-budi-templates.s3-eu-west-1.amazonaws.com"
|
"prod-budi-templates.s3-eu-west-1.amazonaws.com"
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
export async function fetch(ctx: BBContext) {
|
||||||
let type = env.TEMPLATE_REPOSITORY
|
let type = env.TEMPLATE_REPOSITORY
|
||||||
let response,
|
let response,
|
||||||
error = false
|
error = false
|
||||||
try {
|
try {
|
||||||
response = await fetch(`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`)
|
response = await nodeFetch(
|
||||||
|
`https://${DEFAULT_TEMPLATES_BUCKET}/manifest.json`
|
||||||
|
)
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
error = true
|
error = true
|
||||||
}
|
}
|
||||||
|
@ -29,10 +32,10 @@ exports.fetch = async function (ctx) {
|
||||||
|
|
||||||
// can't currently test this, have to ignore from coverage
|
// can't currently test this, have to ignore from coverage
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
exports.downloadTemplate = async function (ctx) {
|
export async function downloadTemplate(ctx: BBContext) {
|
||||||
const { type, name } = ctx.params
|
const { type, name } = ctx.params
|
||||||
|
|
||||||
await downloadTemplate(type, name)
|
await dlTemplate(type, name)
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: `template ${type}:${name} downloaded successfully.`,
|
message: `template ${type}:${name} downloaded successfully.`,
|
|
@ -1,23 +1,22 @@
|
||||||
const {
|
import {
|
||||||
generateUserMetadataID,
|
generateUserMetadataID,
|
||||||
getUserMetadataParams,
|
getUserMetadataParams,
|
||||||
generateUserFlagID,
|
generateUserFlagID,
|
||||||
} = require("../../db/utils")
|
} from "../../db/utils"
|
||||||
const { InternalTables } = require("../../db/utils")
|
import { InternalTables } from "../../db/utils"
|
||||||
const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global")
|
import { getGlobalUsers, getRawGlobalUser } from "../../utilities/global"
|
||||||
const { getFullUser } = require("../../utilities/users")
|
import { getFullUser } from "../../utilities/users"
|
||||||
const { isEqual } = require("lodash")
|
import { isEqual } from "lodash"
|
||||||
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
|
import {
|
||||||
const {
|
context,
|
||||||
getDevelopmentAppID,
|
constants,
|
||||||
getProdAppIDs,
|
roles as rolesCore,
|
||||||
dbExists,
|
db as dbCore,
|
||||||
} = require("@budibase/backend-core/db")
|
} from "@budibase/backend-core"
|
||||||
const { UserStatus } = require("@budibase/backend-core/constants")
|
import { BBContext, User } from "@budibase/types"
|
||||||
const { getAppDB, doInAppContext } = require("@budibase/backend-core/context")
|
|
||||||
|
|
||||||
async function rawMetadata() {
|
async function rawMetadata() {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
return (
|
return (
|
||||||
await db.allDocs(
|
await db.allDocs(
|
||||||
getUserMetadataParams(null, {
|
getUserMetadataParams(null, {
|
||||||
|
@ -27,9 +26,9 @@ async function rawMetadata() {
|
||||||
).rows.map(row => row.doc)
|
).rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function combineMetadataAndUser(user, metadata) {
|
function combineMetadataAndUser(user: any, metadata: any) {
|
||||||
// skip users with no access
|
// skip users with no access
|
||||||
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
|
if (user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
delete user._rev
|
delete user._rev
|
||||||
|
@ -55,9 +54,9 @@ function combineMetadataAndUser(user, metadata) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.syncGlobalUsers = async () => {
|
export async function syncGlobalUsers() {
|
||||||
// sync user metadata
|
// sync user metadata
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const [users, metadata] = await Promise.all([getGlobalUsers(), rawMetadata()])
|
const [users, metadata] = await Promise.all([getGlobalUsers(), rawMetadata()])
|
||||||
const toWrite = []
|
const toWrite = []
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
|
@ -69,13 +68,13 @@ exports.syncGlobalUsers = async () => {
|
||||||
await db.bulkDocs(toWrite)
|
await db.bulkDocs(toWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.syncUser = async function (ctx) {
|
export async function syncUser(ctx: BBContext) {
|
||||||
let deleting = false,
|
let deleting = false,
|
||||||
user
|
user: User | any
|
||||||
const userId = ctx.params.id
|
const userId = ctx.params.id
|
||||||
try {
|
try {
|
||||||
user = await getRawGlobalUser(userId)
|
user = await getRawGlobalUser(userId)
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
if (err && err.status === 404) {
|
if (err && err.status === 404) {
|
||||||
user = {}
|
user = {}
|
||||||
deleting = true
|
deleting = true
|
||||||
|
@ -92,21 +91,21 @@ exports.syncUser = async function (ctx) {
|
||||||
let prodAppIds
|
let prodAppIds
|
||||||
// if they are a builder then get all production app IDs
|
// if they are a builder then get all production app IDs
|
||||||
if ((user.builder && user.builder.global) || deleting) {
|
if ((user.builder && user.builder.global) || deleting) {
|
||||||
prodAppIds = await getProdAppIDs()
|
prodAppIds = await dbCore.getProdAppIDs()
|
||||||
} else {
|
} else {
|
||||||
prodAppIds = Object.entries(roles)
|
prodAppIds = Object.entries(roles)
|
||||||
.filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
|
.filter(entry => entry[1] !== rolesCore.BUILTIN_ROLE_IDS.PUBLIC)
|
||||||
.map(([appId]) => appId)
|
.map(([appId]) => appId)
|
||||||
}
|
}
|
||||||
for (let prodAppId of prodAppIds) {
|
for (let prodAppId of prodAppIds) {
|
||||||
const roleId = roles[prodAppId]
|
const roleId = roles[prodAppId]
|
||||||
const devAppId = getDevelopmentAppID(prodAppId)
|
const devAppId = dbCore.getDevelopmentAppID(prodAppId)
|
||||||
for (let appId of [prodAppId, devAppId]) {
|
for (let appId of [prodAppId, devAppId]) {
|
||||||
if (!(await dbExists(appId))) {
|
if (!(await dbCore.dbExists(appId))) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
await doInAppContext(appId, async () => {
|
await context.doInAppContext(appId, async () => {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const metadataId = generateUserMetadataID(userId)
|
const metadataId = generateUserMetadataID(userId)
|
||||||
let metadata
|
let metadata
|
||||||
try {
|
try {
|
||||||
|
@ -127,8 +126,8 @@ exports.syncUser = async function (ctx) {
|
||||||
? combineMetadataAndUser(user, metadata)
|
? combineMetadataAndUser(user, metadata)
|
||||||
: {
|
: {
|
||||||
...metadata,
|
...metadata,
|
||||||
status: UserStatus.INACTIVE,
|
status: constants.UserStatus.INACTIVE,
|
||||||
metadata: BUILTIN_ROLE_IDS.PUBLIC,
|
metadata: rolesCore.BUILTIN_ROLE_IDS.PUBLIC,
|
||||||
}
|
}
|
||||||
// if its null then there was no updates required
|
// if its null then there was no updates required
|
||||||
if (combined) {
|
if (combined) {
|
||||||
|
@ -142,10 +141,9 @@ exports.syncUser = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchMetadata = async function (ctx) {
|
export async function fetchMetadata(ctx: BBContext) {
|
||||||
const database = getAppDB()
|
|
||||||
const global = await getGlobalUsers()
|
const global = await getGlobalUsers()
|
||||||
const metadata = await rawMetadata(database)
|
const metadata = await rawMetadata()
|
||||||
const users = []
|
const users = []
|
||||||
for (let user of global) {
|
for (let user of global) {
|
||||||
// find the metadata that matches up to the global ID
|
// find the metadata that matches up to the global ID
|
||||||
|
@ -162,18 +160,18 @@ exports.fetchMetadata = async function (ctx) {
|
||||||
ctx.body = users
|
ctx.body = users
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateSelfMetadata = async function (ctx) {
|
export async function updateSelfMetadata(ctx: BBContext) {
|
||||||
// overwrite the ID with current users
|
// overwrite the ID with current users
|
||||||
ctx.request.body._id = ctx.user._id
|
ctx.request.body._id = ctx.user?._id
|
||||||
// make sure no stale rev
|
// make sure no stale rev
|
||||||
delete ctx.request.body._rev
|
delete ctx.request.body._rev
|
||||||
// make sure no csrf token
|
// make sure no csrf token
|
||||||
delete ctx.request.body.csrfToken
|
delete ctx.request.body.csrfToken
|
||||||
await exports.updateMetadata(ctx)
|
await updateMetadata(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateMetadata = async function (ctx) {
|
export async function updateMetadata(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const user = ctx.request.body
|
const user = ctx.request.body
|
||||||
// this isn't applicable to the user
|
// this isn't applicable to the user
|
||||||
delete user.roles
|
delete user.roles
|
||||||
|
@ -184,8 +182,8 @@ exports.updateMetadata = async function (ctx) {
|
||||||
ctx.body = await db.put(metadata)
|
ctx.body = await db.put(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroyMetadata = async function (ctx) {
|
export async function destroyMetadata(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
try {
|
try {
|
||||||
const dbUser = await db.get(ctx.params.id)
|
const dbUser = await db.get(ctx.params.id)
|
||||||
await db.remove(dbUser._id, dbUser._rev)
|
await db.remove(dbUser._id, dbUser._rev)
|
||||||
|
@ -197,18 +195,18 @@ exports.destroyMetadata = async function (ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.findMetadata = async function (ctx) {
|
export async function findMetadata(ctx: BBContext) {
|
||||||
ctx.body = await getFullUser(ctx, ctx.params.id)
|
ctx.body = await getFullUser(ctx, ctx.params.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.setFlag = async function (ctx) {
|
export async function setFlag(ctx: BBContext) {
|
||||||
const userId = ctx.user._id
|
const userId = ctx.user?._id
|
||||||
const { flag, value } = ctx.request.body
|
const { flag, value } = ctx.request.body
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
ctx.throw(400, "Must supply a 'flag' field in request body.")
|
ctx.throw(400, "Must supply a 'flag' field in request body.")
|
||||||
}
|
}
|
||||||
const flagDocId = generateUserFlagID(userId)
|
const flagDocId = generateUserFlagID(userId!)
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let doc
|
let doc
|
||||||
try {
|
try {
|
||||||
doc = await db.get(flagDocId)
|
doc = await db.get(flagDocId)
|
||||||
|
@ -220,10 +218,10 @@ exports.setFlag = async function (ctx) {
|
||||||
ctx.body = { message: "Flag set successfully" }
|
ctx.body = { message: "Flag set successfully" }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getFlags = async function (ctx) {
|
export async function getFlags(ctx: BBContext) {
|
||||||
const userId = ctx.user._id
|
const userId = ctx.user?._id
|
||||||
const docId = generateUserFlagID(userId)
|
const docId = generateUserFlagID(userId!)
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
let doc
|
let doc
|
||||||
try {
|
try {
|
||||||
doc = await db.get(docId)
|
doc = await db.get(docId)
|
|
@ -1,4 +1,6 @@
|
||||||
exports.csv = function (headers, rows) {
|
import { Row } from "@budibase/types"
|
||||||
|
|
||||||
|
export function csv(headers: string[], rows: Row[]) {
|
||||||
let csv = headers.map(key => `"${key}"`).join(",")
|
let csv = headers.map(key => `"${key}"`).join(",")
|
||||||
|
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
|
@ -16,11 +18,11 @@ exports.csv = function (headers, rows) {
|
||||||
return csv
|
return csv
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.json = function (headers, rows) {
|
export function json(headers: string[], rows: Row[]) {
|
||||||
return JSON.stringify(rows, undefined, 2)
|
return JSON.stringify(rows, undefined, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.ExportFormats = {
|
export const ExportFormats = {
|
||||||
CSV: "csv",
|
CSV: "csv",
|
||||||
JSON: "json",
|
JSON: "json",
|
||||||
}
|
}
|
|
@ -1,21 +1,29 @@
|
||||||
const viewTemplate = require("./viewBuilder")
|
import viewTemplate from "./viewBuilder"
|
||||||
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
import { apiFileReturn } from "../../../utilities/fileSystem"
|
||||||
const exporters = require("./exporters")
|
import * as exporters from "./exporters"
|
||||||
const { saveView, getView, getViews, deleteView } = require("./utils")
|
import { deleteView, getView, getViews, saveView } from "./utils"
|
||||||
const { fetchView } = require("../row")
|
import { fetchView } from "../row"
|
||||||
const { FieldTypes } = require("../../../constants")
|
import { FieldTypes } from "../../../constants"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { context, events } from "@budibase/backend-core"
|
||||||
const { events } = require("@budibase/backend-core")
|
import { DocumentType } from "../../../db/utils"
|
||||||
const { DocumentType } = require("../../../db/utils")
|
import sdk from "../../../sdk"
|
||||||
const { cloneDeep, isEqual } = require("lodash")
|
import {
|
||||||
const sdk = require("../../../sdk")
|
BBContext,
|
||||||
|
Row,
|
||||||
|
Table,
|
||||||
|
TableExportFormat,
|
||||||
|
TableSchema,
|
||||||
|
View,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
const { cloneDeep, isEqual } = require("lodash")
|
||||||
|
|
||||||
|
export async function fetch(ctx: BBContext) {
|
||||||
ctx.body = await getViews()
|
ctx.body = await getViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async ctx => {
|
export async function save(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const { originalName, ...viewToSave } = ctx.request.body
|
const { originalName, ...viewToSave } = ctx.request.body
|
||||||
const view = viewTemplate(viewToSave)
|
const view = viewTemplate(viewToSave)
|
||||||
const viewName = viewToSave.name
|
const viewName = viewToSave.name
|
||||||
|
@ -47,7 +55,7 @@ exports.save = async ctx => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const calculationEvents = async (existingView, newView) => {
|
export async function calculationEvents(existingView: View, newView: View) {
|
||||||
const existingCalculation = existingView && existingView.calculation
|
const existingCalculation = existingView && existingView.calculation
|
||||||
const newCalculation = newView && newView.calculation
|
const newCalculation = newView && newView.calculation
|
||||||
|
|
||||||
|
@ -68,7 +76,7 @@ const calculationEvents = async (existingView, newView) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterEvents = async (existingView, newView) => {
|
export async function filterEvents(existingView: View, newView: View) {
|
||||||
const hasExistingFilters = !!(
|
const hasExistingFilters = !!(
|
||||||
existingView &&
|
existingView &&
|
||||||
existingView.filters &&
|
existingView.filters &&
|
||||||
|
@ -93,7 +101,7 @@ const filterEvents = async (existingView, newView) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleViewEvents = async (existingView, newView) => {
|
async function handleViewEvents(existingView: View, newView: View) {
|
||||||
if (!existingView) {
|
if (!existingView) {
|
||||||
await events.view.created(newView)
|
await events.view.created(newView)
|
||||||
} else {
|
} else {
|
||||||
|
@ -103,8 +111,8 @@ const handleViewEvents = async (existingView, newView) => {
|
||||||
await filterEvents(existingView, newView)
|
await filterEvents(existingView, newView)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async ctx => {
|
export async function destroy(ctx: BBContext) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const viewName = decodeURI(ctx.params.viewName)
|
const viewName = decodeURI(ctx.params.viewName)
|
||||||
const view = await deleteView(viewName)
|
const view = await deleteView(viewName)
|
||||||
const table = await db.get(view.meta.tableId)
|
const table = await db.get(view.meta.tableId)
|
||||||
|
@ -115,11 +123,11 @@ exports.destroy = async ctx => {
|
||||||
ctx.body = view
|
ctx.body = view
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.exportView = async ctx => {
|
export async function exportView(ctx: BBContext) {
|
||||||
const viewName = decodeURI(ctx.query.view)
|
const viewName = decodeURI(ctx.query.view as string)
|
||||||
const view = await getView(viewName)
|
const view = await getView(viewName)
|
||||||
|
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format as string
|
||||||
if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
|
if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
|
||||||
ctx.throw(400, "Format must be specified, either csv or json")
|
ctx.throw(400, "Format must be specified, either csv or json")
|
||||||
}
|
}
|
||||||
|
@ -130,6 +138,7 @@ exports.exportView = async ctx => {
|
||||||
ctx.query = {
|
ctx.query = {
|
||||||
group: view.meta.groupBy,
|
group: view.meta.groupBy,
|
||||||
calculation: view.meta.calculation,
|
calculation: view.meta.calculation,
|
||||||
|
// @ts-ignore
|
||||||
stats: !!view.meta.field,
|
stats: !!view.meta.field,
|
||||||
field: view.meta.field,
|
field: view.meta.field,
|
||||||
}
|
}
|
||||||
|
@ -140,11 +149,11 @@ exports.exportView = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchView(ctx)
|
await fetchView(ctx)
|
||||||
let rows = ctx.body
|
let rows = ctx.body as Row[]
|
||||||
|
|
||||||
let schema = view && view.meta && view.meta.schema
|
let schema: TableSchema = view && view.meta && view.meta.schema
|
||||||
const tableId = ctx.params.tableId || view.meta.tableId
|
const tableId = ctx.params.tableId || view.meta.tableId
|
||||||
const table = await sdk.tables.getTable(tableId)
|
const table: Table = await sdk.tables.getTable(tableId)
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
schema = table.schema
|
schema = table.schema
|
||||||
}
|
}
|
||||||
|
@ -175,15 +184,15 @@ exports.exportView = async ctx => {
|
||||||
|
|
||||||
// Export part
|
// Export part
|
||||||
let headers = Object.keys(schema)
|
let headers = Object.keys(schema)
|
||||||
const exporter = exporters[format]
|
const exporter = format === "csv" ? exporters.csv : exporters.json
|
||||||
const filename = `${viewName}.${format}`
|
const filename = `${viewName}.${format}`
|
||||||
// send down the file
|
// send down the file
|
||||||
ctx.attachment(filename)
|
ctx.attachment(filename)
|
||||||
ctx.body = apiFileReturn(exporter(headers, rows))
|
ctx.body = apiFileReturn(exporter(headers, rows))
|
||||||
|
|
||||||
if (viewName.startsWith(DocumentType.TABLE)) {
|
if (viewName.startsWith(DocumentType.TABLE)) {
|
||||||
await events.table.exported(table, format)
|
await events.table.exported(table, format as TableExportFormat)
|
||||||
} else {
|
} else {
|
||||||
await events.view.exported(table, format)
|
await events.view.exported(table, format as TableExportFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,17 @@
|
||||||
const {
|
import {
|
||||||
ViewName,
|
ViewName,
|
||||||
generateMemoryViewID,
|
generateMemoryViewID,
|
||||||
getMemoryViewParams,
|
getMemoryViewParams,
|
||||||
DocumentType,
|
DocumentType,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
} = require("../../../db/utils")
|
} from "../../../db/utils"
|
||||||
const env = require("../../../environment")
|
import env from "../../../environment"
|
||||||
const { getAppDB } = require("@budibase/backend-core/context")
|
import { context } from "@budibase/backend-core"
|
||||||
const viewBuilder = require("./viewBuilder")
|
import viewBuilder from "./viewBuilder"
|
||||||
|
import { Database } from "@budibase/types"
|
||||||
|
|
||||||
exports.getView = async viewName => {
|
export async function getView(viewName: string) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
return designDoc.views[viewName]
|
return designDoc.views[viewName]
|
||||||
|
@ -23,7 +24,7 @@ exports.getView = async viewName => {
|
||||||
try {
|
try {
|
||||||
const viewDoc = await db.get(generateMemoryViewID(viewName))
|
const viewDoc = await db.get(generateMemoryViewID(viewName))
|
||||||
return viewDoc.view
|
return viewDoc.view
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
// Return null when PouchDB doesn't found the view
|
// Return null when PouchDB doesn't found the view
|
||||||
if (err.status === 404) {
|
if (err.status === 404) {
|
||||||
return null
|
return null
|
||||||
|
@ -34,14 +35,15 @@ exports.getView = async viewName => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getViews = async () => {
|
export async function getViews() {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
const response = []
|
const response = []
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
for (let name of Object.keys(designDoc.views)) {
|
for (let name of Object.keys(designDoc.views)) {
|
||||||
// Only return custom views, not built ins
|
// Only return custom views, not built ins
|
||||||
if (Object.values(ViewName).indexOf(name) !== -1) {
|
const viewNames = Object.values(ViewName) as string[]
|
||||||
|
if (viewNames.indexOf(name) !== -1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
response.push({
|
response.push({
|
||||||
|
@ -67,8 +69,12 @@ exports.getViews = async () => {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.saveView = async (originalName, viewName, viewTemplate) => {
|
export async function saveView(
|
||||||
const db = getAppDB()
|
originalName: string | null,
|
||||||
|
viewName: string,
|
||||||
|
viewTemplate: any
|
||||||
|
) {
|
||||||
|
const db = context.getAppDB()
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
designDoc.views = {
|
designDoc.views = {
|
||||||
|
@ -83,7 +89,7 @@ exports.saveView = async (originalName, viewName, viewTemplate) => {
|
||||||
} else {
|
} else {
|
||||||
const id = generateMemoryViewID(viewName)
|
const id = generateMemoryViewID(viewName)
|
||||||
const originalId = originalName ? generateMemoryViewID(originalName) : null
|
const originalId = originalName ? generateMemoryViewID(originalName) : null
|
||||||
const viewDoc = {
|
const viewDoc: any = {
|
||||||
_id: id,
|
_id: id,
|
||||||
view: viewTemplate,
|
view: viewTemplate,
|
||||||
name: viewName,
|
name: viewName,
|
||||||
|
@ -105,8 +111,8 @@ exports.saveView = async (originalName, viewName, viewTemplate) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.deleteView = async viewName => {
|
export async function deleteView(viewName: string) {
|
||||||
const db = getAppDB()
|
const db = context.getAppDB()
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
const view = designDoc.views[viewName]
|
const view = designDoc.views[viewName]
|
||||||
|
@ -121,7 +127,7 @@ exports.deleteView = async viewName => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.migrateToInMemoryView = async (db, viewName) => {
|
export async function migrateToInMemoryView(db: Database, viewName: string) {
|
||||||
// delete the view initially
|
// delete the view initially
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
// run the view back through the view builder to update it
|
// run the view back through the view builder to update it
|
||||||
|
@ -131,7 +137,7 @@ exports.migrateToInMemoryView = async (db, viewName) => {
|
||||||
await exports.saveView(db, null, viewName, view)
|
await exports.saveView(db, null, viewName, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.migrateToDesignView = async (db, viewName) => {
|
export async function migrateToDesignView(db: Database, viewName: string) {
|
||||||
let view = await db.get(generateMemoryViewID(viewName))
|
let view = await db.get(generateMemoryViewID(viewName))
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
designDoc.views[viewName] = viewBuilder(view.view.meta)
|
designDoc.views[viewName] = viewBuilder(view.view.meta)
|
||||||
|
@ -139,7 +145,7 @@ exports.migrateToDesignView = async (db, viewName) => {
|
||||||
await db.remove(view._id, view._rev)
|
await db.remove(view._id, view._rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getFromDesignDoc = async (db, viewName) => {
|
export async function getFromDesignDoc(db: Database, viewName: string) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
let view = designDoc.views[viewName]
|
let view = designDoc.views[viewName]
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
|
@ -148,7 +154,7 @@ exports.getFromDesignDoc = async (db, viewName) => {
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getFromMemoryDoc = async (db, viewName) => {
|
export async function getFromMemoryDoc(db: Database, viewName: string) {
|
||||||
let view = await db.get(generateMemoryViewID(viewName))
|
let view = await db.get(generateMemoryViewID(viewName))
|
||||||
if (view) {
|
if (view) {
|
||||||
view = view.view
|
view = view.view
|
|
@ -1,20 +1,15 @@
|
||||||
const Router = require("@koa/router")
|
import Router from "@koa/router"
|
||||||
const {
|
import { errors, auth } from "@budibase/backend-core"
|
||||||
buildAuthMiddleware,
|
import currentApp from "../middleware/currentapp"
|
||||||
auditLog,
|
import zlib from "zlib"
|
||||||
buildTenancyMiddleware,
|
import { mainRoutes, staticRoutes, publicRoutes } from "./routes"
|
||||||
} = require("@budibase/backend-core/auth")
|
import pkg from "../../package.json"
|
||||||
const { errors } = require("@budibase/backend-core")
|
import env from "../environment"
|
||||||
const currentApp = require("../middleware/currentapp")
|
import { middleware as pro } from "@budibase/pro"
|
||||||
|
export { shutdown } from "./routes/public"
|
||||||
const compress = require("koa-compress")
|
const compress = require("koa-compress")
|
||||||
const zlib = require("zlib")
|
|
||||||
const { mainRoutes, staticRoutes, publicRoutes } = require("./routes")
|
|
||||||
const pkg = require("../../package.json")
|
|
||||||
const env = require("../environment")
|
|
||||||
const { middleware: pro } = require("@budibase/pro")
|
|
||||||
const { shutdown } = require("./routes/public")
|
|
||||||
|
|
||||||
const router = new Router()
|
export const router: Router = new Router()
|
||||||
|
|
||||||
router.get("/health", ctx => (ctx.status = 200))
|
router.get("/health", ctx => (ctx.status = 200))
|
||||||
router.get("/version", ctx => (ctx.body = pkg.version))
|
router.get("/version", ctx => (ctx.body = pkg.version))
|
||||||
|
@ -42,7 +37,7 @@ router
|
||||||
// re-direct before any middlewares occur
|
// re-direct before any middlewares occur
|
||||||
.redirect("/", "/builder")
|
.redirect("/", "/builder")
|
||||||
.use(
|
.use(
|
||||||
buildAuthMiddleware(null, {
|
auth.buildAuthMiddleware(null, {
|
||||||
publicAllowed: true,
|
publicAllowed: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -50,19 +45,20 @@ router
|
||||||
// the server can be public anywhere, so nowhere should throw errors
|
// the server can be public anywhere, so nowhere should throw errors
|
||||||
// if the tenancy has not been set, it'll have to be discovered at application layer
|
// if the tenancy has not been set, it'll have to be discovered at application layer
|
||||||
.use(
|
.use(
|
||||||
buildTenancyMiddleware(null, null, {
|
auth.buildTenancyMiddleware(null, null, {
|
||||||
noTenancyRequired: true,
|
noTenancyRequired: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.use(pro.licensing())
|
.use(pro.licensing())
|
||||||
|
// @ts-ignore
|
||||||
.use(currentApp)
|
.use(currentApp)
|
||||||
.use(auditLog)
|
.use(auth.auditLog)
|
||||||
|
|
||||||
// error handling middleware
|
// error handling middleware
|
||||||
router.use(async (ctx, next) => {
|
router.use(async (ctx, next) => {
|
||||||
try {
|
try {
|
||||||
await next()
|
await next()
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
ctx.status = err.status || err.statusCode || 500
|
ctx.status = err.status || err.statusCode || 500
|
||||||
const error = errors.getPublicError(err)
|
const error = errors.getPublicError(err)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
|
@ -91,6 +87,3 @@ router.use(publicRoutes.allowedMethods())
|
||||||
// WARNING - static routes will catch everything else after them this must be last
|
// WARNING - static routes will catch everything else after them this must be last
|
||||||
router.use(staticRoutes.routes())
|
router.use(staticRoutes.routes())
|
||||||
router.use(staticRoutes.allowedMethods())
|
router.use(staticRoutes.allowedMethods())
|
||||||
|
|
||||||
module.exports.router = router
|
|
||||||
module.exports.shutdown = shutdown
|
|
|
@ -1,9 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/analytics")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router.get("/api/bbtel", controller.isEnabled)
|
|
||||||
router.post("/api/bbtel/ping", controller.ping)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/analytics"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router.get("/api/bbtel", controller.isEnabled)
|
||||||
|
router.post("/api/bbtel/ping", controller.ping)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,12 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/apikeys")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/keys", authorized(BUILDER), controller.fetch)
|
|
||||||
.put("/api/keys/:key", authorized(BUILDER), controller.update)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/apikeys"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get("/api/keys", authorized(permissions.BUILDER), controller.fetch)
|
||||||
|
.put("/api/keys/:key", authorized(permissions.BUILDER), controller.update)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,16 +1,20 @@
|
||||||
import Router from "@koa/router"
|
import Router from "@koa/router"
|
||||||
import * as controller from "../controllers/application"
|
import * as controller from "../controllers/application"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
import { BUILDER } from "@budibase/backend-core/permissions"
|
import { permissions } from "@budibase/backend-core"
|
||||||
import { applicationValidator } from "./utils/validators"
|
import { applicationValidator } from "./utils/validators"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
|
.post(
|
||||||
|
"/api/applications/:appId/sync",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.sync
|
||||||
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/applications",
|
"/api/applications",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
applicationValidator(),
|
applicationValidator(),
|
||||||
controller.create
|
controller.create
|
||||||
)
|
)
|
||||||
|
@ -19,20 +23,24 @@ router
|
||||||
.get("/api/applications/:appId/appPackage", controller.fetchAppPackage)
|
.get("/api/applications/:appId/appPackage", controller.fetchAppPackage)
|
||||||
.put(
|
.put(
|
||||||
"/api/applications/:appId",
|
"/api/applications/:appId",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
applicationValidator({ isCreate: false }),
|
applicationValidator({ isCreate: false }),
|
||||||
controller.update
|
controller.update
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/applications/:appId/client/update",
|
"/api/applications/:appId/client/update",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
controller.updateClient
|
controller.updateClient
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/applications/:appId/client/revert",
|
"/api/applications/:appId/client/revert",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
controller.revertClient
|
controller.revertClient
|
||||||
)
|
)
|
||||||
.delete("/api/applications/:appId", authorized(BUILDER), controller.destroy)
|
.delete(
|
||||||
|
"/api/applications/:appId",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
export default router
|
export = router
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import Router from "@koa/router"
|
import Router from "@koa/router"
|
||||||
import * as controller from "../controllers/auth"
|
import * as controller from "../controllers/auth"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router.get("/api/self", controller.fetchSelf)
|
router.get("/api/self", controller.fetchSelf)
|
||||||
|
|
||||||
export default router
|
export = router
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/automation")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const {
|
|
||||||
BUILDER,
|
|
||||||
PermissionLevel,
|
|
||||||
PermissionType,
|
|
||||||
} = require("@budibase/backend-core/permissions")
|
|
||||||
const { bodyResource, paramResource } = require("../../middleware/resourceId")
|
|
||||||
const {
|
|
||||||
middleware: appInfoMiddleware,
|
|
||||||
AppType,
|
|
||||||
} = require("../../middleware/appInfo")
|
|
||||||
const { automationValidator } = require("./utils/validators")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get(
|
|
||||||
"/api/automations/trigger/list",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.getTriggerList
|
|
||||||
)
|
|
||||||
.get(
|
|
||||||
"/api/automations/action/list",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.getActionList
|
|
||||||
)
|
|
||||||
.get(
|
|
||||||
"/api/automations/definitions/list",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.getDefinitionList
|
|
||||||
)
|
|
||||||
.get("/api/automations", authorized(BUILDER), controller.fetch)
|
|
||||||
.get(
|
|
||||||
"/api/automations/:id",
|
|
||||||
paramResource("id"),
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.find
|
|
||||||
)
|
|
||||||
.put(
|
|
||||||
"/api/automations",
|
|
||||||
bodyResource("_id"),
|
|
||||||
authorized(BUILDER),
|
|
||||||
automationValidator(true),
|
|
||||||
controller.update
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/api/automations",
|
|
||||||
authorized(BUILDER),
|
|
||||||
automationValidator(false),
|
|
||||||
controller.create
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/api/automations/logs/search",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.logSearch
|
|
||||||
)
|
|
||||||
.delete(
|
|
||||||
"/api/automations/logs",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.clearLogError
|
|
||||||
)
|
|
||||||
.delete(
|
|
||||||
"/api/automations/:id/:rev",
|
|
||||||
paramResource("id"),
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.destroy
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/api/automations/:id/trigger",
|
|
||||||
appInfoMiddleware({ appType: AppType.PROD }),
|
|
||||||
paramResource("id"),
|
|
||||||
authorized(PermissionType.AUTOMATION, PermissionLevel.EXECUTE),
|
|
||||||
controller.trigger
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/api/automations/:id/test",
|
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
|
||||||
paramResource("id"),
|
|
||||||
authorized(PermissionType.AUTOMATION, PermissionLevel.EXECUTE),
|
|
||||||
controller.test
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/automation"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
import { bodyResource, paramResource } from "../../middleware/resourceId"
|
||||||
|
import {
|
||||||
|
middleware as appInfoMiddleware,
|
||||||
|
AppType,
|
||||||
|
} from "../../middleware/appInfo"
|
||||||
|
import { automationValidator } from "./utils/validators"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get(
|
||||||
|
"/api/automations/trigger/list",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.getTriggerList
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/automations/action/list",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.getActionList
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/automations/definitions/list",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.getDefinitionList
|
||||||
|
)
|
||||||
|
.get("/api/automations", authorized(permissions.BUILDER), controller.fetch)
|
||||||
|
.get(
|
||||||
|
"/api/automations/:id",
|
||||||
|
paramResource("id"),
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.find
|
||||||
|
)
|
||||||
|
.put(
|
||||||
|
"/api/automations",
|
||||||
|
bodyResource("_id"),
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
automationValidator(true),
|
||||||
|
controller.update
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/automations",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
automationValidator(false),
|
||||||
|
controller.create
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/automations/logs/search",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.logSearch
|
||||||
|
)
|
||||||
|
.delete(
|
||||||
|
"/api/automations/logs",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.clearLogError
|
||||||
|
)
|
||||||
|
.delete(
|
||||||
|
"/api/automations/:id/:rev",
|
||||||
|
paramResource("id"),
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/automations/:id/trigger",
|
||||||
|
appInfoMiddleware({ appType: AppType.PROD }),
|
||||||
|
paramResource("id"),
|
||||||
|
authorized(
|
||||||
|
permissions.PermissionType.AUTOMATION,
|
||||||
|
permissions.PermissionLevel.EXECUTE
|
||||||
|
),
|
||||||
|
controller.trigger
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/automations/:id/test",
|
||||||
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
|
paramResource("id"),
|
||||||
|
authorized(
|
||||||
|
permissions.PermissionType.AUTOMATION,
|
||||||
|
permissions.PermissionLevel.EXECUTE
|
||||||
|
),
|
||||||
|
controller.test
|
||||||
|
)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,10 +1,14 @@
|
||||||
import Router from "@koa/router"
|
import Router from "@koa/router"
|
||||||
import * as controller from "../controllers/backup"
|
import * as controller from "../controllers/backup"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
import { BUILDER } from "@budibase/backend-core/permissions"
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump)
|
router.get(
|
||||||
|
"/api/backups/export",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.exportAppDump
|
||||||
|
)
|
||||||
|
|
||||||
export default router
|
export = router
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/cloud")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/cloud/export", authorized(BUILDER), controller.exportApps)
|
|
||||||
// has to be public, only run if apps don't exist
|
|
||||||
.post("/api/cloud/import", controller.importApps)
|
|
||||||
.get("/api/cloud/import/complete", controller.hasBeenImported)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/cloud"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get(
|
||||||
|
"/api/cloud/export",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.exportApps
|
||||||
|
)
|
||||||
|
// has to be public, only run if apps don't exist
|
||||||
|
.post("/api/cloud/import", controller.importApps)
|
||||||
|
.get("/api/cloud/import/complete", controller.hasBeenImported)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,14 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/component")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router.get(
|
|
||||||
"/api/:appId/components/definitions",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.fetchAppComponentDefinitions
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/component"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/api/:appId/components/definitions",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.fetchAppComponentDefinitions
|
||||||
|
)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,51 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const datasourceController = require("../controllers/datasource")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const {
|
|
||||||
BUILDER,
|
|
||||||
PermissionLevel,
|
|
||||||
PermissionType,
|
|
||||||
} = require("@budibase/backend-core/permissions")
|
|
||||||
const {
|
|
||||||
datasourceValidator,
|
|
||||||
datasourceQueryValidator,
|
|
||||||
} = require("./utils/validators")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/datasources", authorized(BUILDER), datasourceController.fetch)
|
|
||||||
.get(
|
|
||||||
"/api/datasources/:datasourceId",
|
|
||||||
authorized(PermissionType.TABLE, PermissionLevel.READ),
|
|
||||||
datasourceController.find
|
|
||||||
)
|
|
||||||
.put(
|
|
||||||
"/api/datasources/:datasourceId",
|
|
||||||
authorized(PermissionType.TABLE, PermissionLevel.READ),
|
|
||||||
datasourceController.update
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/api/datasources/query",
|
|
||||||
authorized(PermissionType.TABLE, PermissionLevel.READ),
|
|
||||||
datasourceQueryValidator(),
|
|
||||||
datasourceController.query
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/api/datasources/:datasourceId/schema",
|
|
||||||
authorized(BUILDER),
|
|
||||||
datasourceController.buildSchemaFromDb
|
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/api/datasources",
|
|
||||||
authorized(BUILDER),
|
|
||||||
datasourceValidator(),
|
|
||||||
datasourceController.save
|
|
||||||
)
|
|
||||||
.delete(
|
|
||||||
"/api/datasources/:datasourceId/:revId",
|
|
||||||
authorized(BUILDER),
|
|
||||||
datasourceController.destroy
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as datasourceController from "../controllers/datasource"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
import {
|
||||||
|
datasourceValidator,
|
||||||
|
datasourceQueryValidator,
|
||||||
|
} from "./utils/validators"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get(
|
||||||
|
"/api/datasources",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
datasourceController.fetch
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/datasources/:datasourceId",
|
||||||
|
authorized(
|
||||||
|
permissions.PermissionType.TABLE,
|
||||||
|
permissions.PermissionLevel.READ
|
||||||
|
),
|
||||||
|
datasourceController.find
|
||||||
|
)
|
||||||
|
.put(
|
||||||
|
"/api/datasources/:datasourceId",
|
||||||
|
authorized(
|
||||||
|
permissions.PermissionType.TABLE,
|
||||||
|
permissions.PermissionLevel.READ
|
||||||
|
),
|
||||||
|
datasourceController.update
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/datasources/query",
|
||||||
|
authorized(
|
||||||
|
permissions.PermissionType.TABLE,
|
||||||
|
permissions.PermissionLevel.READ
|
||||||
|
),
|
||||||
|
datasourceQueryValidator(),
|
||||||
|
datasourceController.query
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/datasources/:datasourceId/schema",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
datasourceController.buildSchemaFromDb
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/datasources",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
datasourceValidator(),
|
||||||
|
datasourceController.save
|
||||||
|
)
|
||||||
|
.delete(
|
||||||
|
"/api/datasources/:datasourceId/:revId",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
datasourceController.destroy
|
||||||
|
)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,17 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/deploy")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/deployments", authorized(BUILDER), controller.fetchDeployments)
|
|
||||||
.get(
|
|
||||||
"/api/deploy/:deploymentId",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.deploymentProgress
|
|
||||||
)
|
|
||||||
.post("/api/deploy", authorized(BUILDER), controller.deployApp)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/deploy"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get(
|
||||||
|
"/api/deployments",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.fetchDeployments
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/deploy/:deploymentId",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.deploymentProgress
|
||||||
|
)
|
||||||
|
.post("/api/deploy", authorized(permissions.BUILDER), controller.deployApp)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,26 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/dev")
|
|
||||||
const env = require("../../environment")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
function redirectPath(path) {
|
|
||||||
router
|
|
||||||
.get(`/api/${path}/:devPath(.*)`, controller.buildRedirectGet(path))
|
|
||||||
.post(`/api/${path}/:devPath(.*)`, controller.buildRedirectPost(path))
|
|
||||||
.delete(`/api/${path}/:devPath(.*)`, controller.buildRedirectDelete(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.isDev() || env.isTest()) {
|
|
||||||
redirectPath("global")
|
|
||||||
redirectPath("system")
|
|
||||||
}
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/dev/version", authorized(BUILDER), controller.getBudibaseVersion)
|
|
||||||
.delete("/api/dev/:appId/lock", authorized(BUILDER), controller.clearLock)
|
|
||||||
.post("/api/dev/:appId/revert", authorized(BUILDER), controller.revert)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/dev"
|
||||||
|
import env from "../../environment"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
function redirectPath(path: string) {
|
||||||
|
router
|
||||||
|
.get(`/api/${path}/:devPath(.*)`, controller.buildRedirectGet(path))
|
||||||
|
.post(`/api/${path}/:devPath(.*)`, controller.buildRedirectPost(path))
|
||||||
|
.delete(`/api/${path}/:devPath(.*)`, controller.buildRedirectDelete(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.isDev() || env.isTest()) {
|
||||||
|
redirectPath("global")
|
||||||
|
redirectPath("system")
|
||||||
|
}
|
||||||
|
|
||||||
|
router
|
||||||
|
.get(
|
||||||
|
"/api/dev/version",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.getBudibaseVersion
|
||||||
|
)
|
||||||
|
.delete(
|
||||||
|
"/api/dev/:appId/lock",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.clearLock
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/dev/:appId/revert",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.revert
|
||||||
|
)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,12 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/integration")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/integrations", authorized(BUILDER), controller.fetch)
|
|
||||||
.get("/api/integrations/:type", authorized(BUILDER), controller.find)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import controller from "../controllers/integration"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get("/api/integrations", authorized(permissions.BUILDER), controller.fetch)
|
||||||
|
.get(
|
||||||
|
"/api/integrations/:type",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.find
|
||||||
|
)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,16 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
const controller = require("../controllers/layout")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.post("/api/layouts", authorized(BUILDER), controller.save)
|
|
||||||
.delete(
|
|
||||||
"/api/layouts/:layoutId/:layoutRev",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.destroy
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
import * as controller from "../controllers/layout"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.post("/api/layouts", authorized(permissions.BUILDER), controller.save)
|
||||||
|
.delete(
|
||||||
|
"/api/layouts/:layoutId/:layoutRev",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,38 +1,38 @@
|
||||||
const Router = require("@koa/router")
|
import Router from "@koa/router"
|
||||||
const controller = require("../controllers/metadata")
|
import * as controller from "../controllers/metadata"
|
||||||
const {
|
import {
|
||||||
middleware: appInfoMiddleware,
|
middleware as appInfoMiddleware,
|
||||||
AppType,
|
AppType,
|
||||||
} = require("../../middleware/appInfo")
|
} from "../../middleware/appInfo"
|
||||||
const authorized = require("../../middleware/authorized")
|
import authorized from "../../middleware/authorized"
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post(
|
.post(
|
||||||
"/api/metadata/:type/:entityId",
|
"/api/metadata/:type/:entityId",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.saveMetadata
|
controller.saveMetadata
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/api/metadata/:type/:entityId",
|
"/api/metadata/:type/:entityId",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.deleteMetadata
|
controller.deleteMetadata
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/metadata/type",
|
"/api/metadata/type",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.getTypes
|
controller.getTypes
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/metadata/:type/:entityId",
|
"/api/metadata/:type/:entityId",
|
||||||
authorized(BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
controller.getMetadata
|
controller.getMetadata
|
||||||
)
|
)
|
||||||
|
|
||||||
module.exports = router
|
export = router
|
|
@ -1,14 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const migrationsController = require("../controllers/migrations")
|
|
||||||
const router = new Router()
|
|
||||||
const { internalApi } = require("@budibase/backend-core/auth")
|
|
||||||
|
|
||||||
router
|
|
||||||
.post("/api/migrations/run", internalApi, migrationsController.migrate)
|
|
||||||
.get(
|
|
||||||
"/api/migrations/definitions",
|
|
||||||
internalApi,
|
|
||||||
migrationsController.fetchDefinitions
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as migrationsController from "../controllers/migrations"
|
||||||
|
import { auth } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.post("/api/migrations/run", auth.internalApi, migrationsController.migrate)
|
||||||
|
.get(
|
||||||
|
"/api/migrations/definitions",
|
||||||
|
auth.internalApi,
|
||||||
|
migrationsController.fetchDefinitions
|
||||||
|
)
|
||||||
|
export = router
|
|
@ -1,33 +0,0 @@
|
||||||
const Router = require("@koa/router")
|
|
||||||
const controller = require("../controllers/permission")
|
|
||||||
const authorized = require("../../middleware/authorized")
|
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
|
||||||
const { permissionValidator } = require("./utils/validators")
|
|
||||||
|
|
||||||
const router = new Router()
|
|
||||||
|
|
||||||
router
|
|
||||||
.get("/api/permission/builtin", authorized(BUILDER), controller.fetchBuiltin)
|
|
||||||
.get("/api/permission/levels", authorized(BUILDER), controller.fetchLevels)
|
|
||||||
.get("/api/permission", authorized(BUILDER), controller.fetch)
|
|
||||||
.get(
|
|
||||||
"/api/permission/:resourceId",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.getResourcePerms
|
|
||||||
)
|
|
||||||
// adding a specific role/level for the resource overrides the underlying access control
|
|
||||||
.post(
|
|
||||||
"/api/permission/:roleId/:resourceId/:level",
|
|
||||||
authorized(BUILDER),
|
|
||||||
permissionValidator(),
|
|
||||||
controller.addPermission
|
|
||||||
)
|
|
||||||
// deleting the level defaults it back the underlying access control for the resource
|
|
||||||
.delete(
|
|
||||||
"/api/permission/:roleId/:resourceId/:level",
|
|
||||||
authorized(BUILDER),
|
|
||||||
permissionValidator(),
|
|
||||||
controller.removePermission
|
|
||||||
)
|
|
||||||
|
|
||||||
module.exports = router
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import Router from "@koa/router"
|
||||||
|
import * as controller from "../controllers/permission"
|
||||||
|
import authorized from "../../middleware/authorized"
|
||||||
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
import { permissionValidator } from "./utils/validators"
|
||||||
|
|
||||||
|
const router: Router = new Router()
|
||||||
|
|
||||||
|
router
|
||||||
|
.get(
|
||||||
|
"/api/permission/builtin",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.fetchBuiltin
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/permission/levels",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.fetchLevels
|
||||||
|
)
|
||||||
|
.get("/api/permission", authorized(permissions.BUILDER), controller.fetch)
|
||||||
|
.get(
|
||||||
|
"/api/permission/:resourceId",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.getResourcePerms
|
||||||
|
)
|
||||||
|
// adding a specific role/level for the resource overrides the underlying access control
|
||||||
|
.post(
|
||||||
|
"/api/permission/:roleId/:resourceId/:level",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
permissionValidator(),
|
||||||
|
controller.addPermission
|
||||||
|
)
|
||||||
|
// deleting the level defaults it back the underlying access control for the resource
|
||||||
|
.delete(
|
||||||
|
"/api/permission/:roleId/:resourceId/:level",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
permissionValidator(),
|
||||||
|
controller.removePermission
|
||||||
|
)
|
||||||
|
|
||||||
|
export = router
|
|
@ -1,14 +1,22 @@
|
||||||
import Router from "@koa/router"
|
import Router from "@koa/router"
|
||||||
import * as controller from "../controllers/plugin"
|
import * as controller from "../controllers/plugin"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
import { BUILDER } from "@budibase/backend-core/permissions"
|
import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/plugin/upload", authorized(BUILDER), controller.upload)
|
.post(
|
||||||
.post("/api/plugin", authorized(BUILDER), controller.create)
|
"/api/plugin/upload",
|
||||||
.get("/api/plugin", authorized(BUILDER), controller.fetch)
|
authorized(permissions.BUILDER),
|
||||||
.delete("/api/plugin/:pluginId", authorized(BUILDER), controller.destroy)
|
controller.upload
|
||||||
|
)
|
||||||
|
.post("/api/plugin", authorized(permissions.BUILDER), controller.create)
|
||||||
|
.get("/api/plugin", authorized(permissions.BUILDER), controller.fetch)
|
||||||
|
.delete(
|
||||||
|
"/api/plugin/:pluginId",
|
||||||
|
authorized(permissions.BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
const Router = require("@koa/router")
|
import Router from "@koa/router"
|
||||||
const queryController = require("../controllers/query")
|
import * as queryController from "../controllers/query"
|
||||||
const authorized = require("../../middleware/authorized")
|
import authorized from "../../middleware/authorized"
|
||||||
const {
|
import { permissions } from "@budibase/backend-core"
|
||||||
PermissionLevel,
|
import {
|
||||||
PermissionType,
|
|
||||||
BUILDER,
|
|
||||||
} = require("@budibase/backend-core/permissions")
|
|
||||||
const {
|
|
||||||
bodyResource,
|
bodyResource,
|
||||||
bodySubResource,
|
bodySubResource,
|
||||||
paramResource,
|
paramResource,
|
||||||
} = require("../../middleware/resourceId")
|
} from "../../middleware/resourceId"
|
||||||
const {
|
import {
|
||||||
generateQueryPreviewValidation,
|
generateQueryPreviewValidation,
|
||||||
generateQueryValidation,
|
generateQueryValidation,
|
||||||
} = require("../controllers/query/validation")
|
} from "../controllers/query/validation"
|
||||||
|
const { BUILDER, PermissionType, PermissionLevel } = permissions
|
||||||
|
|
||||||
const router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/queries", authorized(BUILDER), queryController.fetch)
|
.get("/api/queries", authorized(BUILDER), queryController.fetch)
|
||||||
|
@ -48,17 +45,17 @@ router
|
||||||
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
|
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
|
||||||
queryController.executeV1
|
queryController.executeV1
|
||||||
)
|
)
|
||||||
.post(
|
|
||||||
"/api/v2/queries/:queryId",
|
|
||||||
paramResource("queryId"),
|
|
||||||
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
|
|
||||||
queryController.executeV2
|
|
||||||
)
|
|
||||||
.delete(
|
.delete(
|
||||||
"/api/queries/:queryId/:revId",
|
"/api/queries/:queryId/:revId",
|
||||||
paramResource("queryId"),
|
paramResource("queryId"),
|
||||||
authorized(BUILDER),
|
authorized(BUILDER),
|
||||||
queryController.destroy
|
queryController.destroy
|
||||||
)
|
)
|
||||||
|
.post(
|
||||||
|
"/api/v2/queries/:queryId",
|
||||||
|
paramResource("queryId"),
|
||||||
|
authorized(PermissionType.QUERY, PermissionLevel.WRITE),
|
||||||
|
queryController.executeV2 as any
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = router
|
export = router
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue