Merge branch 'develop' of github.com:Budibase/budibase into feature/test-image
This commit is contained in:
commit
e8667fd04d
|
@ -4,6 +4,9 @@ metadata:
|
||||||
annotations:
|
annotations:
|
||||||
kompose.cmd: kompose convert
|
kompose.cmd: kompose convert
|
||||||
kompose.version: 1.21.0 (992df58d8)
|
kompose.version: 1.21.0 (992df58d8)
|
||||||
|
{{ if .Values.globals.logAnnotations }}
|
||||||
|
{{ toYaml .Values.globals.logAnnotations | indent 4 }}
|
||||||
|
{{ end }}
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
io.kompose.service: app-service
|
io.kompose.service: app-service
|
||||||
|
@ -156,8 +159,24 @@ spec:
|
||||||
- name: ELASTIC_APM_SERVER_URL
|
- name: ELASTIC_APM_SERVER_URL
|
||||||
value: {{ .Values.globals.elasticApmServerUrl | quote }}
|
value: {{ .Values.globals.elasticApmServerUrl | quote }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if .Values.globals.globalAgentHttpProxy }}
|
||||||
|
- name: GLOBAL_AGENT_HTTP_PROXY
|
||||||
|
value: {{ .Values.globals.globalAgentHttpProxy | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.globalAgentHttpsProxy }}
|
||||||
|
- name: GLOBAL_AGENT_HTTPS_PROXY
|
||||||
|
value: {{ .Values.globals.globalAgentHttpsProxy | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.globalAgentNoProxy }}
|
||||||
|
- name: GLOBAL_AGENT_NO_PROXY
|
||||||
|
value: {{ .Values.globals.globalAgentNoProxy | quote }}
|
||||||
|
{{ end }}
|
||||||
- name: CDN_URL
|
- name: CDN_URL
|
||||||
value: {{ .Values.globals.cdnUrl }}
|
value: {{ .Values.globals.cdnUrl }}
|
||||||
|
{{ if .Values.services.tlsRejectUnauthorized }}
|
||||||
|
- name: NODE_TLS_REJECT_UNAUTHORIZED
|
||||||
|
value: {{ .Values.services.tlsRejectUnauthorized }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
image: budibase/apps:{{ .Values.globals.appVersion }}
|
image: budibase/apps:{{ .Values.globals.appVersion }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
|
|
@ -42,6 +42,7 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ template "budibase.fullname" . }}
|
name: {{ template "budibase.fullname" . }}
|
||||||
key: objectStoreSecret
|
key: objectStoreSecret
|
||||||
|
|
||||||
image: minio/minio
|
image: minio/minio
|
||||||
imagePullPolicy: ""
|
imagePullPolicy: ""
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
|
|
|
@ -4,6 +4,9 @@ metadata:
|
||||||
annotations:
|
annotations:
|
||||||
kompose.cmd: kompose convert
|
kompose.cmd: kompose convert
|
||||||
kompose.version: 1.21.0 (992df58d8)
|
kompose.version: 1.21.0 (992df58d8)
|
||||||
|
{{ if .Values.globals.logAnnotations }}
|
||||||
|
{{ toYaml .Values.globals.logAnnotations | indent 4 }}
|
||||||
|
{{ end }}
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: budibase-proxy
|
app.kubernetes.io/name: budibase-proxy
|
||||||
|
|
|
@ -60,5 +60,6 @@ spec:
|
||||||
- name: redis-data
|
- name: redis-data
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: redis-data
|
claimName: redis-data
|
||||||
|
|
||||||
status: {}
|
status: {}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
@ -4,6 +4,9 @@ metadata:
|
||||||
annotations:
|
annotations:
|
||||||
kompose.cmd: kompose convert
|
kompose.cmd: kompose convert
|
||||||
kompose.version: 1.21.0 (992df58d8)
|
kompose.version: 1.21.0 (992df58d8)
|
||||||
|
{{ if .Values.globals.logAnnotations }}
|
||||||
|
{{ toYaml .Values.globals.logAnnotations | indent 4 }}
|
||||||
|
{{ end }}
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
io.kompose.service: worker-service
|
io.kompose.service: worker-service
|
||||||
|
@ -147,8 +150,24 @@ spec:
|
||||||
- name: ELASTIC_APM_SERVER_URL
|
- name: ELASTIC_APM_SERVER_URL
|
||||||
value: {{ .Values.globals.elasticApmServerUrl | quote }}
|
value: {{ .Values.globals.elasticApmServerUrl | quote }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if .Values.globals.globalAgentHttpProxy }}
|
||||||
|
- name: GLOBAL_AGENT_HTTP_PROXY
|
||||||
|
value: {{ .Values.globals.globalAgentHttpProxy | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.globalAgentHttpsProxy }}
|
||||||
|
- name: GLOBAL_AGENT_HTTPS_PROXY
|
||||||
|
value: {{ .Values.globals.globalAgentHttpsProxy | quote }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.globals.globalAgentNoProxy }}
|
||||||
|
- name: GLOBAL_AGENT_NO_PROXY
|
||||||
|
value: {{ .Values.globals.globalAgentNoProxy | quote }}
|
||||||
|
{{ end }}
|
||||||
- name: CDN_URL
|
- name: CDN_URL
|
||||||
value: {{ .Values.globals.cdnUrl }}
|
value: {{ .Values.globals.cdnUrl }}
|
||||||
|
{{ if .Values.services.tlsRejectUnauthorized }}
|
||||||
|
- name: NODE_TLS_REJECT_UNAUTHORIZED
|
||||||
|
value: {{ .Values.services.tlsRejectUnauthorized }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
image: budibase/worker:{{ .Values.globals.appVersion }}
|
image: budibase/worker:{{ .Values.globals.appVersion }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
|
|
@ -22,6 +22,12 @@ serviceAccount:
|
||||||
|
|
||||||
podAnnotations: {}
|
podAnnotations: {}
|
||||||
|
|
||||||
|
# logAnnotations:
|
||||||
|
# co.elastic.logs/multiline.type: pattern
|
||||||
|
# co.elastic.logs/multiline.pattern: '^[[:space:]]'
|
||||||
|
# co.elastic.logs/multiline.negate: false
|
||||||
|
# co.elastic.logs/multiline.match: after
|
||||||
|
|
||||||
podSecurityContext:
|
podSecurityContext:
|
||||||
{}
|
{}
|
||||||
# fsGroup: 2000
|
# fsGroup: 2000
|
||||||
|
@ -106,10 +112,14 @@ globals:
|
||||||
# elasticApmEnabled:
|
# elasticApmEnabled:
|
||||||
# elasticApmSecretToken:
|
# elasticApmSecretToken:
|
||||||
# elasticApmServerUrl:
|
# elasticApmServerUrl:
|
||||||
|
# globalAgentHttpProxy:
|
||||||
|
# globalAgentHttpsProxy:
|
||||||
|
# globalAgentNoProxy:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
budibaseVersion: latest
|
budibaseVersion: latest
|
||||||
dns: cluster.local
|
dns: cluster.local
|
||||||
|
# tlsRejectUnauthorized: 0
|
||||||
|
|
||||||
proxy:
|
proxy:
|
||||||
port: 10000
|
port: 10000
|
||||||
|
|
|
@ -47,7 +47,8 @@ ADD hosting/single/nginx/nginx.conf /etc/nginx
|
||||||
ADD hosting/single/nginx/nginx-default-site.conf /etc/nginx/sites-enabled/default
|
ADD hosting/single/nginx/nginx-default-site.conf /etc/nginx/sites-enabled/default
|
||||||
RUN mkdir -p /var/log/nginx && \
|
RUN mkdir -p /var/log/nginx && \
|
||||||
touch /var/log/nginx/error.log && \
|
touch /var/log/nginx/error.log && \
|
||||||
touch /var/run/nginx.pid
|
touch /var/run/nginx.pid && \
|
||||||
|
usermod -a -G tty www-data
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
RUN mkdir -p scripts/integrations/oracle
|
RUN mkdir -p scripts/integrations/oracle
|
||||||
|
|
|
@ -2,7 +2,8 @@ server {
|
||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
listen [::]:80 default_server;
|
listen [::]:80 default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
error_log /dev/stderr warn;
|
||||||
|
access_log /dev/stdout main;
|
||||||
client_max_body_size 1000m;
|
client_max_body_size 1000m;
|
||||||
ignore_invalid_headers off;
|
ignore_invalid_headers off;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
user www-data www-data;
|
user www-data www-data;
|
||||||
error_log /var/log/nginx/error.log;
|
error_log /dev/stderr warn;
|
||||||
pid /var/run/nginx.pid;
|
pid /var/run/nginx.pid;
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
worker_rlimit_nofile 8192;
|
worker_rlimit_nofile 8192;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.1.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "2.1.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"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",
|
||||||
|
@ -16,11 +16,11 @@
|
||||||
"prepack": "cp package.json dist",
|
"prepack": "cp package.json dist",
|
||||||
"build": "tsc -p tsconfig.build.json",
|
"build": "tsc -p tsconfig.build.json",
|
||||||
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
||||||
"test": "jest --coverage",
|
"test": "jest --coverage --maxWorkers=2",
|
||||||
"test:watch": "jest --watchAll"
|
"test:watch": "jest --watchAll"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/types": "2.1.40-alpha.7",
|
"@budibase/types": "2.1.46-alpha.1",
|
||||||
"@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",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { ssoCallbackUrl } from "./utils"
|
import { ssoCallbackUrl } from "./utils"
|
||||||
import { authenticateThirdParty } from "./third-party-common"
|
import { authenticateThirdParty, SaveUserFunction } from "./third-party-common"
|
||||||
import { ConfigType, GoogleConfig, Database, SSOProfile } from "@budibase/types"
|
import { ConfigType, GoogleConfig, Database, SSOProfile } from "@budibase/types"
|
||||||
const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy
|
const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy
|
||||||
|
|
||||||
export function buildVerifyFn(saveUserFn?: Function) {
|
export function buildVerifyFn(saveUserFn?: SaveUserFunction) {
|
||||||
return (
|
return (
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
refreshToken: string,
|
refreshToken: string,
|
||||||
|
@ -39,7 +39,7 @@ export function buildVerifyFn(saveUserFn?: Function) {
|
||||||
export async function strategyFactory(
|
export async function strategyFactory(
|
||||||
config: GoogleConfig["config"],
|
config: GoogleConfig["config"],
|
||||||
callbackUrl: string,
|
callbackUrl: string,
|
||||||
saveUserFn?: Function
|
saveUserFn?: SaveUserFunction
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const { clientID, clientSecret } = config
|
const { clientID, clientSecret } = config
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
import { authenticateThirdParty } from "./third-party-common"
|
import { authenticateThirdParty, SaveUserFunction } from "./third-party-common"
|
||||||
import { ssoCallbackUrl } from "./utils"
|
import { ssoCallbackUrl } from "./utils"
|
||||||
import {
|
import {
|
||||||
Config,
|
Config,
|
||||||
|
@ -17,7 +17,7 @@ type JwtClaims = {
|
||||||
email: string
|
email: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildVerifyFn(saveUserFn?: Function) {
|
export function buildVerifyFn(saveUserFn?: SaveUserFunction) {
|
||||||
/**
|
/**
|
||||||
* @param {*} issuer The identity provider base URL
|
* @param {*} issuer The identity provider base URL
|
||||||
* @param {*} sub The user ID
|
* @param {*} sub The user ID
|
||||||
|
@ -106,7 +106,7 @@ function validEmail(value: string) {
|
||||||
*/
|
*/
|
||||||
export async function strategyFactory(
|
export async function strategyFactory(
|
||||||
config: OIDCConfiguration,
|
config: OIDCConfiguration,
|
||||||
saveUserFn?: Function
|
saveUserFn?: SaveUserFunction
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const verify = buildVerifyFn(saveUserFn)
|
const verify = buildVerifyFn(saveUserFn)
|
||||||
|
|
|
@ -9,6 +9,17 @@ import fetch from "node-fetch"
|
||||||
import { ThirdPartyUser } from "@budibase/types"
|
import { ThirdPartyUser } from "@budibase/types"
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
|
|
||||||
|
type SaveUserOpts = {
|
||||||
|
requirePassword?: boolean
|
||||||
|
hashPassword?: boolean
|
||||||
|
currentUserId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SaveUserFunction = (
|
||||||
|
user: ThirdPartyUser,
|
||||||
|
opts: SaveUserOpts
|
||||||
|
) => Promise<any>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common authentication logic for third parties. e.g. OAuth, OIDC.
|
* Common authentication logic for third parties. e.g. OAuth, OIDC.
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +27,7 @@ export async function authenticateThirdParty(
|
||||||
thirdPartyUser: ThirdPartyUser,
|
thirdPartyUser: ThirdPartyUser,
|
||||||
requireLocalAccount: boolean = true,
|
requireLocalAccount: boolean = true,
|
||||||
done: Function,
|
done: Function,
|
||||||
saveUserFn?: Function
|
saveUserFn?: SaveUserFunction
|
||||||
) {
|
) {
|
||||||
if (!saveUserFn) {
|
if (!saveUserFn) {
|
||||||
throw new Error("Save user function must be provided")
|
throw new Error("Save user function must be provided")
|
||||||
|
@ -81,7 +92,7 @@ export async function authenticateThirdParty(
|
||||||
|
|
||||||
// create or sync the user
|
// create or sync the user
|
||||||
try {
|
try {
|
||||||
await saveUserFn(dbUser, false, false)
|
await saveUserFn(dbUser, { hashPassword: false, requirePassword: false })
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return authError(done, err)
|
return authError(done, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1521,13 +1521,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||||
integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
|
integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
|
||||||
|
|
||||||
async@~2.1.4:
|
|
||||||
version "2.1.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
|
|
||||||
integrity sha512-+g/Ncjbx0JSq2Mk03WQkyKvNh5q9Qvyo/RIqIqnmC5feJY70PNl2ESwZU2BhAB+AZPkHNzzyC2Dq2AS5VnTKhQ==
|
|
||||||
dependencies:
|
|
||||||
lodash "^4.14.0"
|
|
||||||
|
|
||||||
asynckit@^0.4.0:
|
asynckit@^0.4.0:
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
|
@ -2669,32 +2662,6 @@ globals@^11.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||||
|
|
||||||
google-auth-library@~0.10.0:
|
|
||||||
version "0.10.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e"
|
|
||||||
integrity sha512-KM54Y9GhdAzfXUHmWEoYmaOykSLuMG7W4HvVLYqyogxOyE6px8oSS8W13ngqW0oDGZ915GFW3V6OM6+qcdvPOA==
|
|
||||||
dependencies:
|
|
||||||
gtoken "^1.2.1"
|
|
||||||
jws "^3.1.4"
|
|
||||||
lodash.noop "^3.0.1"
|
|
||||||
request "^2.74.0"
|
|
||||||
|
|
||||||
google-p12-pem@^0.1.0:
|
|
||||||
version "0.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177"
|
|
||||||
integrity sha512-puhMlJ2+E/rgvxWaqgN/nC7x623OAE8MR9vBUqxF0inCE7HoVfCHvTeQ9+BR+rj9KM0fIg6XV6tmbt7XHHssoQ==
|
|
||||||
dependencies:
|
|
||||||
node-forge "^0.7.1"
|
|
||||||
|
|
||||||
googleapis@^16.0.0:
|
|
||||||
version "16.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-16.1.0.tgz#0f19f2d70572d918881a0f626e3b1a2fa8629576"
|
|
||||||
integrity sha512-5czmF7xkIlJKc1+/+5tltrI1skoR3HKtkDOld9rk+DOucTpZRjOhCoJzoSjxB3M8rP2tEb1VIr1TPyzR3V2PUQ==
|
|
||||||
dependencies:
|
|
||||||
async "~2.1.4"
|
|
||||||
google-auth-library "~0.10.0"
|
|
||||||
string-template "~1.0.0"
|
|
||||||
|
|
||||||
got@^9.6.0:
|
got@^9.6.0:
|
||||||
version "9.6.0"
|
version "9.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
|
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
|
||||||
|
@ -2717,16 +2684,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.9:
|
||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||||
|
|
||||||
gtoken@^1.2.1:
|
|
||||||
version "1.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8"
|
|
||||||
integrity sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w==
|
|
||||||
dependencies:
|
|
||||||
google-p12-pem "^0.1.0"
|
|
||||||
jws "^3.0.0"
|
|
||||||
mime "^1.4.1"
|
|
||||||
request "^2.72.0"
|
|
||||||
|
|
||||||
har-schema@^2.0.0:
|
har-schema@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||||
|
@ -3609,7 +3566,7 @@ jwa@^1.4.1:
|
||||||
ecdsa-sig-formatter "1.0.11"
|
ecdsa-sig-formatter "1.0.11"
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
jws@^3.0.0, jws@^3.1.4, jws@^3.2.2:
|
jws@^3.2.2:
|
||||||
version "3.2.2"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||||
|
@ -3855,11 +3812,6 @@ lodash.memoize@4.x:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||||
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
|
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
|
||||||
|
|
||||||
lodash.noop@^3.0.1:
|
|
||||||
version "3.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c"
|
|
||||||
integrity sha512-TmYdmu/pebrdTIBDK/FDx9Bmfzs9x0sZG6QIJuMDTqEPfeciLcN13ij+cOd0i9vwJfBtbG9UQ+C7MkXgYxrIJg==
|
|
||||||
|
|
||||||
lodash.once@^4.0.0:
|
lodash.once@^4.0.0:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||||
|
@ -3870,7 +3822,7 @@ lodash.pick@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
|
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
|
||||||
integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==
|
integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==
|
||||||
|
|
||||||
lodash@4.17.21, lodash@^4.14.0, lodash@^4.17.21:
|
lodash@4.17.21, lodash@^4.17.21:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
@ -3982,7 +3934,7 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24,
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-db "1.52.0"
|
mime-db "1.52.0"
|
||||||
|
|
||||||
mime@^1.3.4, mime@^1.4.1:
|
mime@^1.3.4:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||||
|
@ -4129,11 +4081,6 @@ node-fetch@2.6.7, node-fetch@^2.6.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-url "^5.0.0"
|
whatwg-url "^5.0.0"
|
||||||
|
|
||||||
node-forge@^0.7.1:
|
|
||||||
version "0.7.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
|
|
||||||
integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
|
|
||||||
|
|
||||||
node-gyp-build-optional-packages@5.0.3:
|
node-gyp-build-optional-packages@5.0.3:
|
||||||
version "5.0.3"
|
version "5.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
|
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
|
||||||
|
@ -4356,14 +4303,6 @@ parseurl@^1.3.2, parseurl@^1.3.3:
|
||||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||||
|
|
||||||
passport-google-auth@1.0.2:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938"
|
|
||||||
integrity sha512-cfAqna6jZLyMEwUdd4PIwAh2mQKQVEDAaRIaom1pG6h4x4Gwjllf/Jflt3TkR1Sen5Rkvr3l7kSXCWE1EKkh8g==
|
|
||||||
dependencies:
|
|
||||||
googleapis "^16.0.0"
|
|
||||||
passport-strategy "1.x"
|
|
||||||
|
|
||||||
passport-google-oauth1@1.x.x:
|
passport-google-oauth1@1.x.x:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz#af74a803df51ec646f66a44d82282be6f108e0cc"
|
resolved "https://registry.yarnpkg.com/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz#af74a803df51ec646f66a44d82282be6f108e0cc"
|
||||||
|
@ -4426,7 +4365,7 @@ passport-oauth2@1.x.x:
|
||||||
uid2 "0.0.x"
|
uid2 "0.0.x"
|
||||||
utils-merge "1.x.x"
|
utils-merge "1.x.x"
|
||||||
|
|
||||||
passport-strategy@1.x, passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
passport-strategy@1.x.x, passport-strategy@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||||
integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==
|
integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==
|
||||||
|
@ -4938,7 +4877,7 @@ remove-trailing-slash@^0.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d"
|
resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d"
|
||||||
integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==
|
integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA==
|
||||||
|
|
||||||
request@^2.72.0, request@^2.74.0, request@^2.88.0:
|
request@^2.88.0:
|
||||||
version "2.88.2"
|
version "2.88.2"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||||
|
@ -5204,11 +5143,6 @@ string-length@^4.0.1:
|
||||||
char-regex "^1.0.2"
|
char-regex "^1.0.2"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
string-template@~1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96"
|
|
||||||
integrity sha512-SLqR3GBUXuoPP5MmYtD7ompvXiG87QjT6lzOszyXjTM86Uu7At7vNnt2xgyTLq5o9T4IxTYFyGxcULqpsmsfdg==
|
|
||||||
|
|
||||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
|
|
|
@ -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.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"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.40-alpha.7",
|
"@budibase/string-templates": "2.1.46-alpha.1",
|
||||||
"@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",
|
||||||
|
@ -67,19 +67,19 @@
|
||||||
"@spectrum-css/search": "^3.0.2",
|
"@spectrum-css/search": "^3.0.2",
|
||||||
"@spectrum-css/sidenav": "^3.0.2",
|
"@spectrum-css/sidenav": "^3.0.2",
|
||||||
"@spectrum-css/slider": "3.0.1",
|
"@spectrum-css/slider": "3.0.1",
|
||||||
"@spectrum-css/statuslight": "^3.0.2",
|
"@spectrum-css/statuslight": "3.0.2",
|
||||||
"@spectrum-css/stepper": "^3.0.3",
|
"@spectrum-css/stepper": "3.0.3",
|
||||||
"@spectrum-css/switch": "^1.0.2",
|
"@spectrum-css/switch": "1.0.2",
|
||||||
"@spectrum-css/table": "^3.0.1",
|
"@spectrum-css/table": "3.0.1",
|
||||||
"@spectrum-css/tabs": "^3.2.12",
|
"@spectrum-css/tabs": "3.2.12",
|
||||||
"@spectrum-css/tags": "^3.0.2",
|
"@spectrum-css/tags": "3.0.2",
|
||||||
"@spectrum-css/textfield": "^3.0.1",
|
"@spectrum-css/textfield": "3.0.1",
|
||||||
"@spectrum-css/toast": "^3.0.1",
|
"@spectrum-css/toast": "3.0.1",
|
||||||
"@spectrum-css/tooltip": "^3.0.3",
|
"@spectrum-css/tooltip": "3.0.3",
|
||||||
"@spectrum-css/treeview": "^3.0.2",
|
"@spectrum-css/treeview": "3.0.2",
|
||||||
"@spectrum-css/typography": "^3.0.1",
|
"@spectrum-css/typography": "3.0.1",
|
||||||
"@spectrum-css/underlay": "^2.0.9",
|
"@spectrum-css/underlay": "2.0.9",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "3.0.1",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.4",
|
||||||
"easymde": "^2.16.1",
|
"easymde": "^2.16.1",
|
||||||
"svelte-flatpickr": "^3.2.3",
|
"svelte-flatpickr": "^3.2.3",
|
||||||
|
|
|
@ -205,7 +205,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.spectrum-Popover.auto-width :global(.spectrum-Menu-itemLabel) {
|
.spectrum-Popover.auto-width :global(.spectrum-Menu-itemLabel) {
|
||||||
|
max-width: 400px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
.spectrum-Picker {
|
.spectrum-Picker {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -28,9 +28,9 @@
|
||||||
let loading = false
|
let loading = false
|
||||||
$: confirmDisabled = disabled || loading
|
$: confirmDisabled = disabled || loading
|
||||||
|
|
||||||
async function secondary() {
|
async function secondary(e) {
|
||||||
loading = true
|
loading = true
|
||||||
if (!secondaryAction || (await secondaryAction()) !== false) {
|
if (!secondaryAction || (await secondaryAction(e)) !== false) {
|
||||||
hide()
|
hide()
|
||||||
}
|
}
|
||||||
loading = false
|
loading = false
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
background-color: var(--spectrum-global-color-gray-300) !important;
|
background-color: var(--spectrum-global-color-gray-200) !important;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -31,8 +31,8 @@
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
90deg,
|
90deg,
|
||||||
rgba(255, 255, 255, 0) 0,
|
rgba(255, 255, 255, 0) 0,
|
||||||
rgba(255, 255, 255, 0.2) 20%,
|
rgba(255, 255, 255, 0.15) 20%,
|
||||||
rgba(255, 255, 255, 0.5) 60%,
|
rgba(255, 255, 255, 0.3) 60%,
|
||||||
rgba(255, 255, 255, 0)
|
rgba(255, 255, 255, 0)
|
||||||
);
|
);
|
||||||
animation: shimmer 2s infinite;
|
animation: shimmer 2s infinite;
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
opacity: 1;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,20 +2,20 @@ import filterTests from "../../support/filterTests"
|
||||||
const interact = require('../../support/interact')
|
const interact = require('../../support/interact')
|
||||||
|
|
||||||
filterTests(["smoke", "all"], () => {
|
filterTests(["smoke", "all"], () => {
|
||||||
context("User Management", () => {
|
xcontext("User Management", () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.login()
|
cy.login()
|
||||||
cy.deleteApp("Cypress Tests")
|
cy.deleteApp("Cypress Tests")
|
||||||
cy.createApp("Cypress Tests", false)
|
cy.createApp("Cypress Tests", false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create a user via basic onboarding", () => {
|
xit("should create a user via basic onboarding", () => {
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000})
|
cy.visit(`${Cypress.config().baseUrl}/builder`, { timeout: 5000})
|
||||||
cy.createUser("bbuser@test.com")
|
cy.createUser("bbuser@test.com")
|
||||||
cy.get(interact.SPECTRUM_TABLE).should("contain", "bbuser")
|
cy.get(interact.SPECTRUM_TABLE).should("contain", "bbuser")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should confirm App User role for a New User", () => {
|
xit("should confirm App User role for a New User", () => {
|
||||||
cy.contains("bbuser").click()
|
cy.contains("bbuser").click()
|
||||||
cy.get(".spectrum-Form-itemField").eq(3).should('contain', 'App User')
|
cy.get(".spectrum-Form-itemField").eq(3).should('contain', 'App User')
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ filterTests(["smoke", "all"], () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Should edit user details within user details page", () => {
|
xit("Should edit user details within user details page", () => {
|
||||||
// Add First name
|
// Add First name
|
||||||
cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => {
|
cy.get(interact.FIELD, { timeout: 1000 }).eq(1).within(() => {
|
||||||
cy.wait(500)
|
cy.wait(500)
|
||||||
|
@ -190,7 +190,7 @@ filterTests(["smoke", "all"], () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should reset the users password", () => {
|
xit("should reset the users password", () => {
|
||||||
cy.get(".title").within(() => {
|
cy.get(".title").within(() => {
|
||||||
cy.get(interact.SPECTRUM_ICON).click({ force: true })
|
cy.get(interact.SPECTRUM_ICON).click({ force: true })
|
||||||
})
|
})
|
||||||
|
@ -230,7 +230,7 @@ filterTests(["smoke", "all"], () => {
|
||||||
cy.login()
|
cy.login()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should delete a user", () => {
|
xit("should delete a user", () => {
|
||||||
cy.deleteUser("bbuser@test.com")
|
cy.deleteUser("bbuser@test.com")
|
||||||
cy.get(interact.SPECTRUM_TABLE, { timeout: 4000 }).should("not.have.text", "bbuser")
|
cy.get(interact.SPECTRUM_TABLE, { timeout: 4000 }).should("not.have.text", "bbuser")
|
||||||
})
|
})
|
||||||
|
|
|
@ -140,7 +140,8 @@ filterTests(["all"], () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
cy.visit(`${Cypress.config().baseUrl}/builder`)
|
||||||
cy.get(".appTable .app-row-actions button")
|
cy.wait(1000)
|
||||||
|
cy.get(".appTable .app-row-actions button", { timeout: 10000 })
|
||||||
.contains("Manage")
|
.contains("Manage")
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "2.1.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -71,10 +71,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.1.40-alpha.7",
|
"@budibase/bbui": "2.1.46-alpha.1",
|
||||||
"@budibase/client": "2.1.40-alpha.7",
|
"@budibase/client": "2.1.46-alpha.1",
|
||||||
"@budibase/frontend-core": "2.1.40-alpha.7",
|
"@budibase/frontend-core": "2.1.46-alpha.1",
|
||||||
"@budibase/string-templates": "2.1.40-alpha.7",
|
"@budibase/string-templates": "2.1.46-alpha.1",
|
||||||
"@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",
|
||||||
|
|
|
@ -481,6 +481,7 @@ const getSelectedRowsBindings = asset => {
|
||||||
block._id + "-table"
|
block._id + "-table"
|
||||||
)}.${makePropSafe("selectedRows")}`,
|
)}.${makePropSafe("selectedRows")}`,
|
||||||
readableBinding: `${block._instanceName}.Selected rows`,
|
readableBinding: `${block._instanceName}.Selected rows`,
|
||||||
|
category: "Selected rows",
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1004,7 +1005,10 @@ const bindingReplacement = (
|
||||||
* {{ literal [componentId] }}
|
* {{ literal [componentId] }}
|
||||||
*/
|
*/
|
||||||
const extractLiteralHandlebarsID = value => {
|
const extractLiteralHandlebarsID = value => {
|
||||||
return value?.match(/{{\s*literal\s*\[+([^\]]+)].*}}/)?.[1]
|
if (!value || typeof value !== "string") {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return value.match(/{{\s*literal\s*\[+([^\]]+)].*}}/)?.[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
DB_TYPE_INTERNAL,
|
DB_TYPE_INTERNAL,
|
||||||
DB_TYPE_EXTERNAL,
|
DB_TYPE_EXTERNAL,
|
||||||
} from "constants/backend"
|
} from "constants/backend"
|
||||||
|
import { getSchemaForDatasource } from "builderStore/dataBinding"
|
||||||
|
|
||||||
const INITIAL_FRONTEND_STATE = {
|
const INITIAL_FRONTEND_STATE = {
|
||||||
apps: [],
|
apps: [],
|
||||||
|
@ -524,7 +525,9 @@ export const getFrontendStore = () => {
|
||||||
// Generate default props
|
// Generate default props
|
||||||
let props = { ...presetProps }
|
let props = { ...presetProps }
|
||||||
settings.forEach(setting => {
|
settings.forEach(setting => {
|
||||||
if (setting.defaultValue !== undefined) {
|
if (setting.type === "multifield" && setting.selectAllFields) {
|
||||||
|
props[setting.key] = Object.keys(defaultDatasource.schema || {})
|
||||||
|
} else if (setting.defaultValue !== undefined) {
|
||||||
props[setting.key] = setting.defaultValue
|
props[setting.key] = setting.defaultValue
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1041,6 +1044,27 @@ export const getFrontendStore = () => {
|
||||||
if (component[name] === value) {
|
if (component[name] === value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settings = getComponentSettings(component._component)
|
||||||
|
const updatedSetting = settings.find(setting => setting.key === name)
|
||||||
|
|
||||||
|
if (
|
||||||
|
updatedSetting?.type === "dataSource" ||
|
||||||
|
updatedSetting?.type === "table"
|
||||||
|
) {
|
||||||
|
const { schema } = getSchemaForDatasource(null, value)
|
||||||
|
const columnNames = Object.keys(schema || {})
|
||||||
|
const multifieldKeysToSelectAll = settings
|
||||||
|
.filter(setting => {
|
||||||
|
return setting.type === "multifield" && setting.selectAllFields
|
||||||
|
})
|
||||||
|
.map(setting => setting.key)
|
||||||
|
|
||||||
|
multifieldKeysToSelectAll.forEach(key => {
|
||||||
|
component[key] = columnNames
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
component[name] = value
|
component[name] = value
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,13 +38,15 @@
|
||||||
export let testData
|
export let testData
|
||||||
export let schemaProperties
|
export let schemaProperties
|
||||||
export let isTestModal = false
|
export let isTestModal = false
|
||||||
|
|
||||||
let webhookModal
|
let webhookModal
|
||||||
let drawer
|
let drawer
|
||||||
let tempFilters = lookForFilters(schemaProperties) || []
|
|
||||||
let fillWidth = true
|
let fillWidth = true
|
||||||
let codeBindingOpen = false
|
let codeBindingOpen = false
|
||||||
let inputData
|
let inputData
|
||||||
|
|
||||||
|
$: filters = lookForFilters(schemaProperties) || []
|
||||||
|
$: tempFilters = filters
|
||||||
$: stepId = block.stepId
|
$: stepId = block.stepId
|
||||||
$: bindings = getAvailableBindings(
|
$: bindings = getAvailableBindings(
|
||||||
block || $automationStore.selectedBlock,
|
block || $automationStore.selectedBlock,
|
||||||
|
@ -222,16 +224,17 @@
|
||||||
{:else if value.customType === "filters"}
|
{:else if value.customType === "filters"}
|
||||||
<ActionButton on:click={drawer.show}>Define filters</ActionButton>
|
<ActionButton on:click={drawer.show}>Define filters</ActionButton>
|
||||||
<Drawer bind:this={drawer} {fillWidth} title="Filtering">
|
<Drawer bind:this={drawer} {fillWidth} title="Filtering">
|
||||||
<Button cta slot="buttons" on:click={() => saveFilters(key)}
|
<Button cta slot="buttons" on:click={() => saveFilters(key)}>
|
||||||
>Save</Button
|
Save
|
||||||
>
|
</Button>
|
||||||
<FilterDrawer
|
<FilterDrawer
|
||||||
slot="body"
|
slot="body"
|
||||||
bind:filters={tempFilters}
|
{filters}
|
||||||
{bindings}
|
{bindings}
|
||||||
{schemaFields}
|
{schemaFields}
|
||||||
panel={AutomationBindingPanel}
|
panel={AutomationBindingPanel}
|
||||||
fillWidth
|
fillWidth
|
||||||
|
on:change={e => (tempFilters = e.detail)}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
{:else if value.customType === "password"}
|
{:else if value.customType === "password"}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
|
||||||
let hideAutocolumns = true
|
let hideAutocolumns = true
|
||||||
|
let filters
|
||||||
|
|
||||||
$: isUsersTable = $tables.selected?._id === TableNames.USERS
|
$: isUsersTable = $tables.selected?._id === TableNames.USERS
|
||||||
$: type = $tables.selected?.type
|
$: type = $tables.selected?.type
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
$: hasCols = checkHasCols(schema)
|
$: hasCols = checkHasCols(schema)
|
||||||
$: hasRows = !!$fetch.rows?.length
|
$: hasRows = !!$fetch.rows?.length
|
||||||
$: showError($fetch.error)
|
$: showError($fetch.error)
|
||||||
|
$: id, (filters = null)
|
||||||
|
|
||||||
const showError = error => {
|
const showError = error => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -102,8 +104,9 @@
|
||||||
|
|
||||||
// Fetch data whenever filters change
|
// Fetch data whenever filters change
|
||||||
const onFilter = e => {
|
const onFilter = e => {
|
||||||
|
filters = e.detail
|
||||||
fetch.update({
|
fetch.update({
|
||||||
filter: e.detail,
|
filter: filters,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +120,12 @@
|
||||||
const onUpdateRows = () => {
|
const onUpdateRows = () => {
|
||||||
fetch.refresh()
|
fetch.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When importing new rows it is better to reinitialise request/paging data.
|
||||||
|
// Not doing so causes inconsistency in paging behaviour and content.
|
||||||
|
const onImportData = () => {
|
||||||
|
fetch.getInitialData()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -169,7 +178,7 @@
|
||||||
<ImportButton
|
<ImportButton
|
||||||
disabled={$tables.selected?._id === "ta_users"}
|
disabled={$tables.selected?._id === "ta_users"}
|
||||||
tableId={$tables.selected?._id}
|
tableId={$tables.selected?._id}
|
||||||
on:updaterows={onUpdateRows}
|
on:importrows={onImportData}
|
||||||
/>
|
/>
|
||||||
<ExportButton
|
<ExportButton
|
||||||
disabled={!hasRows || !hasCols}
|
disabled={!hasRows || !hasCols}
|
||||||
|
@ -178,6 +187,7 @@
|
||||||
{#key id}
|
{#key id}
|
||||||
<TableFilterButton
|
<TableFilterButton
|
||||||
{schema}
|
{schema}
|
||||||
|
{filters}
|
||||||
on:change={onFilter}
|
on:change={onFilter}
|
||||||
disabled={!hasCols}
|
disabled={!hasCols}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import { Table, Modal, Heading, notifications, Layout } from "@budibase/bbui"
|
import { Table, Modal, Heading, notifications, Layout } from "@budibase/bbui"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import Spinner from "components/common/Spinner.svelte"
|
import Spinner from "components/common/Spinner.svelte"
|
||||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import DeleteRowsButton from "./buttons/DeleteRowsButton.svelte"
|
import DeleteRowsButton from "./buttons/DeleteRowsButton.svelte"
|
||||||
import CreateEditRow from "./modals/CreateEditRow.svelte"
|
import CreateEditRow from "./modals/CreateEditRow.svelte"
|
||||||
import CreateEditUser from "./modals/CreateEditUser.svelte"
|
import CreateEditUser from "./modals/CreateEditUser.svelte"
|
||||||
|
@ -34,8 +35,8 @@
|
||||||
let editRowModal
|
let editRowModal
|
||||||
let editColumnModal
|
let editColumnModal
|
||||||
let customRenderers = []
|
let customRenderers = []
|
||||||
|
let confirmDelete
|
||||||
|
|
||||||
$: isInternal = type !== "external"
|
|
||||||
$: isUsersTable = tableId === TableNames.USERS
|
$: isUsersTable = tableId === TableNames.USERS
|
||||||
$: data && resetSelectedRows()
|
$: data && resetSelectedRows()
|
||||||
$: editRowComponent = isUsersTable ? CreateEditUser : CreateEditRow
|
$: editRowComponent = isUsersTable ? CreateEditUser : CreateEditRow
|
||||||
|
@ -89,15 +90,17 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRows = async () => {
|
const deleteRows = async targetRows => {
|
||||||
try {
|
try {
|
||||||
await API.deleteRows({
|
await API.deleteRows({
|
||||||
tableId,
|
tableId,
|
||||||
rows: selectedRows,
|
rows: targetRows,
|
||||||
})
|
})
|
||||||
data = data.filter(row => !selectedRows.includes(row))
|
|
||||||
notifications.success(`Successfully deleted ${selectedRows.length} rows`)
|
const deletedRowIds = targetRows.map(row => row._id)
|
||||||
selectedRows = []
|
data = data.filter(row => deletedRowIds.indexOf(row._id))
|
||||||
|
|
||||||
|
notifications.success(`Successfully deleted ${targetRows.length} rows`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error deleting rows")
|
notifications.error("Error deleting rows")
|
||||||
}
|
}
|
||||||
|
@ -133,7 +136,14 @@
|
||||||
<div class="popovers">
|
<div class="popovers">
|
||||||
<slot />
|
<slot />
|
||||||
{#if !isUsersTable && selectedRows.length > 0}
|
{#if !isUsersTable && selectedRows.length > 0}
|
||||||
<DeleteRowsButton on:updaterows {selectedRows} {deleteRows} />
|
<DeleteRowsButton
|
||||||
|
on:updaterows
|
||||||
|
{selectedRows}
|
||||||
|
deleteRows={async rows => {
|
||||||
|
await deleteRows(rows)
|
||||||
|
resetSelectedRows()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -164,8 +174,33 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<Modal bind:this={editRowModal}>
|
<Modal bind:this={editRowModal}>
|
||||||
<svelte:component this={editRowComponent} on:updaterows row={editableRow} />
|
<svelte:component
|
||||||
|
this={editRowComponent}
|
||||||
|
on:updaterows
|
||||||
|
on:deleteRows={() => {
|
||||||
|
confirmDelete.show()
|
||||||
|
}}
|
||||||
|
row={editableRow}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
bind:this={confirmDelete}
|
||||||
|
okText="Delete"
|
||||||
|
onOk={async () => {
|
||||||
|
if (editableRow) {
|
||||||
|
await deleteRows([editableRow])
|
||||||
|
}
|
||||||
|
editableRow = undefined
|
||||||
|
}}
|
||||||
|
onCancel={async () => {
|
||||||
|
editRow(editableRow)
|
||||||
|
}}
|
||||||
|
title="Confirm Deletion"
|
||||||
|
>
|
||||||
|
Are you sure you want to delete this row?
|
||||||
|
</ConfirmDialog>
|
||||||
|
|
||||||
<Modal bind:this={editColumnModal}>
|
<Modal bind:this={editColumnModal}>
|
||||||
<CreateEditColumn
|
<CreateEditColumn
|
||||||
field={editableColumn}
|
field={editableColumn}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
let modal
|
let modal
|
||||||
|
|
||||||
async function confirmDeletion() {
|
async function confirmDeletion() {
|
||||||
await deleteRows()
|
await deleteRows(selectedRows)
|
||||||
modal?.hide()
|
modal?.hide()
|
||||||
dispatch("updaterows")
|
dispatch("updaterows")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,5 +12,5 @@
|
||||||
Import
|
Import
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
<ImportModal {tableId} on:updaterows />
|
<ImportModal {tableId} on:importrows />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
let modal
|
|
||||||
let tempValue = filters || []
|
|
||||||
|
|
||||||
|
let modal
|
||||||
|
|
||||||
|
$: tempValue = filters || []
|
||||||
$: schemaFields = Object.values(schema || {})
|
$: schemaFields = Object.values(schema || {})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -34,8 +35,9 @@
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<FilterDrawer
|
<FilterDrawer
|
||||||
allowBindings={false}
|
allowBindings={false}
|
||||||
bind:filters={tempValue}
|
{filters}
|
||||||
{schemaFields}
|
{schemaFields}
|
||||||
|
on:change={e => (tempValue = e.detail)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
|
@ -50,22 +50,34 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent
|
<span class="modal-wrap">
|
||||||
title={creating ? "Create Row" : "Edit Row"}
|
<ModalContent
|
||||||
confirmText={creating ? "Create Row" : "Save Row"}
|
title={creating ? "Create Row" : "Edit Row"}
|
||||||
onConfirm={saveRow}
|
confirmText={creating ? "Create Row" : "Save Row"}
|
||||||
>
|
onConfirm={saveRow}
|
||||||
{#each tableSchema as [key, meta]}
|
showCancelButton={creating}
|
||||||
{#if !meta.autocolumn && meta.type !== FORMULA_TYPE}
|
showSecondaryButton={!creating}
|
||||||
<div>
|
secondaryButtonWarning={!creating}
|
||||||
<RowFieldControl error={errors[key]} {meta} bind:value={row[key]} />
|
secondaryButtonText="Delete"
|
||||||
</div>
|
secondaryAction={() => {
|
||||||
{/if}
|
dispatch("deleteRows", row)
|
||||||
{/each}
|
}}
|
||||||
</ModalContent>
|
>
|
||||||
|
{#each tableSchema as [key, meta]}
|
||||||
|
{#if !meta.autocolumn && meta.type !== FORMULA_TYPE}
|
||||||
|
<div>
|
||||||
|
<RowFieldControl error={errors[key]} {meta} bind:value={row[key]} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</ModalContent>
|
||||||
|
</span>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
.modal-wrap :global(.secondary-action) {
|
||||||
|
margin-right: unset;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always refresh rows just to be sure
|
// Always refresh rows just to be sure
|
||||||
dispatch("updaterows")
|
dispatch("importrows")
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,23 @@
|
||||||
|
|
||||||
const touched = writable({})
|
const touched = writable({})
|
||||||
|
|
||||||
|
function invalidThroughTable({ through, throughTo, throughFrom }) {
|
||||||
|
// need to know the foreign key columns to check error
|
||||||
|
if (!through || !throughTo || !throughFrom) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const throughTable = plusTables.find(tbl => tbl._id === through)
|
||||||
|
const otherColumns = Object.values(throughTable.schema).filter(
|
||||||
|
col => col.name !== throughFrom && col.name !== throughTo
|
||||||
|
)
|
||||||
|
for (let col of otherColumns) {
|
||||||
|
if (col.constraints?.presence && !col.autocolumn) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function checkForErrors(fromRelate, toRelate) {
|
function checkForErrors(fromRelate, toRelate) {
|
||||||
const isMany =
|
const isMany =
|
||||||
fromRelate.relationshipType === RelationshipTypes.MANY_TO_MANY
|
fromRelate.relationshipType === RelationshipTypes.MANY_TO_MANY
|
||||||
|
@ -59,6 +76,10 @@
|
||||||
if ($touched.through && isMany && !fromRelate.through) {
|
if ($touched.through && isMany && !fromRelate.through) {
|
||||||
errObj.through = tableNotSet
|
errObj.through = tableNotSet
|
||||||
}
|
}
|
||||||
|
if ($touched.through && invalidThroughTable(fromRelate)) {
|
||||||
|
errObj.through =
|
||||||
|
"Ensure all columns in table are nullable or auto generated"
|
||||||
|
}
|
||||||
if ($touched.foreign && !isMany && !fromRelate.fieldName) {
|
if ($touched.foreign && !isMany && !fromRelate.fieldName) {
|
||||||
errObj.foreign = "Please pick the foreign key"
|
errObj.foreign = "Please pick the foreign key"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { tables, datasources } from "stores/backend"
|
import { tables, datasources } from "stores/backend"
|
||||||
import {
|
import {
|
||||||
ActionMenu,
|
ActionMenu,
|
||||||
|
@ -18,7 +19,10 @@
|
||||||
let editorModal
|
let editorModal
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
let error = ""
|
let error = ""
|
||||||
let originalName = table.name
|
|
||||||
|
let originalName
|
||||||
|
let updatedName
|
||||||
|
|
||||||
let templateScreens
|
let templateScreens
|
||||||
let willBeDeleted
|
let willBeDeleted
|
||||||
let deleteTableName
|
let deleteTableName
|
||||||
|
@ -59,7 +63,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
await tables.save(table)
|
const updatedTable = cloneDeep(table)
|
||||||
|
updatedTable.name = updatedName
|
||||||
|
await tables.save(updatedTable)
|
||||||
notifications.success("Table renamed successfully")
|
notifications.success("Table renamed successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +76,11 @@
|
||||||
? `Table with name ${tableName} already exists. Please choose another name.`
|
? `Table with name ${tableName} already exists. Please choose another name.`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initForm = () => {
|
||||||
|
originalName = table.name + ""
|
||||||
|
updatedName = table.name + ""
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if allowDeletion}
|
{#if allowDeletion}
|
||||||
|
@ -84,17 +95,17 @@
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Modal bind:this={editorModal}>
|
<Modal bind:this={editorModal} on:show={initForm}>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Edit Table"
|
title="Edit Table"
|
||||||
confirmText="Save"
|
confirmText="Save"
|
||||||
onConfirm={save}
|
onConfirm={save}
|
||||||
disabled={table.name === originalName || error}
|
disabled={updatedName === originalName || error}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
label="Table Name"
|
label="Table Name"
|
||||||
thin
|
thin
|
||||||
bind:value={table.name}
|
bind:value={updatedName}
|
||||||
on:input={checkValid}
|
on:input={checkValid}
|
||||||
{error}
|
{error}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import { views } from "stores/backend"
|
import { views } from "stores/backend"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import {
|
import {
|
||||||
notifications,
|
notifications,
|
||||||
|
@ -15,13 +16,17 @@
|
||||||
export let view
|
export let view
|
||||||
|
|
||||||
let editorModal
|
let editorModal
|
||||||
let originalName = view.name
|
let originalName
|
||||||
|
let updatedName
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
|
const updatedView = cloneDeep(view)
|
||||||
|
updatedView.name = updatedName
|
||||||
|
|
||||||
await views.save({
|
await views.save({
|
||||||
originalName,
|
originalName,
|
||||||
...view,
|
...updatedView,
|
||||||
})
|
})
|
||||||
notifications.success("View renamed successfully")
|
notifications.success("View renamed successfully")
|
||||||
}
|
}
|
||||||
|
@ -37,6 +42,11 @@
|
||||||
notifications.error("Error deleting view")
|
notifications.error("Error deleting view")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initForm = () => {
|
||||||
|
updatedName = view.name + ""
|
||||||
|
originalName = view.name + ""
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionMenu>
|
<ActionMenu>
|
||||||
|
@ -46,9 +56,9 @@
|
||||||
<MenuItem icon="Edit" on:click={editorModal.show}>Edit</MenuItem>
|
<MenuItem icon="Edit" on:click={editorModal.show}>Edit</MenuItem>
|
||||||
<MenuItem icon="Delete" on:click={confirmDeleteDialog.show}>Delete</MenuItem>
|
<MenuItem icon="Delete" on:click={confirmDeleteDialog.show}>Delete</MenuItem>
|
||||||
</ActionMenu>
|
</ActionMenu>
|
||||||
<Modal bind:this={editorModal}>
|
<Modal bind:this={editorModal} on:show={initForm}>
|
||||||
<ModalContent title="Edit View" onConfirm={save} confirmText="Save">
|
<ModalContent title="Edit View" onConfirm={save} confirmText="Save">
|
||||||
<Input label="View Name" thin bind:value={view.name} />
|
<Input label="View Name" thin bind:value={updatedName} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let valid
|
export let valid
|
||||||
export let allowJS = false
|
export let allowJS = false
|
||||||
|
export let allowHelpers = true
|
||||||
|
|
||||||
let helpers = handlebarsCompletions()
|
let helpers = handlebarsCompletions()
|
||||||
let getCaretPosition
|
let getCaretPosition
|
||||||
|
@ -85,7 +86,7 @@
|
||||||
return helper.label.match(searchRgx) || helper.description.match(searchRgx)
|
return helper.label.match(searchRgx) || helper.description.match(searchRgx)
|
||||||
})
|
})
|
||||||
|
|
||||||
$: categoryNames = [...categories.map(cat => cat[0]), "Helpers"]
|
$: categoryNames = getCategoryNames(categories)
|
||||||
|
|
||||||
$: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`)
|
$: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`)
|
||||||
|
|
||||||
|
@ -96,6 +97,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getCategoryNames = categories => {
|
||||||
|
let names = [...categories.map(cat => cat[0])]
|
||||||
|
if (allowHelpers) {
|
||||||
|
names.push("Helpers")
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
// Adds a JS/HBS helper to the expression
|
// Adds a JS/HBS helper to the expression
|
||||||
const addHelper = (helper, js) => {
|
const addHelper = (helper, js) => {
|
||||||
let tempVal
|
let tempVal
|
||||||
|
@ -343,7 +352,7 @@
|
||||||
for more details.
|
for more details.
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $admin.isDev}
|
{#if $admin.isDev && allowJS}
|
||||||
<div class="convert">
|
<div class="convert">
|
||||||
<Button secondary on:click={convert}>Convert to JS</Button>
|
<Button secondary on:click={convert}>Convert to JS</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
export let valid
|
export let valid
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let allowJS = false
|
export let allowJS = false
|
||||||
|
export let allowHelpers = true
|
||||||
|
|
||||||
$: enrichedBindings = enrichBindings(bindings)
|
$: enrichedBindings = enrichBindings(bindings)
|
||||||
|
|
||||||
|
@ -25,5 +26,6 @@
|
||||||
bindings={enrichedBindings}
|
bindings={enrichedBindings}
|
||||||
{value}
|
{value}
|
||||||
{allowJS}
|
{allowJS}
|
||||||
|
{allowHelpers}
|
||||||
on:change
|
on:change
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let fillWidth
|
export let fillWidth
|
||||||
export let allowJS = true
|
export let allowJS = true
|
||||||
|
export let allowHelpers = true
|
||||||
export let updateOnChange = true
|
export let updateOnChange = true
|
||||||
export let drawerLeft
|
export let drawerLeft
|
||||||
|
|
||||||
|
@ -77,6 +78,7 @@
|
||||||
on:change={event => (tempValue = event.detail)}
|
on:change={event => (tempValue = event.detail)}
|
||||||
{bindings}
|
{bindings}
|
||||||
{allowJS}
|
{allowJS}
|
||||||
|
{allowHelpers}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ export function addJSBinding(value, caretPos, binding, { helper } = {}) {
|
||||||
if (!helper) {
|
if (!helper) {
|
||||||
binding = `$("${binding}")`
|
binding = `$("${binding}")`
|
||||||
} else {
|
} else {
|
||||||
binding = `helper.${binding}()`
|
binding = `helpers.${binding}()`
|
||||||
}
|
}
|
||||||
if (caretPos.start) {
|
if (caretPos.start) {
|
||||||
value =
|
value =
|
||||||
|
|
|
@ -27,6 +27,11 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Label small />
|
<Label small />
|
||||||
|
<Checkbox
|
||||||
|
text="Do not display default notification"
|
||||||
|
bind:value={parameters.notificationOverride}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
{#if parameters.confirm}
|
{#if parameters.confirm}
|
||||||
|
|
|
@ -95,6 +95,11 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Label small />
|
<Label small />
|
||||||
|
<Checkbox
|
||||||
|
text="Do not display default notification"
|
||||||
|
bind:value={parameters.notificationOverride}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
{#if parameters.confirm}
|
{#if parameters.confirm}
|
||||||
|
|
|
@ -48,7 +48,11 @@
|
||||||
getOptionLabel={query => query.name}
|
getOptionLabel={query => query.name}
|
||||||
getOptionValue={query => query._id}
|
getOptionValue={query => query._id}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
text="Do not display default notification"
|
||||||
|
bind:value={parameters.notificationOverride}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
{#if parameters.queryId}
|
{#if parameters.queryId}
|
||||||
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,11 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Label small />
|
<Label small />
|
||||||
|
<Checkbox
|
||||||
|
text="Do not display default notification"
|
||||||
|
bind:value={parameters.notificationOverride}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
{#if parameters.confirm}
|
{#if parameters.confirm}
|
||||||
|
|
|
@ -93,6 +93,11 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Label small />
|
<Label small />
|
||||||
|
<Checkbox
|
||||||
|
text="Do not display default notification"
|
||||||
|
bind:value={parameters.notificationOverride}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
<Checkbox text="Require confirmation" bind:value={parameters.confirm} />
|
||||||
|
|
||||||
{#if parameters.confirm}
|
{#if parameters.confirm}
|
||||||
|
|
|
@ -17,33 +17,73 @@
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
import { LuceneUtils, Constants } from "@budibase/frontend-core"
|
import { LuceneUtils, Constants } from "@budibase/frontend-core"
|
||||||
import { getFields } from "helpers/searchFields"
|
import { getFields } from "helpers/searchFields"
|
||||||
import { createEventDispatcher, onMount } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
const { OperatorOptions } = Constants
|
|
||||||
const { getValidOperatorsForType } = LuceneUtils
|
|
||||||
|
|
||||||
export let schemaFields
|
export let schemaFields
|
||||||
export let filters = []
|
export let filters = []
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
export let panel = ClientBindingPanel
|
export let panel = ClientBindingPanel
|
||||||
export let allowBindings = true
|
export let allowBindings = true
|
||||||
export let allOr = false
|
|
||||||
export let fillWidth = false
|
export let fillWidth = false
|
||||||
|
export let tableId
|
||||||
|
|
||||||
$: dispatch("change", filters)
|
const dispatch = createEventDispatcher()
|
||||||
$: enrichedSchemaFields = getFields(schemaFields || [])
|
const { OperatorOptions } = Constants
|
||||||
|
const { getValidOperatorsForType } = LuceneUtils
|
||||||
|
const KeyedFieldRegex = /\d[0-9]*:/g
|
||||||
|
const behaviourOptions = [
|
||||||
|
{ value: "and", label: "Match all filters" },
|
||||||
|
{ value: "or", label: "Match any filter" },
|
||||||
|
]
|
||||||
|
|
||||||
|
let rawFilters
|
||||||
|
let matchAny = false
|
||||||
|
|
||||||
|
$: parseFilters(filters)
|
||||||
|
$: dispatch("change", enrichFilters(rawFilters, matchAny))
|
||||||
|
$: enrichedSchemaFields = getFields(
|
||||||
|
schemaFields || [],
|
||||||
|
{ allowLinks: true },
|
||||||
|
tableId
|
||||||
|
)
|
||||||
$: fieldOptions = enrichedSchemaFields.map(field => field.name) || []
|
$: fieldOptions = enrichedSchemaFields.map(field => field.name) || []
|
||||||
$: valueTypeOptions = allowBindings ? ["Value", "Binding"] : ["Value"]
|
$: valueTypeOptions = allowBindings ? ["Value", "Binding"] : ["Value"]
|
||||||
|
|
||||||
let behaviourValue
|
// Remove field key prefixes and determine whether to use the "match all"
|
||||||
const behaviourOptions = [
|
// or "match any" behaviour
|
||||||
{ value: "and", label: "Match all of the following filters" },
|
const parseFilters = filters => {
|
||||||
{ value: "or", label: "Match any of the following filters" },
|
matchAny = filters?.find(filter => filter.operator === "allOr") != null
|
||||||
]
|
rawFilters = (filters || [])
|
||||||
|
.filter(filter => filter.operator !== "allOr")
|
||||||
|
.map(filter => {
|
||||||
|
const { field } = filter
|
||||||
|
let newFilter = { ...filter }
|
||||||
|
delete newFilter.allOr
|
||||||
|
if (typeof field === "string" && field.match(KeyedFieldRegex) != null) {
|
||||||
|
const parts = field.split(":")
|
||||||
|
parts.shift()
|
||||||
|
newFilter.field = parts.join(":")
|
||||||
|
}
|
||||||
|
return newFilter
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add field key prefixes and a special metadata filter object to indicate
|
||||||
|
// whether to use the "match all" or "match any" behaviour
|
||||||
|
const enrichFilters = (rawFilters, matchAny) => {
|
||||||
|
let count = 1
|
||||||
|
return rawFilters
|
||||||
|
.filter(filter => filter.field)
|
||||||
|
.map(filter => ({
|
||||||
|
...filter,
|
||||||
|
field: `${count++}:${filter.field}`,
|
||||||
|
}))
|
||||||
|
.concat(matchAny ? [{ operator: "allOr" }] : [])
|
||||||
|
}
|
||||||
|
|
||||||
const addFilter = () => {
|
const addFilter = () => {
|
||||||
filters = [
|
rawFilters = [
|
||||||
...filters,
|
...rawFilters,
|
||||||
{
|
{
|
||||||
id: generate(),
|
id: generate(),
|
||||||
field: null,
|
field: null,
|
||||||
|
@ -55,13 +95,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeFilter = id => {
|
const removeFilter = id => {
|
||||||
filters = filters.filter(field => field.id !== id)
|
rawFilters = rawFilters.filter(field => field.id !== id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const duplicateFilter = id => {
|
const duplicateFilter = id => {
|
||||||
const existingFilter = filters.find(filter => filter.id === id)
|
const existingFilter = rawFilters.find(filter => filter.id === id)
|
||||||
const duplicate = { ...existingFilter, id: generate() }
|
const duplicate = { ...existingFilter, id: generate() }
|
||||||
filters = [...filters, duplicate]
|
rawFilters = [...rawFilters, duplicate]
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSchema = filter => {
|
const getSchema = filter => {
|
||||||
|
@ -128,32 +168,22 @@
|
||||||
const schema = enrichedSchemaFields.find(x => x.name === field)
|
const schema = enrichedSchemaFields.find(x => x.name === field)
|
||||||
return schema?.constraints?.inclusion || []
|
return schema?.constraints?.inclusion || []
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
behaviourValue = allOr ? "or" : "and"
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Body size="S">
|
{#if !rawFilters?.length}
|
||||||
{#if !filters?.length}
|
<Body size="S">Add your first filter expression.</Body>
|
||||||
Add your first filter expression.
|
{:else}
|
||||||
{:else}
|
|
||||||
Results are filtered to only those which match all of the following
|
|
||||||
constraints.
|
|
||||||
{/if}
|
|
||||||
</Body>
|
|
||||||
{#if filters?.length}
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<Select
|
<Select
|
||||||
label="Behaviour"
|
label="Behaviour"
|
||||||
value={behaviourValue}
|
value={matchAny ? "or" : "and"}
|
||||||
options={behaviourOptions}
|
options={behaviourOptions}
|
||||||
getOptionLabel={opt => opt.label}
|
getOptionLabel={opt => opt.label}
|
||||||
getOptionValue={opt => opt.value}
|
getOptionValue={opt => opt.value}
|
||||||
on:change={e => (allOr = e.detail === "or")}
|
on:change={e => (matchAny = e.detail === "or")}
|
||||||
placeholder={null}
|
placeholder={null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -162,7 +192,7 @@
|
||||||
<Label>Filters</Label>
|
<Label>Filters</Label>
|
||||||
</div>
|
</div>
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
{#each filters as filter, idx}
|
{#each rawFilters as filter, idx}
|
||||||
<Select
|
<Select
|
||||||
bind:value={filter.field}
|
bind:value={filter.field}
|
||||||
options={fieldOptions}
|
options={fieldOptions}
|
||||||
|
@ -264,7 +294,7 @@
|
||||||
column-gap: var(--spacing-l);
|
column-gap: var(--spacing-l);
|
||||||
row-gap: var(--spacing-s);
|
row-gap: var(--spacing-s);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 1fr 150px 120px 1fr 16px 16px;
|
grid-template-columns: minmax(150px, 1fr) 170px 120px minmax(150px, 1fr) 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-label {
|
.filter-label {
|
||||||
|
|
|
@ -8,74 +8,22 @@
|
||||||
import FilterDrawer from "./FilterDrawer.svelte"
|
import FilterDrawer from "./FilterDrawer.svelte"
|
||||||
import { currentAsset } from "builderStore"
|
import { currentAsset } from "builderStore"
|
||||||
|
|
||||||
const QUERY_START_REGEX = /\d[0-9]*:/g
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let value = []
|
export let value = []
|
||||||
export let componentInstance
|
export let componentInstance
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
|
|
||||||
let drawer,
|
let drawer
|
||||||
toSaveFilters = null,
|
|
||||||
allOr,
|
|
||||||
initialAllOr
|
|
||||||
|
|
||||||
$: initialFilters = correctFilters(value || [])
|
$: tempValue = value
|
||||||
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
|
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
$: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
|
$: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
|
||||||
$: schemaFields = Object.values(schema || {})
|
$: schemaFields = Object.values(schema || {})
|
||||||
|
|
||||||
function addNumbering(filters) {
|
|
||||||
let count = 1
|
|
||||||
for (let value of filters) {
|
|
||||||
if (value.field && value.field?.match(QUERY_START_REGEX) == null) {
|
|
||||||
value.field = `${count++}:${value.field}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filters
|
|
||||||
}
|
|
||||||
|
|
||||||
function correctFilters(filters) {
|
|
||||||
const corrected = []
|
|
||||||
for (let filter of filters) {
|
|
||||||
let field = filter.field
|
|
||||||
if (filter.operator === "allOr") {
|
|
||||||
initialAllOr = allOr = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof filter.field === "string" &&
|
|
||||||
filter.field.match(QUERY_START_REGEX) != null
|
|
||||||
) {
|
|
||||||
const parts = field.split(":")
|
|
||||||
const number = parts[0]
|
|
||||||
// it's the new format, remove number
|
|
||||||
if (!isNaN(parseInt(number))) {
|
|
||||||
parts.shift()
|
|
||||||
field = parts.join(":")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
corrected.push({
|
|
||||||
...filter,
|
|
||||||
field,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return corrected
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveFilter() {
|
async function saveFilter() {
|
||||||
if (!toSaveFilters && allOr !== initialAllOr) {
|
dispatch("change", tempValue)
|
||||||
toSaveFilters = initialFilters
|
notifications.success("Filters saved")
|
||||||
}
|
|
||||||
const filters = toSaveFilters?.filter(filter => filter.operator !== "allOr")
|
|
||||||
if (allOr && filters) {
|
|
||||||
filters.push({ operator: "allOr" })
|
|
||||||
}
|
|
||||||
// only save if anything was updated
|
|
||||||
if (filters) {
|
|
||||||
dispatch("change", addNumbering(filters))
|
|
||||||
}
|
|
||||||
notifications.success("Filters saved.")
|
|
||||||
drawer.hide()
|
drawer.hide()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -85,12 +33,10 @@
|
||||||
<Button cta slot="buttons" on:click={saveFilter}>Save</Button>
|
<Button cta slot="buttons" on:click={saveFilter}>Save</Button>
|
||||||
<FilterDrawer
|
<FilterDrawer
|
||||||
slot="body"
|
slot="body"
|
||||||
filters={initialFilters}
|
filters={value}
|
||||||
{bindings}
|
{bindings}
|
||||||
{schemaFields}
|
{schemaFields}
|
||||||
bind:allOr
|
tableId={dataSource.tableId}
|
||||||
on:change={event => {
|
on:change={e => (tempValue = e.detail)}
|
||||||
toSaveFilters = event.detail
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
export let showMenu = false
|
export let showMenu = false
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
export let bindingDrawerLeft
|
export let bindingDrawerLeft
|
||||||
|
export let allowHelpers = true
|
||||||
|
|
||||||
let fields = Object.entries(object || {}).map(([name, value]) => ({
|
let fields = Object.entries(object || {}).map(([name, value]) => ({
|
||||||
name,
|
name,
|
||||||
|
@ -122,6 +123,7 @@
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
allowJS={false}
|
allowJS={false}
|
||||||
|
{allowHelpers}
|
||||||
fillWidth={true}
|
fillWidth={true}
|
||||||
drawerLeft={bindingDrawerLeft}
|
drawerLeft={bindingDrawerLeft}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -223,6 +223,7 @@
|
||||||
.config {
|
.config {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: var(--spacing-s);
|
grid-gap: var(--spacing-s);
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.config-field {
|
.config-field {
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
valuePlaceholder="Default"
|
valuePlaceholder="Default"
|
||||||
bindings={[...userBindings]}
|
bindings={[...userBindings]}
|
||||||
bindingDrawerLeft="260px"
|
bindingDrawerLeft="260px"
|
||||||
|
allowHelpers={false}
|
||||||
on:change
|
on:change
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,11 @@ export function getTableFields(linkField) {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFields(fields, { allowLinks } = { allowLinks: true }) {
|
export function getFields(
|
||||||
|
fields,
|
||||||
|
{ allowLinks } = { allowLinks: true },
|
||||||
|
tableId
|
||||||
|
) {
|
||||||
let filteredFields = fields.filter(
|
let filteredFields = fields.filter(
|
||||||
field => !BannedSearchTypes.includes(field.type)
|
field => !BannedSearchTypes.includes(field.type)
|
||||||
)
|
)
|
||||||
|
@ -30,5 +34,9 @@ export function getFields(fields, { allowLinks } = { allowLinks: true }) {
|
||||||
const staticFormulaFields = fields.filter(
|
const staticFormulaFields = fields.filter(
|
||||||
field => field.type === "formula" && field.formulaType === "static"
|
field => field.type === "formula" && field.formulaType === "static"
|
||||||
)
|
)
|
||||||
|
const table = get(tables).list.find(table => table._id === tableId)
|
||||||
|
if (table?.type === "external" && table?.sql) {
|
||||||
|
filteredFields = filteredFields.filter(field => field.name !== "_id")
|
||||||
|
}
|
||||||
return filteredFields.concat(staticFormulaFields)
|
return filteredFields.concat(staticFormulaFields)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,13 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
}
|
}
|
||||||
|
.header-left {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
.header-left :global(> *) {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
.header-left :global(.spectrum-Picker) {
|
.header-left :global(.spectrum-Picker) {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--spectrum-global-color-gray-900);
|
color: var(--spectrum-global-color-gray-900);
|
||||||
|
|
|
@ -282,12 +282,16 @@
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: auto 160px auto 1fr 130px 130px 1fr auto auto;
|
grid-template-columns:
|
||||||
|
auto 150px auto minmax(140px, 1fr) 120px 100px minmax(140px, 1fr)
|
||||||
|
auto auto;
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s);
|
||||||
transition: background-color ease-in-out 130ms;
|
transition: background-color ease-in-out 130ms;
|
||||||
}
|
}
|
||||||
.condition.update {
|
.condition.update {
|
||||||
grid-template-columns: auto 160px 1fr auto 1fr auto 1fr 130px 130px 1fr auto auto;
|
grid-template-columns:
|
||||||
|
auto 150px minmax(140px, 1fr) auto minmax(140px, 1fr) auto
|
||||||
|
minmax(140px, 1fr) 120px 100px minmax(140px, 1fr) auto auto;
|
||||||
}
|
}
|
||||||
.condition:hover {
|
.condition:hover {
|
||||||
background-color: var(--spectrum-global-color-gray-100);
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
|
|
|
@ -48,7 +48,6 @@
|
||||||
? {
|
? {
|
||||||
title: "User Groups",
|
title: "User Groups",
|
||||||
href: "/builder/portal/manage/groups",
|
href: "/builder/portal/manage/groups",
|
||||||
badge: "New",
|
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
{ title: "Auth", href: "/builder/portal/manage/auth" },
|
{ title: "Auth", href: "/builder/portal/manage/auth" },
|
||||||
|
@ -56,7 +55,6 @@
|
||||||
{
|
{
|
||||||
title: "Plugins",
|
title: "Plugins",
|
||||||
href: "/builder/portal/manage/plugins",
|
href: "/builder/portal/manage/plugins",
|
||||||
badge: "New",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -119,14 +117,12 @@
|
||||||
{
|
{
|
||||||
title: "Upgrade",
|
title: "Upgrade",
|
||||||
href: $adminStore.accountPortalUrl + "/portal/upgrade",
|
href: $adminStore.accountPortalUrl + "/portal/upgrade",
|
||||||
badge: "New",
|
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
} else if (!$adminStore.cloud && admin) {
|
} else if (!$adminStore.cloud && admin) {
|
||||||
menu = menu.concat({
|
menu = menu.concat({
|
||||||
title: "Upgrade",
|
title: "Upgrade",
|
||||||
href: "/builder/portal/settings/upgrade",
|
href: "/builder/portal/settings/upgrade",
|
||||||
badge: "New",
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ModalContent showCancelButton={false} {title} confirmText="Done">
|
<ModalContent size="M" showCancelButton={false} {title} confirmText="Done">
|
||||||
{#if hasSuccess}
|
{#if hasSuccess}
|
||||||
<Body size="XS">
|
<Body size="XS">
|
||||||
Your users should now receive an email invite to get access to their
|
Your users should now receive an email invite to get access to their
|
||||||
|
@ -70,6 +70,3 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
Table,
|
Table,
|
||||||
Layout,
|
Layout,
|
||||||
Modal,
|
Modal,
|
||||||
ModalContent,
|
|
||||||
Search,
|
Search,
|
||||||
notifications,
|
notifications,
|
||||||
Pagination,
|
Pagination,
|
||||||
|
@ -23,6 +22,7 @@
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte"
|
import OnboardingTypeModal from "./_components/OnboardingTypeModal.svelte"
|
||||||
import PasswordModal from "./_components/PasswordModal.svelte"
|
import PasswordModal from "./_components/PasswordModal.svelte"
|
||||||
|
import InvitedModal from "./_components/InvitedModal.svelte"
|
||||||
import ImportUsersModal from "./_components/ImportUsersModal.svelte"
|
import ImportUsersModal from "./_components/ImportUsersModal.svelte"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { Constants, Utils, fetchData } from "@budibase/frontend-core"
|
import { Constants, Utils, fetchData } from "@budibase/frontend-core"
|
||||||
|
@ -67,6 +67,8 @@
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
$: userData = []
|
||||||
|
$: inviteUsersResponse = { successful: [], unsuccessful: [] }
|
||||||
$: {
|
$: {
|
||||||
enrichedUsers = $fetch.rows?.map(user => {
|
enrichedUsers = $fetch.rows?.map(user => {
|
||||||
let userGroups = []
|
let userGroups = []
|
||||||
|
@ -112,8 +114,7 @@
|
||||||
groups: userData.groups,
|
groups: userData.groups,
|
||||||
}))
|
}))
|
||||||
try {
|
try {
|
||||||
const res = await users.invite(payload)
|
inviteUsersResponse = await users.invite(payload)
|
||||||
notifications.success(res.message)
|
|
||||||
inviteConfirmationModal.show()
|
inviteConfirmationModal.show()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error inviting user")
|
notifications.error("Error inviting user")
|
||||||
|
@ -273,16 +274,7 @@
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={inviteConfirmationModal}>
|
<Modal bind:this={inviteConfirmationModal}>
|
||||||
<ModalContent
|
<InvitedModal {inviteUsersResponse} />
|
||||||
showCancelButton={false}
|
|
||||||
title="Invites sent!"
|
|
||||||
confirmText="Done"
|
|
||||||
>
|
|
||||||
<Body size="S">
|
|
||||||
Your users should now recieve an email invite to get access to their
|
|
||||||
Budibase account
|
|
||||||
</Body>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal bind:this={onboardingTypeModal}>
|
<Modal bind:this={onboardingTypeModal}>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "2.1.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"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.40-alpha.7",
|
"@budibase/backend-core": "2.1.46-alpha.1",
|
||||||
"@budibase/string-templates": "2.1.40-alpha.7",
|
"@budibase/string-templates": "2.1.46-alpha.1",
|
||||||
"@budibase/types": "2.1.40-alpha.7",
|
"@budibase/types": "2.1.46-alpha.1",
|
||||||
"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",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2598,6 +2598,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"passwordfield": {
|
"passwordfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Password Field",
|
"name": "Password Field",
|
||||||
"icon": "LockClosed",
|
"icon": "LockClosed",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -3066,6 +3067,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"longformfield": {
|
"longformfield": {
|
||||||
|
"skeleton": false,
|
||||||
"name": "Long Form Field",
|
"name": "Long Form Field",
|
||||||
"icon": "TextAlignLeft",
|
"icon": "TextAlignLeft",
|
||||||
"styles": [
|
"styles": [
|
||||||
|
@ -5118,7 +5120,8 @@
|
||||||
{
|
{
|
||||||
"type": "multifield",
|
"type": "multifield",
|
||||||
"label": "Fields",
|
"label": "Fields",
|
||||||
"key": "fields"
|
"key": "fields",
|
||||||
|
"selectAllFields": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
@ -5204,7 +5207,8 @@
|
||||||
"icon": "RailRight",
|
"icon": "RailRight",
|
||||||
"hasChildren": true,
|
"hasChildren": true,
|
||||||
"illegalChildren": [
|
"illegalChildren": [
|
||||||
"section"
|
"section",
|
||||||
|
"sidepanel"
|
||||||
],
|
],
|
||||||
"showEmptyState": false,
|
"showEmptyState": false,
|
||||||
"draggable": false,
|
"draggable": false,
|
||||||
|
@ -5283,7 +5287,8 @@
|
||||||
"type": "multifield",
|
"type": "multifield",
|
||||||
"label": "Fields",
|
"label": "Fields",
|
||||||
"key": "detailFields",
|
"key": "detailFields",
|
||||||
"nested": true
|
"nested": true,
|
||||||
|
"selectAllFields": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5293,4 +5298,4 @@
|
||||||
"suffix": "repeater"
|
"suffix": "repeater"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "2.1.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"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.40-alpha.7",
|
"@budibase/bbui": "2.1.46-alpha.1",
|
||||||
"@budibase/frontend-core": "2.1.40-alpha.7",
|
"@budibase/frontend-core": "2.1.46-alpha.1",
|
||||||
"@budibase/string-templates": "2.1.40-alpha.7",
|
"@budibase/string-templates": "2.1.46-alpha.1",
|
||||||
"@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",
|
||||||
|
|
|
@ -479,6 +479,7 @@
|
||||||
definition.name !== "Screenslot" &&
|
definition.name !== "Screenslot" &&
|
||||||
children.length === 0 &&
|
children.length === 0 &&
|
||||||
!instance._blockElementHasChildren &&
|
!instance._blockElementHasChildren &&
|
||||||
|
!definition.block &&
|
||||||
definition.skeleton !== false
|
definition.skeleton !== false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,17 @@
|
||||||
// Derive visibility
|
// Derive visibility
|
||||||
$: open = $sidePanelStore.contentId === $component.id
|
$: open = $sidePanelStore.contentId === $component.id
|
||||||
|
|
||||||
|
// Derive a render key which is only changed whenever this panel is made
|
||||||
|
// visible after being hidden. We need to key the content to avoid showing
|
||||||
|
// stale data when re-revealing a side panel that was closed, but we cannot
|
||||||
|
// hide the content altogether when hidden as this breaks ejection.
|
||||||
|
let renderKey = null
|
||||||
|
$: {
|
||||||
|
if (open) {
|
||||||
|
renderKey = Math.random()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const showInSidePanel = (el, visible) => {
|
const showInSidePanel = (el, visible) => {
|
||||||
const update = visible => {
|
const update = visible => {
|
||||||
const target = document.getElementById("side-panel-container")
|
const target = document.getElementById("side-panel-container")
|
||||||
|
@ -47,7 +58,10 @@
|
||||||
// Apply initial visibility
|
// Apply initial visibility
|
||||||
update(visible)
|
update(visible)
|
||||||
|
|
||||||
return { update }
|
return {
|
||||||
|
update,
|
||||||
|
destroy: () => update(false),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -57,7 +71,9 @@
|
||||||
class="side-panel"
|
class="side-panel"
|
||||||
class:open
|
class:open
|
||||||
>
|
>
|
||||||
<slot />
|
{#key renderKey}
|
||||||
|
<slot />
|
||||||
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
let dataProviderId
|
let dataProviderId
|
||||||
let repeaterId
|
let repeaterId
|
||||||
let schema
|
let schema
|
||||||
let schemaLoaded = false
|
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: enrichedSearchColumns = enrichSearchColumns(searchColumns, schema)
|
$: enrichedSearchColumns = enrichSearchColumns(searchColumns, schema)
|
||||||
|
@ -75,138 +74,135 @@
|
||||||
enrichRelationships: true,
|
enrichRelationships: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
schemaLoaded = true
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if schemaLoaded}
|
<Block>
|
||||||
<Block>
|
<BlockComponent
|
||||||
<BlockComponent
|
type="form"
|
||||||
type="form"
|
bind:id={formId}
|
||||||
bind:id={formId}
|
props={{ dataSource, disableValidation: true }}
|
||||||
props={{ dataSource, disableValidation: true }}
|
>
|
||||||
>
|
{#if title || enrichedSearchColumns?.length || showTitleButton}
|
||||||
{#if title || enrichedSearchColumns?.length || showTitleButton}
|
<BlockComponent
|
||||||
|
type="container"
|
||||||
|
props={{
|
||||||
|
direction: "row",
|
||||||
|
hAlign: "stretch",
|
||||||
|
vAlign: "middle",
|
||||||
|
gap: "M",
|
||||||
|
wrap: true,
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
normal: {
|
||||||
|
"margin-bottom": "20px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
order={0}
|
||||||
|
>
|
||||||
|
<BlockComponent
|
||||||
|
type="heading"
|
||||||
|
props={{
|
||||||
|
text: title,
|
||||||
|
}}
|
||||||
|
order={0}
|
||||||
|
/>
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="container"
|
type="container"
|
||||||
props={{
|
props={{
|
||||||
direction: "row",
|
direction: "row",
|
||||||
hAlign: "stretch",
|
hAlign: "left",
|
||||||
vAlign: "middle",
|
vAlign: "middle",
|
||||||
gap: "M",
|
gap: "M",
|
||||||
wrap: true,
|
wrap: true,
|
||||||
}}
|
}}
|
||||||
|
order={1}
|
||||||
|
>
|
||||||
|
{#if enrichedSearchColumns?.length}
|
||||||
|
{#each enrichedSearchColumns as column, idx}
|
||||||
|
<BlockComponent
|
||||||
|
type={column.componentType}
|
||||||
|
props={{
|
||||||
|
field: column.name,
|
||||||
|
placeholder: column.name,
|
||||||
|
text: column.name,
|
||||||
|
autoWidth: true,
|
||||||
|
}}
|
||||||
|
order={idx}
|
||||||
|
styles={{
|
||||||
|
normal: {
|
||||||
|
width: "192px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
{#if showTitleButton}
|
||||||
|
<BlockComponent
|
||||||
|
type="button"
|
||||||
|
props={{
|
||||||
|
onClick: titleButtonAction,
|
||||||
|
text: titleButtonText,
|
||||||
|
type: "cta",
|
||||||
|
}}
|
||||||
|
order={enrichedSearchColumns?.length ?? 0}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</BlockComponent>
|
||||||
|
</BlockComponent>
|
||||||
|
{/if}
|
||||||
|
<BlockComponent
|
||||||
|
type="dataprovider"
|
||||||
|
bind:id={dataProviderId}
|
||||||
|
props={{
|
||||||
|
dataSource,
|
||||||
|
filter: enrichedFilter,
|
||||||
|
sortColumn,
|
||||||
|
sortOrder,
|
||||||
|
paginate,
|
||||||
|
limit,
|
||||||
|
}}
|
||||||
|
order={1}
|
||||||
|
>
|
||||||
|
<BlockComponent
|
||||||
|
type="repeater"
|
||||||
|
bind:id={repeaterId}
|
||||||
|
context="repeater"
|
||||||
|
props={{
|
||||||
|
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
|
||||||
|
direction: "row",
|
||||||
|
hAlign: "stretch",
|
||||||
|
vAlign: "top",
|
||||||
|
gap: "M",
|
||||||
|
noRowsMessage: "No rows found",
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
custom: `display: grid;\ngrid-template-columns: repeat(auto-fill, minmax(min(${cardWidth}px, 100%), 1fr));`,
|
||||||
|
}}
|
||||||
|
order={0}
|
||||||
|
>
|
||||||
|
<BlockComponent
|
||||||
|
type="spectrumcard"
|
||||||
|
props={{
|
||||||
|
title: cardTitle,
|
||||||
|
subtitle: cardSubtitle,
|
||||||
|
description: cardDescription,
|
||||||
|
imageURL: cardImageURL,
|
||||||
|
horizontal: cardHorizontal,
|
||||||
|
showButton: showCardButton,
|
||||||
|
buttonText: cardButtonText,
|
||||||
|
buttonOnClick: cardButtonOnClick,
|
||||||
|
linkURL: fullCardURL,
|
||||||
|
linkPeek: cardPeek,
|
||||||
|
}}
|
||||||
styles={{
|
styles={{
|
||||||
normal: {
|
normal: {
|
||||||
"margin-bottom": "20px",
|
width: "auto",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
order={0}
|
order={0}
|
||||||
>
|
/>
|
||||||
<BlockComponent
|
|
||||||
type="heading"
|
|
||||||
props={{
|
|
||||||
text: title,
|
|
||||||
}}
|
|
||||||
order={0}
|
|
||||||
/>
|
|
||||||
<BlockComponent
|
|
||||||
type="container"
|
|
||||||
props={{
|
|
||||||
direction: "row",
|
|
||||||
hAlign: "left",
|
|
||||||
vAlign: "middle",
|
|
||||||
gap: "M",
|
|
||||||
wrap: true,
|
|
||||||
}}
|
|
||||||
order={1}
|
|
||||||
>
|
|
||||||
{#if enrichedSearchColumns?.length}
|
|
||||||
{#each enrichedSearchColumns as column, idx}
|
|
||||||
<BlockComponent
|
|
||||||
type={column.componentType}
|
|
||||||
props={{
|
|
||||||
field: column.name,
|
|
||||||
placeholder: column.name,
|
|
||||||
text: column.name,
|
|
||||||
autoWidth: true,
|
|
||||||
}}
|
|
||||||
order={idx}
|
|
||||||
styles={{
|
|
||||||
normal: {
|
|
||||||
width: "192px",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{#if showTitleButton}
|
|
||||||
<BlockComponent
|
|
||||||
type="button"
|
|
||||||
props={{
|
|
||||||
onClick: titleButtonAction,
|
|
||||||
text: titleButtonText,
|
|
||||||
type: "cta",
|
|
||||||
}}
|
|
||||||
order={enrichedSearchColumns?.length ?? 0}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</BlockComponent>
|
|
||||||
</BlockComponent>
|
|
||||||
{/if}
|
|
||||||
<BlockComponent
|
|
||||||
type="dataprovider"
|
|
||||||
bind:id={dataProviderId}
|
|
||||||
props={{
|
|
||||||
dataSource,
|
|
||||||
filter: enrichedFilter,
|
|
||||||
sortColumn,
|
|
||||||
sortOrder,
|
|
||||||
paginate,
|
|
||||||
limit,
|
|
||||||
}}
|
|
||||||
order={1}
|
|
||||||
>
|
|
||||||
<BlockComponent
|
|
||||||
type="repeater"
|
|
||||||
bind:id={repeaterId}
|
|
||||||
context="repeater"
|
|
||||||
props={{
|
|
||||||
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
|
|
||||||
direction: "row",
|
|
||||||
hAlign: "stretch",
|
|
||||||
vAlign: "top",
|
|
||||||
gap: "M",
|
|
||||||
noRowsMessage: "No rows found",
|
|
||||||
}}
|
|
||||||
styles={{
|
|
||||||
custom: `display: grid;\ngrid-template-columns: repeat(auto-fill, minmax(min(${cardWidth}px, 100%), 1fr));`,
|
|
||||||
}}
|
|
||||||
order={0}
|
|
||||||
>
|
|
||||||
<BlockComponent
|
|
||||||
type="spectrumcard"
|
|
||||||
props={{
|
|
||||||
title: cardTitle,
|
|
||||||
subtitle: cardSubtitle,
|
|
||||||
description: cardDescription,
|
|
||||||
imageURL: cardImageURL,
|
|
||||||
horizontal: cardHorizontal,
|
|
||||||
showButton: showCardButton,
|
|
||||||
buttonText: cardButtonText,
|
|
||||||
buttonOnClick: cardButtonOnClick,
|
|
||||||
linkURL: fullCardURL,
|
|
||||||
linkPeek: cardPeek,
|
|
||||||
}}
|
|
||||||
styles={{
|
|
||||||
normal: {
|
|
||||||
width: "auto",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
order={0}
|
|
||||||
/>
|
|
||||||
</BlockComponent>
|
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
</Block>
|
</BlockComponent>
|
||||||
{/if}
|
</Block>
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
let newRowSidePanelId
|
let newRowSidePanelId
|
||||||
let schema
|
let schema
|
||||||
let primaryDisplay
|
let primaryDisplay
|
||||||
let schemaLoaded = false
|
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: enrichedSearchColumns = enrichSearchColumns(searchColumns, schema)
|
$: enrichedSearchColumns = enrichSearchColumns(searchColumns, schema)
|
||||||
|
@ -66,7 +65,7 @@
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
$: buttonClickActions =
|
$: buttonClickActions =
|
||||||
clickBehaviour === "actions" || dataSource?.type !== "table"
|
titleButtonClickBehaviour === "actions" || dataSource?.type !== "table"
|
||||||
? onClickTitleButton
|
? onClickTitleButton
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
|
@ -89,7 +88,6 @@
|
||||||
enrichRelationships: true,
|
enrichRelationships: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
schemaLoaded = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNormalFields = schema => {
|
const getNormalFields = schema => {
|
||||||
|
@ -113,162 +111,160 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if schemaLoaded}
|
<Block>
|
||||||
<Block>
|
<BlockComponent
|
||||||
<BlockComponent
|
type="form"
|
||||||
type="form"
|
bind:id={formId}
|
||||||
bind:id={formId}
|
props={{
|
||||||
props={{
|
dataSource,
|
||||||
dataSource,
|
disableValidation: true,
|
||||||
disableValidation: true,
|
editAutoColumns: true,
|
||||||
editAutoColumns: true,
|
size,
|
||||||
size,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{#if title || enrichedSearchColumns?.length || showTitleButton}
|
||||||
{#if title || enrichedSearchColumns?.length || showTitleButton}
|
<BlockComponent
|
||||||
|
type="container"
|
||||||
|
props={{
|
||||||
|
direction: "row",
|
||||||
|
hAlign: "stretch",
|
||||||
|
vAlign: "middle",
|
||||||
|
gap: "M",
|
||||||
|
wrap: true,
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
normal: {
|
||||||
|
"margin-bottom": "20px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
order={0}
|
||||||
|
>
|
||||||
|
<BlockComponent
|
||||||
|
type="heading"
|
||||||
|
props={{
|
||||||
|
text: title,
|
||||||
|
}}
|
||||||
|
order={0}
|
||||||
|
/>
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="container"
|
type="container"
|
||||||
props={{
|
props={{
|
||||||
direction: "row",
|
direction: "row",
|
||||||
hAlign: "stretch",
|
hAlign: "left",
|
||||||
vAlign: "middle",
|
vAlign: "center",
|
||||||
gap: "M",
|
gap: "M",
|
||||||
wrap: true,
|
wrap: true,
|
||||||
}}
|
}}
|
||||||
styles={{
|
order={1}
|
||||||
normal: {
|
|
||||||
"margin-bottom": "20px",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
order={0}
|
|
||||||
>
|
>
|
||||||
<BlockComponent
|
{#if enrichedSearchColumns?.length}
|
||||||
type="heading"
|
{#each enrichedSearchColumns as column, idx}
|
||||||
props={{
|
|
||||||
text: title,
|
|
||||||
}}
|
|
||||||
order={0}
|
|
||||||
/>
|
|
||||||
<BlockComponent
|
|
||||||
type="container"
|
|
||||||
props={{
|
|
||||||
direction: "row",
|
|
||||||
hAlign: "left",
|
|
||||||
vAlign: "center",
|
|
||||||
gap: "M",
|
|
||||||
wrap: true,
|
|
||||||
}}
|
|
||||||
order={1}
|
|
||||||
>
|
|
||||||
{#if enrichedSearchColumns?.length}
|
|
||||||
{#each enrichedSearchColumns as column, idx}
|
|
||||||
<BlockComponent
|
|
||||||
type={column.componentType}
|
|
||||||
props={{
|
|
||||||
field: column.name,
|
|
||||||
placeholder: column.name,
|
|
||||||
text: column.name,
|
|
||||||
autoWidth: true,
|
|
||||||
}}
|
|
||||||
styles={{
|
|
||||||
normal: {
|
|
||||||
width: "192px",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
order={idx}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{#if showTitleButton}
|
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="button"
|
type={column.componentType}
|
||||||
props={{
|
props={{
|
||||||
onClick: buttonClickActions,
|
field: column.name,
|
||||||
text: titleButtonText,
|
placeholder: column.name,
|
||||||
type: "cta",
|
text: column.name,
|
||||||
|
autoWidth: true,
|
||||||
}}
|
}}
|
||||||
order={enrichedSearchColumns?.length ?? 0}
|
styles={{
|
||||||
|
normal: {
|
||||||
|
width: "192px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
order={idx}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/each}
|
||||||
</BlockComponent>
|
{/if}
|
||||||
|
{#if showTitleButton}
|
||||||
|
<BlockComponent
|
||||||
|
type="button"
|
||||||
|
props={{
|
||||||
|
onClick: buttonClickActions,
|
||||||
|
text: titleButtonText,
|
||||||
|
type: "cta",
|
||||||
|
}}
|
||||||
|
order={enrichedSearchColumns?.length ?? 0}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
{/if}
|
</BlockComponent>
|
||||||
|
{/if}
|
||||||
|
<BlockComponent
|
||||||
|
type="dataprovider"
|
||||||
|
bind:id={dataProviderId}
|
||||||
|
props={{
|
||||||
|
dataSource,
|
||||||
|
filter: enrichedFilter,
|
||||||
|
sortColumn: sortColumn || primaryDisplay,
|
||||||
|
sortOrder,
|
||||||
|
paginate,
|
||||||
|
limit: rowCount,
|
||||||
|
}}
|
||||||
|
order={1}
|
||||||
|
>
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="dataprovider"
|
type="table"
|
||||||
bind:id={dataProviderId}
|
context="table"
|
||||||
props={{
|
props={{
|
||||||
dataSource,
|
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
|
||||||
filter: enrichedFilter,
|
columns: tableColumns,
|
||||||
sortColumn: sortColumn || primaryDisplay,
|
rowCount,
|
||||||
sortOrder,
|
quiet,
|
||||||
paginate,
|
compact,
|
||||||
limit: rowCount,
|
allowSelectRows,
|
||||||
|
size,
|
||||||
|
onClick: rowClickActions,
|
||||||
}}
|
}}
|
||||||
order={1}
|
/>
|
||||||
|
</BlockComponent>
|
||||||
|
{#if clickBehaviour === "details"}
|
||||||
|
<BlockComponent
|
||||||
|
name="Details side panel"
|
||||||
|
type="sidepanel"
|
||||||
|
bind:id={detailsSidePanelId}
|
||||||
|
context="details-side-panel"
|
||||||
|
order={2}
|
||||||
>
|
>
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type="table"
|
name="Details form block"
|
||||||
context="table"
|
type="formblock"
|
||||||
|
bind:id={detailsFormBlockId}
|
||||||
props={{
|
props={{
|
||||||
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
|
dataSource,
|
||||||
columns: tableColumns,
|
showSaveButton: true,
|
||||||
rowCount,
|
showDeleteButton: true,
|
||||||
quiet,
|
actionType: "Update",
|
||||||
compact,
|
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
|
||||||
allowSelectRows,
|
fields: normalFields,
|
||||||
size,
|
title: editTitle,
|
||||||
onClick: rowClickActions,
|
labelPosition: "left",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</BlockComponent>
|
</BlockComponent>
|
||||||
{#if clickBehaviour === "details"}
|
{/if}
|
||||||
|
{#if showTitleButton && titleButtonClickBehaviour === "new"}
|
||||||
|
<BlockComponent
|
||||||
|
name="New row side panel"
|
||||||
|
type="sidepanel"
|
||||||
|
bind:id={newRowSidePanelId}
|
||||||
|
context="new-side-panel"
|
||||||
|
order={3}
|
||||||
|
>
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
name="Details side panel"
|
name="New row form block"
|
||||||
type="sidepanel"
|
type="formblock"
|
||||||
bind:id={detailsSidePanelId}
|
props={{
|
||||||
context="details-side-panel"
|
dataSource,
|
||||||
order={2}
|
showSaveButton: true,
|
||||||
>
|
showDeleteButton: false,
|
||||||
<BlockComponent
|
actionType: "Create",
|
||||||
name="Details form block"
|
fields: normalFields,
|
||||||
type="formblock"
|
title: "Create Row",
|
||||||
bind:id={detailsFormBlockId}
|
labelPosition: "left",
|
||||||
props={{
|
}}
|
||||||
dataSource,
|
/>
|
||||||
showSaveButton: true,
|
</BlockComponent>
|
||||||
showDeleteButton: true,
|
{/if}
|
||||||
actionType: "Update",
|
</BlockComponent>
|
||||||
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
|
</Block>
|
||||||
fields: normalFields,
|
|
||||||
title: editTitle,
|
|
||||||
labelPosition: "left",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</BlockComponent>
|
|
||||||
{/if}
|
|
||||||
{#if showTitleButton && titleButtonClickBehaviour === "new"}
|
|
||||||
<BlockComponent
|
|
||||||
name="New row side panel"
|
|
||||||
type="sidepanel"
|
|
||||||
bind:id={newRowSidePanelId}
|
|
||||||
context="new-side-panel"
|
|
||||||
order={3}
|
|
||||||
>
|
|
||||||
<BlockComponent
|
|
||||||
name="New row form block"
|
|
||||||
type="formblock"
|
|
||||||
props={{
|
|
||||||
dataSource,
|
|
||||||
showSaveButton: true,
|
|
||||||
showDeleteButton: false,
|
|
||||||
actionType: "Create",
|
|
||||||
fields: normalFields,
|
|
||||||
title: "Create Row",
|
|
||||||
labelPosition: "left",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</BlockComponent>
|
|
||||||
{/if}
|
|
||||||
</BlockComponent>
|
|
||||||
</Block>
|
|
||||||
{/if}
|
|
||||||
|
|
|
@ -20,12 +20,10 @@
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
const { API, fetchDatasourceSchema } = getContext("sdk")
|
const { API, fetchDatasourceSchema } = getContext("sdk")
|
||||||
|
|
||||||
let loaded = false
|
|
||||||
let schema
|
let schema
|
||||||
let table
|
let table
|
||||||
|
|
||||||
$: fetchSchema(dataSource)
|
$: fetchSchema(dataSource)
|
||||||
$: fetchTable(dataSource)
|
|
||||||
|
|
||||||
// Returns the closes data context which isn't a built in context
|
// Returns the closes data context which isn't a built in context
|
||||||
const getInitialValues = (type, dataSource, context) => {
|
const getInitialValues = (type, dataSource, context) => {
|
||||||
|
@ -48,13 +46,6 @@
|
||||||
|
|
||||||
// Fetches the form schema from this form's dataSource
|
// Fetches the form schema from this form's dataSource
|
||||||
const fetchSchema = async dataSource => {
|
const fetchSchema = async dataSource => {
|
||||||
schema = (await fetchDatasourceSchema(dataSource)) || {}
|
|
||||||
if (!loaded) {
|
|
||||||
loaded = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchTable = async dataSource => {
|
|
||||||
if (dataSource?.tableId && dataSource?.type !== "query") {
|
if (dataSource?.tableId && dataSource?.type !== "query") {
|
||||||
try {
|
try {
|
||||||
table = await API.fetchTableDefinition(dataSource.tableId)
|
table = await API.fetchTableDefinition(dataSource.tableId)
|
||||||
|
@ -62,29 +53,32 @@
|
||||||
table = null
|
table = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const res = await fetchDatasourceSchema(dataSource)
|
||||||
|
schema = res || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: initialValues = getInitialValues(actionType, dataSource, $context)
|
$: initialValues = getInitialValues(actionType, dataSource, $context)
|
||||||
$: resetKey = Helpers.hashString(
|
$: resetKey = Helpers.hashString(
|
||||||
JSON.stringify(initialValues) + JSON.stringify(dataSource) + disabled
|
!!schema +
|
||||||
|
JSON.stringify(initialValues) +
|
||||||
|
JSON.stringify(dataSource) +
|
||||||
|
disabled
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if loaded}
|
{#key resetKey}
|
||||||
{#key resetKey}
|
<InnerForm
|
||||||
<InnerForm
|
{dataSource}
|
||||||
{dataSource}
|
{theme}
|
||||||
{theme}
|
{size}
|
||||||
{size}
|
{disabled}
|
||||||
{disabled}
|
{actionType}
|
||||||
{actionType}
|
{schema}
|
||||||
{schema}
|
{table}
|
||||||
{table}
|
{initialValues}
|
||||||
{initialValues}
|
{disableValidation}
|
||||||
{disableValidation}
|
{editAutoColumns}
|
||||||
{editAutoColumns}
|
>
|
||||||
>
|
<slot />
|
||||||
<slot />
|
</InnerForm>
|
||||||
</InnerForm>
|
{/key}
|
||||||
{/key}
|
|
||||||
{/if}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { derived } from "svelte/store"
|
import { derived } from "svelte/store"
|
||||||
|
import { Constants } from "@budibase/frontend-core"
|
||||||
import { devToolsStore } from "../devTools.js"
|
import { devToolsStore } from "../devTools.js"
|
||||||
import { authStore } from "../auth.js"
|
import { authStore } from "../auth.js"
|
||||||
|
|
||||||
|
@ -6,6 +7,10 @@ import { authStore } from "../auth.js"
|
||||||
export const currentRole = derived(
|
export const currentRole = derived(
|
||||||
[devToolsStore, authStore],
|
[devToolsStore, authStore],
|
||||||
([$devToolsStore, $authStore]) => {
|
([$devToolsStore, $authStore]) => {
|
||||||
return ($devToolsStore.enabled && $devToolsStore.role) || $authStore?.roleId
|
return (
|
||||||
|
($devToolsStore.enabled && $devToolsStore.role) ||
|
||||||
|
$authStore?.roleId ||
|
||||||
|
Constants.Roles.PUBLIC
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,18 +11,25 @@ export const createSidePanelStore = () => {
|
||||||
open: $store.contentId != null,
|
open: $store.contentId != null,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
let timeout
|
||||||
|
|
||||||
const open = id => {
|
const open = id => {
|
||||||
|
clearTimeout(timeout)
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.contentId = id
|
state.contentId = id
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delay closing by 50ms to avoid toggling visibility when cycling though
|
||||||
|
// records
|
||||||
const close = () => {
|
const close = () => {
|
||||||
store.update(state => {
|
timeout = setTimeout(() => {
|
||||||
state.contentId = null
|
store.update(state => {
|
||||||
return state
|
state.contentId = null
|
||||||
})
|
return state
|
||||||
|
})
|
||||||
|
}, 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -18,7 +18,8 @@ import { enrichDataBindings } from "./enrichDataBinding"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
|
||||||
const saveRowHandler = async (action, context) => {
|
const saveRowHandler = async (action, context) => {
|
||||||
const { fields, providerId, tableId } = action.parameters
|
const { fields, providerId, tableId, notificationOverride } =
|
||||||
|
action.parameters
|
||||||
let payload
|
let payload
|
||||||
if (providerId) {
|
if (providerId) {
|
||||||
payload = { ...context[providerId] }
|
payload = { ...context[providerId] }
|
||||||
|
@ -35,7 +36,10 @@ const saveRowHandler = async (action, context) => {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const row = await API.saveRow(payload)
|
const row = await API.saveRow(payload)
|
||||||
notificationStore.actions.success("Row saved")
|
|
||||||
|
if (!notificationOverride) {
|
||||||
|
notificationStore.actions.success("Row saved")
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh related datasources
|
// Refresh related datasources
|
||||||
await dataSourceStore.actions.invalidateDataSource(row.tableId, {
|
await dataSourceStore.actions.invalidateDataSource(row.tableId, {
|
||||||
|
@ -50,7 +54,8 @@ const saveRowHandler = async (action, context) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const duplicateRowHandler = async (action, context) => {
|
const duplicateRowHandler = async (action, context) => {
|
||||||
const { fields, providerId, tableId } = action.parameters
|
const { fields, providerId, tableId, notificationOverride } =
|
||||||
|
action.parameters
|
||||||
if (providerId) {
|
if (providerId) {
|
||||||
let payload = { ...context[providerId] }
|
let payload = { ...context[providerId] }
|
||||||
if (fields) {
|
if (fields) {
|
||||||
|
@ -65,7 +70,9 @@ const duplicateRowHandler = async (action, context) => {
|
||||||
delete payload._rev
|
delete payload._rev
|
||||||
try {
|
try {
|
||||||
const row = await API.saveRow(payload)
|
const row = await API.saveRow(payload)
|
||||||
notificationStore.actions.success("Row saved")
|
if (!notificationOverride) {
|
||||||
|
notificationStore.actions.success("Row saved")
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh related datasources
|
// Refresh related datasources
|
||||||
await dataSourceStore.actions.invalidateDataSource(row.tableId, {
|
await dataSourceStore.actions.invalidateDataSource(row.tableId, {
|
||||||
|
@ -81,11 +88,13 @@ const duplicateRowHandler = async (action, context) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRowHandler = async action => {
|
const deleteRowHandler = async action => {
|
||||||
const { tableId, revId, rowId } = action.parameters
|
const { tableId, revId, rowId, notificationOverride } = action.parameters
|
||||||
if (tableId && rowId) {
|
if (tableId && rowId) {
|
||||||
try {
|
try {
|
||||||
await API.deleteRow({ tableId, rowId, revId })
|
await API.deleteRow({ tableId, rowId, revId })
|
||||||
notificationStore.actions.success("Row deleted")
|
if (!notificationOverride) {
|
||||||
|
notificationStore.actions.success("Row deleted")
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh related datasources
|
// Refresh related datasources
|
||||||
await dataSourceStore.actions.invalidateDataSource(tableId, {
|
await dataSourceStore.actions.invalidateDataSource(tableId, {
|
||||||
|
@ -99,14 +108,16 @@ const deleteRowHandler = async action => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const triggerAutomationHandler = async action => {
|
const triggerAutomationHandler = async action => {
|
||||||
const { fields } = action.parameters
|
const { fields, notificationOverride } = action.parameters
|
||||||
if (fields) {
|
if (fields) {
|
||||||
try {
|
try {
|
||||||
await API.triggerAutomation({
|
await API.triggerAutomation({
|
||||||
automationId: action.parameters.automationId,
|
automationId: action.parameters.automationId,
|
||||||
fields,
|
fields,
|
||||||
})
|
})
|
||||||
notificationStore.actions.success("Automation triggered")
|
if (!notificationOverride) {
|
||||||
|
notificationStore.actions.success("Automation triggered")
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Abort next actions
|
// Abort next actions
|
||||||
return false
|
return false
|
||||||
|
@ -120,7 +131,8 @@ const navigationHandler = action => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryExecutionHandler = async action => {
|
const queryExecutionHandler = async action => {
|
||||||
const { datasourceId, queryId, queryParams } = action.parameters
|
const { datasourceId, queryId, queryParams, notificationOverride } =
|
||||||
|
action.parameters
|
||||||
try {
|
try {
|
||||||
const query = await API.fetchQueryDefinition(queryId)
|
const query = await API.fetchQueryDefinition(queryId)
|
||||||
if (query?.datasourceId == null) {
|
if (query?.datasourceId == null) {
|
||||||
|
@ -136,7 +148,9 @@ const queryExecutionHandler = async action => {
|
||||||
// Trigger a notification and invalidate the datasource as long as this
|
// Trigger a notification and invalidate the datasource as long as this
|
||||||
// was not a readable query
|
// was not a readable query
|
||||||
if (!query.readable) {
|
if (!query.readable) {
|
||||||
notificationStore.actions.success("Query executed successfully")
|
if (!notificationOverride) {
|
||||||
|
notificationStore.actions.success("Query executed successfully")
|
||||||
|
}
|
||||||
await dataSourceStore.actions.invalidateDataSource(query.datasourceId)
|
await dataSourceStore.actions.invalidateDataSource(query.datasourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/frontend-core",
|
"name": "@budibase/frontend-core",
|
||||||
"version": "2.1.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"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.40-alpha.7",
|
"@budibase/bbui": "2.1.46-alpha.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,11 +28,11 @@ export const OperatorOptions = {
|
||||||
},
|
},
|
||||||
MoreThan: {
|
MoreThan: {
|
||||||
value: "rangeLow",
|
value: "rangeLow",
|
||||||
label: "More than",
|
label: "More than or equal to",
|
||||||
},
|
},
|
||||||
LessThan: {
|
LessThan: {
|
||||||
value: "rangeHigh",
|
value: "rangeHigh",
|
||||||
label: "Less than",
|
label: "Less than or equal to",
|
||||||
},
|
},
|
||||||
Contains: {
|
Contains: {
|
||||||
value: "contains",
|
value: "contains",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/sdk",
|
"name": "@budibase/sdk",
|
||||||
"version": "2.1.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"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.40-alpha.7",
|
"version": "2.1.46-alpha.1",
|
||||||
"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.40-alpha.7",
|
"@budibase/backend-core": "2.1.46-alpha.1",
|
||||||
"@budibase/client": "2.1.40-alpha.7",
|
"@budibase/client": "2.1.46-alpha.1",
|
||||||
"@budibase/pro": "2.1.40-alpha.7",
|
"@budibase/pro": "2.1.46-alpha.0",
|
||||||
"@budibase/string-templates": "2.1.40-alpha.7",
|
"@budibase/string-templates": "2.1.46-alpha.1",
|
||||||
"@budibase/types": "2.1.40-alpha.7",
|
"@budibase/types": "2.1.46-alpha.1",
|
||||||
"@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",
|
||||||
|
|
|
@ -11,7 +11,7 @@ GO
|
||||||
CREATE TABLE products
|
CREATE TABLE products
|
||||||
(
|
(
|
||||||
id int IDENTITY(1,1),
|
id int IDENTITY(1,1),
|
||||||
name varchar (20),
|
name varchar (20) NOT NULL,
|
||||||
description varchar(30),
|
description varchar(30),
|
||||||
CONSTRAINT pk_products PRIMARY KEY NONCLUSTERED (id)
|
CONSTRAINT pk_products PRIMARY KEY NONCLUSTERED (id)
|
||||||
);
|
);
|
||||||
|
@ -22,7 +22,7 @@ GO
|
||||||
CREATE TABLE tasks
|
CREATE TABLE tasks
|
||||||
(
|
(
|
||||||
taskid int IDENTITY(1,1),
|
taskid int IDENTITY(1,1),
|
||||||
taskname varchar (20),
|
taskname varchar (20) NOT NULL,
|
||||||
productid int,
|
productid int,
|
||||||
CONSTRAINT pk_tasks PRIMARY KEY NONCLUSTERED (taskid),
|
CONSTRAINT pk_tasks PRIMARY KEY NONCLUSTERED (taskid),
|
||||||
CONSTRAINT fk_products FOREIGN KEY (productid) REFERENCES products (id),
|
CONSTRAINT fk_products FOREIGN KEY (productid) REFERENCES products (id),
|
||||||
|
@ -33,7 +33,7 @@ IF OBJECT_ID ('dbo.people', 'U') IS NOT NULL
|
||||||
GO
|
GO
|
||||||
CREATE TABLE people
|
CREATE TABLE people
|
||||||
(
|
(
|
||||||
name varchar(30),
|
name varchar(30) NOT NULL,
|
||||||
age varchar(20),
|
age varchar(20),
|
||||||
CONSTRAINT pk_people PRIMARY KEY NONCLUSTERED (name, age)
|
CONSTRAINT pk_people PRIMARY KEY NONCLUSTERED (name, age)
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
SELECT 'CREATE DATABASE main'
|
||||||
|
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'main')\gexec
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public."Employee"
|
||||||
|
(
|
||||||
|
id integer NOT NULL,
|
||||||
|
name text COLLATE pg_catalog."default",
|
||||||
|
CONSTRAINT "Employee_pkey" PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
WITH (
|
||||||
|
OIDS = FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO public."Employee" ("id", "name") VALUES (1, 'Alice');
|
||||||
|
INSERT INTO public."Employee" ("id", "name") VALUES (2, 'Bob');
|
||||||
|
CREATE TABLE IF NOT EXISTS public."Skills"
|
||||||
|
(
|
||||||
|
id integer NOT NULL,
|
||||||
|
name text COLLATE pg_catalog."default",
|
||||||
|
CONSTRAINT "Skills_pkey" PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
WITH (
|
||||||
|
OIDS = FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO public."Skills" ("id", "name") VALUES (1, 'Docker');
|
||||||
|
INSERT INTO public."Skills" ("id", "name") VALUES (2, 'Microservices');
|
||||||
|
INSERT INTO public."Skills" ("id", "name") VALUES (3, 'Kubernetes');
|
||||||
|
INSERT INTO public."Skills" ("id", "name") VALUES (4, 'Spring');
|
||||||
|
CREATE TABLE IF NOT EXISTS public."jt_employee_skills_Skills_employee"
|
||||||
|
(
|
||||||
|
employee_id integer,
|
||||||
|
skills_id integer,
|
||||||
|
id integer NOT NULL,
|
||||||
|
CONSTRAINT "jt_employee_skills_Skills_employee_pkey" PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
WITH (
|
||||||
|
OIDS = FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into public."jt_employee_skills_Skills_employee" ("id", "employee_id", "skills_id") VALUES (1, 1, 1);
|
||||||
|
insert into public."jt_employee_skills_Skills_employee" ("id", "employee_id", "skills_id") VALUES (2, 1, 2);
|
||||||
|
insert into public."jt_employee_skills_Skills_employee" ("id", "employee_id", "skills_id") VALUES (3, 1, 3);
|
||||||
|
insert into public."jt_employee_skills_Skills_employee" ("id", "employee_id", "skills_id") VALUES (4, 2, 2);
|
||||||
|
insert into public."jt_employee_skills_Skills_employee" ("id", "employee_id", "skills_id") VALUES (5, 2, 3);
|
||||||
|
insert into public."jt_employee_skills_Skills_employee" ("id", "employee_id", "skills_id") VALUES (6, 2, 4);
|
|
@ -26,7 +26,6 @@ import {
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { USERS_TABLE_SCHEMA } from "../../constants"
|
import { USERS_TABLE_SCHEMA } from "../../constants"
|
||||||
import { buildDefaultDocs } from "../../db/defaultData/datasource_bb_default"
|
import { buildDefaultDocs } from "../../db/defaultData/datasource_bb_default"
|
||||||
|
|
||||||
import { removeAppFromUserRoles } from "../../utilities/workerRequests"
|
import { removeAppFromUserRoles } from "../../utilities/workerRequests"
|
||||||
import {
|
import {
|
||||||
clientLibraryPath,
|
clientLibraryPath,
|
||||||
|
@ -39,18 +38,22 @@ import {
|
||||||
backupClientLibrary,
|
backupClientLibrary,
|
||||||
revertClientLibrary,
|
revertClientLibrary,
|
||||||
} from "../../utilities/fileSystem/clientLibrary"
|
} from "../../utilities/fileSystem/clientLibrary"
|
||||||
import { syncGlobalUsers } from "./user"
|
|
||||||
import { cleanupAutomations } from "../../automations/utils"
|
import { cleanupAutomations } from "../../automations/utils"
|
||||||
import { checkAppMetadata } from "../../automations/logging"
|
import { checkAppMetadata } from "../../automations/logging"
|
||||||
import { getUniqueRows } from "../../utilities/usageQuota/rows"
|
import { getUniqueRows } from "../../utilities/usageQuota/rows"
|
||||||
import { quotas, groups } from "@budibase/pro"
|
import { quotas, groups } from "@budibase/pro"
|
||||||
import { App, Layout, Screen, MigrationType } from "@budibase/types"
|
import {
|
||||||
|
App,
|
||||||
|
Layout,
|
||||||
|
Screen,
|
||||||
|
MigrationType,
|
||||||
|
BBContext,
|
||||||
|
Database,
|
||||||
|
} from "@budibase/types"
|
||||||
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
|
import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
|
||||||
import { enrichPluginURLs } from "../../utilities/plugins"
|
import { enrichPluginURLs } from "../../utilities/plugins"
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
|
|
||||||
const URL_REGEX_SLASH = /\/|\\/g
|
|
||||||
|
|
||||||
// utility function, need to do away with this
|
// utility function, need to do away with this
|
||||||
async function getLayouts() {
|
async function getLayouts() {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
|
@ -74,29 +77,18 @@ async function getScreens() {
|
||||||
).rows.map((row: any) => row.doc)
|
).rows.map((row: any) => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserRoleId(ctx: any) {
|
function getUserRoleId(ctx: BBContext) {
|
||||||
return !ctx.user.role || !ctx.user.role._id
|
return !ctx.user?.role || !ctx.user.role._id
|
||||||
? roles.BUILTIN_ROLE_IDS.PUBLIC
|
? roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
: ctx.user.role._id
|
: ctx.user.role._id
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAppUrl = (ctx: any) => {
|
function checkAppUrl(
|
||||||
// construct the url
|
ctx: BBContext,
|
||||||
let url
|
apps: App[],
|
||||||
if (ctx.request.body.url) {
|
url: string,
|
||||||
// if the url is provided, use that
|
currentAppId?: string
|
||||||
url = encodeURI(ctx.request.body.url)
|
) {
|
||||||
} else if (ctx.request.body.name) {
|
|
||||||
// otherwise use the name
|
|
||||||
url = encodeURI(`${ctx.request.body.name}`)
|
|
||||||
}
|
|
||||||
if (url) {
|
|
||||||
url = `/${url.replace(URL_REGEX_SLASH, "")}`.toLowerCase()
|
|
||||||
}
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkAppUrl = (ctx: any, apps: any, url: any, currentAppId?: string) => {
|
|
||||||
if (currentAppId) {
|
if (currentAppId) {
|
||||||
apps = apps.filter((app: any) => app.appId !== currentAppId)
|
apps = apps.filter((app: any) => app.appId !== currentAppId)
|
||||||
}
|
}
|
||||||
|
@ -105,12 +97,12 @@ const checkAppUrl = (ctx: any, apps: any, url: any, currentAppId?: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkAppName = (
|
function checkAppName(
|
||||||
ctx: any,
|
ctx: BBContext,
|
||||||
apps: any,
|
apps: App[],
|
||||||
name: any,
|
name: string,
|
||||||
currentAppId?: string
|
currentAppId?: string
|
||||||
) => {
|
) {
|
||||||
// TODO: Replace with Joi
|
// TODO: Replace with Joi
|
||||||
if (!name) {
|
if (!name) {
|
||||||
ctx.throw(400, "Name is required")
|
ctx.throw(400, "Name is required")
|
||||||
|
@ -165,14 +157,14 @@ async function createInstance(template: any, includeSampleData: boolean) {
|
||||||
return { _id: appId }
|
return { _id: appId }
|
||||||
}
|
}
|
||||||
|
|
||||||
const addDefaultTables = async (db: any) => {
|
async function addDefaultTables(db: Database) {
|
||||||
const defaultDbDocs = buildDefaultDocs()
|
const defaultDbDocs = buildDefaultDocs()
|
||||||
|
|
||||||
// add in the default db data docs - tables, datasource, rows and links
|
// add in the default db data docs - tables, datasource, rows and links
|
||||||
await db.bulkDocs([...defaultDbDocs])
|
await db.bulkDocs([...defaultDbDocs])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetch = async (ctx: any) => {
|
export async function fetch(ctx: BBContext) {
|
||||||
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
||||||
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
||||||
const apps = (await dbCore.getAllApps({ dev, all })) as App[]
|
const apps = (await dbCore.getAllApps({ dev, all })) as App[]
|
||||||
|
@ -197,7 +189,7 @@ export const fetch = async (ctx: any) => {
|
||||||
ctx.body = await checkAppMetadata(apps)
|
ctx.body = await checkAppMetadata(apps)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchAppDefinition = async (ctx: any) => {
|
export async function fetchAppDefinition(ctx: BBContext) {
|
||||||
const layouts = await getLayouts()
|
const layouts = await getLayouts()
|
||||||
const userRoleId = getUserRoleId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new roles.AccessController()
|
const accessController = new roles.AccessController()
|
||||||
|
@ -212,7 +204,7 @@ export const fetchAppDefinition = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchAppPackage = async (ctx: any) => {
|
export async function fetchAppPackage(ctx: BBContext) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
let application = await db.get(DocumentType.APP_METADATA)
|
let application = await db.get(DocumentType.APP_METADATA)
|
||||||
const layouts = await getLayouts()
|
const layouts = await getLayouts()
|
||||||
|
@ -222,7 +214,7 @@ export const fetchAppPackage = async (ctx: any) => {
|
||||||
application.usedPlugins = enrichPluginURLs(application.usedPlugins)
|
application.usedPlugins = enrichPluginURLs(application.usedPlugins)
|
||||||
|
|
||||||
// Only filter screens if the user is not a builder
|
// Only filter screens if the user is not a builder
|
||||||
if (!(ctx.user.builder && ctx.user.builder.global)) {
|
if (!(ctx.user?.builder && ctx.user.builder.global)) {
|
||||||
const userRoleId = getUserRoleId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new roles.AccessController()
|
const accessController = new roles.AccessController()
|
||||||
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
||||||
|
@ -236,11 +228,12 @@ export const fetchAppPackage = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const performAppCreate = async (ctx: any) => {
|
async function performAppCreate(ctx: BBContext) {
|
||||||
const apps = await dbCore.getAllApps({ dev: true })
|
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
||||||
const name = ctx.request.body.name
|
const name = ctx.request.body.name,
|
||||||
|
possibleUrl = ctx.request.body.url
|
||||||
checkAppName(ctx, apps, name)
|
checkAppName(ctx, apps, name)
|
||||||
const url = getAppUrl(ctx)
|
const url = sdk.applications.getAppUrl({ name, url: possibleUrl })
|
||||||
checkAppUrl(ctx, apps, url)
|
checkAppUrl(ctx, apps, url)
|
||||||
|
|
||||||
const { useTemplate, templateKey, templateString } = ctx.request.body
|
const { useTemplate, templateKey, templateString } = ctx.request.body
|
||||||
|
@ -331,7 +324,7 @@ const performAppCreate = async (ctx: any) => {
|
||||||
return newApplication
|
return newApplication
|
||||||
}
|
}
|
||||||
|
|
||||||
const creationEvents = async (request: any, app: App) => {
|
async function creationEvents(request: any, app: App) {
|
||||||
let creationFns: ((app: App) => Promise<void>)[] = []
|
let creationFns: ((app: App) => Promise<void>)[] = []
|
||||||
|
|
||||||
const body = request.body
|
const body = request.body
|
||||||
|
@ -356,7 +349,7 @@ const creationEvents = async (request: any, app: App) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const appPostCreate = async (ctx: any, app: App) => {
|
async function appPostCreate(ctx: BBContext, app: App) {
|
||||||
const tenantId = tenancy.getTenantId()
|
const tenantId = tenancy.getTenantId()
|
||||||
await migrations.backPopulateMigrations({
|
await migrations.backPopulateMigrations({
|
||||||
type: MigrationType.APP,
|
type: MigrationType.APP,
|
||||||
|
@ -377,7 +370,7 @@ const appPostCreate = async (ctx: any, app: App) => {
|
||||||
if (err.code && err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
|
if (err.code && err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
|
||||||
// this import resulted in row usage exceeding the quota
|
// this import resulted in row usage exceeding the quota
|
||||||
// delete the app
|
// delete the app
|
||||||
// skip pre and post steps as no rows have been added to quotas yet
|
// skip pre- and post-steps as no rows have been added to quotas yet
|
||||||
ctx.params.appId = app.appId
|
ctx.params.appId = app.appId
|
||||||
await destroyApp(ctx)
|
await destroyApp(ctx)
|
||||||
}
|
}
|
||||||
|
@ -387,7 +380,7 @@ const appPostCreate = async (ctx: any, app: App) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const create = async (ctx: any) => {
|
export async function create(ctx: BBContext) {
|
||||||
const newApplication = await quotas.addApp(() => performAppCreate(ctx))
|
const newApplication = await quotas.addApp(() => performAppCreate(ctx))
|
||||||
await appPostCreate(ctx, newApplication)
|
await appPostCreate(ctx, newApplication)
|
||||||
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
||||||
|
@ -397,14 +390,15 @@ export const create = async (ctx: any) => {
|
||||||
|
|
||||||
// This endpoint currently operates as a PATCH rather than a PUT
|
// This endpoint currently operates as a PATCH rather than a PUT
|
||||||
// Thus name and url fields are handled only if present
|
// Thus name and url fields are handled only if present
|
||||||
export const update = async (ctx: any) => {
|
export async function update(ctx: BBContext) {
|
||||||
const apps = await dbCore.getAllApps({ dev: true })
|
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
||||||
// validation
|
// validation
|
||||||
const name = ctx.request.body.name
|
const name = ctx.request.body.name,
|
||||||
|
possibleUrl = ctx.request.body.url
|
||||||
if (name) {
|
if (name) {
|
||||||
checkAppName(ctx, apps, name, ctx.params.appId)
|
checkAppName(ctx, apps, name, ctx.params.appId)
|
||||||
}
|
}
|
||||||
const url = getAppUrl(ctx)
|
const url = sdk.applications.getAppUrl({ name, url: possibleUrl })
|
||||||
if (url) {
|
if (url) {
|
||||||
checkAppUrl(ctx, apps, url, ctx.params.appId)
|
checkAppUrl(ctx, apps, url, ctx.params.appId)
|
||||||
ctx.request.body.url = url
|
ctx.request.body.url = url
|
||||||
|
@ -416,7 +410,7 @@ export const update = async (ctx: any) => {
|
||||||
ctx.body = app
|
ctx.body = app
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateClient = async (ctx: any) => {
|
export async function updateClient(ctx: BBContext) {
|
||||||
// Get current app version
|
// Get current app version
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentType.APP_METADATA)
|
const application = await db.get(DocumentType.APP_METADATA)
|
||||||
|
@ -440,7 +434,7 @@ export const updateClient = async (ctx: any) => {
|
||||||
ctx.body = app
|
ctx.body = app
|
||||||
}
|
}
|
||||||
|
|
||||||
export const revertClient = async (ctx: any) => {
|
export async function revertClient(ctx: BBContext) {
|
||||||
// Check app can be reverted
|
// Check app can be reverted
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentType.APP_METADATA)
|
const application = await db.get(DocumentType.APP_METADATA)
|
||||||
|
@ -466,12 +460,15 @@ export const revertClient = async (ctx: any) => {
|
||||||
ctx.body = app
|
ctx.body = app
|
||||||
}
|
}
|
||||||
|
|
||||||
const destroyApp = async (ctx: any) => {
|
async function destroyApp(ctx: BBContext) {
|
||||||
let appId = ctx.params.appId
|
let appId = ctx.params.appId
|
||||||
let isUnpublish = ctx.query && ctx.query.unpublish
|
let isUnpublish = ctx.query && ctx.query.unpublish
|
||||||
|
|
||||||
if (isUnpublish) {
|
if (isUnpublish) {
|
||||||
appId = dbCore.getProdAppID(appId)
|
appId = dbCore.getProdAppID(appId)
|
||||||
|
const devAppId = dbCore.getDevAppID(appId)
|
||||||
|
// sync before removing the published app
|
||||||
|
await sdk.applications.syncApp(devAppId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = isUnpublish ? context.getProdAppDB() : context.getAppDB()
|
const db = isUnpublish ? context.getProdAppDB() : context.getAppDB()
|
||||||
|
@ -501,12 +498,12 @@ const destroyApp = async (ctx: any) => {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const preDestroyApp = async (ctx: any) => {
|
async function preDestroyApp(ctx: BBContext) {
|
||||||
const { rows } = await getUniqueRows([ctx.params.appId])
|
const { rows } = await getUniqueRows([ctx.params.appId])
|
||||||
ctx.rowCount = rows.length
|
ctx.rowCount = rows.length
|
||||||
}
|
}
|
||||||
|
|
||||||
const postDestroyApp = async (ctx: any) => {
|
async function postDestroyApp(ctx: BBContext) {
|
||||||
const rowCount = ctx.rowCount
|
const rowCount = ctx.rowCount
|
||||||
await groups.cleanupApp(ctx.params.appId)
|
await groups.cleanupApp(ctx.params.appId)
|
||||||
if (rowCount) {
|
if (rowCount) {
|
||||||
|
@ -514,7 +511,7 @@ const postDestroyApp = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const destroy = async (ctx: any) => {
|
export async function destroy(ctx: BBContext) {
|
||||||
await preDestroyApp(ctx)
|
await preDestroyApp(ctx)
|
||||||
const result = await destroyApp(ctx)
|
const result = await destroyApp(ctx)
|
||||||
await postDestroyApp(ctx)
|
await postDestroyApp(ctx)
|
||||||
|
@ -522,62 +519,16 @@ export const destroy = async (ctx: any) => {
|
||||||
ctx.body = result
|
ctx.body = result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sync = async (ctx: any, next: any) => {
|
export async function sync(ctx: BBContext) {
|
||||||
if (env.DISABLE_AUTO_PROD_APP_SYNC) {
|
|
||||||
ctx.status = 200
|
|
||||||
ctx.body = {
|
|
||||||
message:
|
|
||||||
"App sync disabled. You can reenable with the DISABLE_AUTO_PROD_APP_SYNC environment variable.",
|
|
||||||
}
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
const appId = ctx.params.appId
|
const appId = ctx.params.appId
|
||||||
if (!dbCore.isDevAppID(appId)) {
|
|
||||||
ctx.throw(400, "This action cannot be performed for production apps")
|
|
||||||
}
|
|
||||||
|
|
||||||
// replicate prod to dev
|
|
||||||
const prodAppId = dbCore.getProdAppID(appId)
|
|
||||||
|
|
||||||
// specific case, want to make sure setup is skipped
|
|
||||||
const prodDb = context.getProdAppDB({ skip_setup: true })
|
|
||||||
const exists = await prodDb.exists()
|
|
||||||
if (!exists) {
|
|
||||||
// the database doesn't exist. Don't replicate
|
|
||||||
ctx.status = 200
|
|
||||||
ctx.body = {
|
|
||||||
message: "App sync not required, app not deployed.",
|
|
||||||
}
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
const replication = new dbCore.Replication({
|
|
||||||
source: prodAppId,
|
|
||||||
target: appId,
|
|
||||||
})
|
|
||||||
let error
|
|
||||||
try {
|
try {
|
||||||
await replication.replicate(replication.appReplicateOpts())
|
ctx.body = await sdk.applications.syncApp(appId)
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
error = err
|
ctx.throw(err.status || 400, err.message)
|
||||||
} finally {
|
|
||||||
await replication.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync the users
|
|
||||||
await syncGlobalUsers()
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
ctx.throw(400, error)
|
|
||||||
} else {
|
|
||||||
ctx.body = {
|
|
||||||
message: "App sync completed successfully.",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateAppPackage = async (appPackage: any, appId: any) => {
|
export async function updateAppPackage(appPackage: any, appId: any) {
|
||||||
return context.doInAppContext(appId, async () => {
|
return context.doInAppContext(appId, async () => {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentType.APP_METADATA)
|
const application = await db.get(DocumentType.APP_METADATA)
|
||||||
|
@ -598,7 +549,7 @@ export const updateAppPackage = async (appPackage: any, appId: any) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const migrateAppNavigation = async () => {
|
async function migrateAppNavigation() {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const existing: App = await db.get(DocumentType.APP_METADATA)
|
const existing: App = await db.get(DocumentType.APP_METADATA)
|
||||||
const layouts: Layout[] = await getLayouts()
|
const layouts: Layout[] = await getLayouts()
|
||||||
|
|
|
@ -22,6 +22,7 @@ async function createApp(appName: string, appDirectory: string) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// @ts-ignore
|
||||||
return create(ctx)
|
return create(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
} from "../../../automations/utils"
|
} from "../../../automations/utils"
|
||||||
import { backups } from "@budibase/pro"
|
import { backups } from "@budibase/pro"
|
||||||
import { AppBackupTrigger } from "@budibase/types"
|
import { AppBackupTrigger } from "@budibase/types"
|
||||||
|
import sdk from "../../../sdk"
|
||||||
|
|
||||||
// the max time we can wait for an invalidation to complete before considering it failed
|
// the max time we can wait for an invalidation to complete before considering it failed
|
||||||
const MAX_PENDING_TIME_MS = 30 * 60000
|
const MAX_PENDING_TIME_MS = 30 * 60000
|
||||||
|
@ -86,6 +87,11 @@ async function initDeployedApp(prodAppId: any) {
|
||||||
}
|
}
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
console.log("Enabled cron triggers for deployed app..")
|
console.log("Enabled cron triggers for deployed app..")
|
||||||
|
// sync the automations back to the dev DB - since there is now cron
|
||||||
|
// information attached
|
||||||
|
await sdk.applications.syncApp(dbCore.getDevAppID(prodAppId), {
|
||||||
|
automationOnly: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deployApp(deployment: any, userId: string) {
|
async function deployApp(deployment: any, userId: string) {
|
||||||
|
|
|
@ -5,9 +5,18 @@ import {
|
||||||
saveGlobalUser,
|
saveGlobalUser,
|
||||||
} from "../../../utilities/workerRequests"
|
} from "../../../utilities/workerRequests"
|
||||||
import { publicApiUserFix } from "../../../utilities/users"
|
import { publicApiUserFix } from "../../../utilities/users"
|
||||||
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
import { search as stringSearch } from "./utils"
|
import { search as stringSearch } from "./utils"
|
||||||
|
import { BBContext, User } from "@budibase/types"
|
||||||
|
|
||||||
function getUser(ctx: any, userId?: string) {
|
function isLoggedInUser(ctx: BBContext, user: User) {
|
||||||
|
const loggedInId = ctx.user?._id
|
||||||
|
const globalUserId = dbCore.getGlobalIDFromUserMetadataID(loggedInId!)
|
||||||
|
// check both just incase
|
||||||
|
return globalUserId === user._id || loggedInId === user._id
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUser(ctx: BBContext, userId?: string) {
|
||||||
if (userId) {
|
if (userId) {
|
||||||
ctx.params = { userId }
|
ctx.params = { userId }
|
||||||
} else if (!ctx.params?.userId) {
|
} else if (!ctx.params?.userId) {
|
||||||
|
@ -16,37 +25,47 @@ function getUser(ctx: any, userId?: string) {
|
||||||
return readGlobalUser(ctx)
|
return readGlobalUser(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function search(ctx: any, next: any) {
|
export async function search(ctx: BBContext, next: any) {
|
||||||
const { name } = ctx.request.body
|
const { name } = ctx.request.body
|
||||||
const users = await allGlobalUsers(ctx)
|
const users = await allGlobalUsers(ctx)
|
||||||
ctx.body = stringSearch(users, name, "email")
|
ctx.body = stringSearch(users, name, "email")
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(ctx: any, next: any) {
|
export async function create(ctx: BBContext, next: any) {
|
||||||
const response = await saveGlobalUser(publicApiUserFix(ctx))
|
const response = await saveGlobalUser(publicApiUserFix(ctx))
|
||||||
ctx.body = await getUser(ctx, response._id)
|
ctx.body = await getUser(ctx, response._id)
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function read(ctx: any, next: any) {
|
export async function read(ctx: BBContext, next: any) {
|
||||||
ctx.body = await readGlobalUser(ctx)
|
ctx.body = await readGlobalUser(ctx)
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update(ctx: any, next: any) {
|
export async function update(ctx: BBContext, next: any) {
|
||||||
const user = await readGlobalUser(ctx)
|
const user = await readGlobalUser(ctx)
|
||||||
ctx.request.body = {
|
ctx.request.body = {
|
||||||
...ctx.request.body,
|
...ctx.request.body,
|
||||||
_rev: user._rev,
|
_rev: user._rev,
|
||||||
}
|
}
|
||||||
|
// disallow updating your own role - always overwrite with DB roles
|
||||||
|
if (isLoggedInUser(ctx, user)) {
|
||||||
|
ctx.request.body.builder = user.builder
|
||||||
|
ctx.request.body.admin = user.admin
|
||||||
|
ctx.request.body.roles = user.roles
|
||||||
|
}
|
||||||
const response = await saveGlobalUser(publicApiUserFix(ctx))
|
const response = await saveGlobalUser(publicApiUserFix(ctx))
|
||||||
ctx.body = await getUser(ctx, response._id)
|
ctx.body = await getUser(ctx, response._id)
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function destroy(ctx: any, next: any) {
|
export async function destroy(ctx: BBContext, next: any) {
|
||||||
const user = await getUser(ctx)
|
const user = await getUser(ctx)
|
||||||
|
// disallow deleting yourself
|
||||||
|
if (isLoggedInUser(ctx, user)) {
|
||||||
|
ctx.throw(405, "Cannot delete user using its own API key.")
|
||||||
|
}
|
||||||
await deleteGlobalUser(ctx)
|
await deleteGlobalUser(ctx)
|
||||||
ctx.body = user
|
ctx.body = user
|
||||||
await next()
|
await next()
|
||||||
|
|
|
@ -588,7 +588,10 @@ export class ExternalRequest {
|
||||||
for (let [colName, { isMany, rows, tableId }] of Object.entries(related)) {
|
for (let [colName, { isMany, rows, tableId }] of Object.entries(related)) {
|
||||||
const table: Table | undefined = this.getTable(tableId)
|
const table: Table | undefined = this.getTable(tableId)
|
||||||
// if its not the foreign key skip it, nothing to do
|
// if its not the foreign key skip it, nothing to do
|
||||||
if (!table || (table.primary && table.primary.indexOf(colName) !== -1)) {
|
if (
|
||||||
|
!table ||
|
||||||
|
(!isMany && table.primary && table.primary.indexOf(colName) !== -1)
|
||||||
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
import {
|
import { generateUserMetadataID, generateUserFlagID } from "../../db/utils"
|
||||||
generateUserMetadataID,
|
|
||||||
getUserMetadataParams,
|
|
||||||
generateUserFlagID,
|
|
||||||
} from "../../db/utils"
|
|
||||||
import { InternalTables } from "../../db/utils"
|
import { InternalTables } from "../../db/utils"
|
||||||
import { getGlobalUsers, getRawGlobalUser } from "../../utilities/global"
|
import { getGlobalUsers, getRawGlobalUser } from "../../utilities/global"
|
||||||
import { getFullUser } from "../../utilities/users"
|
import { getFullUser } from "../../utilities/users"
|
||||||
import { isEqual } from "lodash"
|
|
||||||
import {
|
import {
|
||||||
context,
|
context,
|
||||||
constants,
|
constants,
|
||||||
|
@ -14,59 +9,7 @@ import {
|
||||||
db as dbCore,
|
db as dbCore,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { BBContext, User } from "@budibase/types"
|
import { BBContext, User } from "@budibase/types"
|
||||||
|
import sdk from "../../sdk"
|
||||||
async function rawMetadata() {
|
|
||||||
const db = context.getAppDB()
|
|
||||||
return (
|
|
||||||
await db.allDocs(
|
|
||||||
getUserMetadataParams(null, {
|
|
||||||
include_docs: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).rows.map(row => row.doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
function combineMetadataAndUser(user: any, metadata: any) {
|
|
||||||
// skip users with no access
|
|
||||||
if (user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
delete user._rev
|
|
||||||
const metadataId = generateUserMetadataID(user._id)
|
|
||||||
const newDoc = {
|
|
||||||
...user,
|
|
||||||
_id: metadataId,
|
|
||||||
tableId: InternalTables.USER_METADATA,
|
|
||||||
}
|
|
||||||
const found = Array.isArray(metadata)
|
|
||||||
? metadata.find(doc => doc._id === metadataId)
|
|
||||||
: metadata
|
|
||||||
// copy rev over for the purposes of equality check
|
|
||||||
if (found) {
|
|
||||||
newDoc._rev = found._rev
|
|
||||||
}
|
|
||||||
if (found == null || !isEqual(newDoc, found)) {
|
|
||||||
return {
|
|
||||||
...found,
|
|
||||||
...newDoc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function syncGlobalUsers() {
|
|
||||||
// sync user metadata
|
|
||||||
const db = context.getAppDB()
|
|
||||||
const [users, metadata] = await Promise.all([getGlobalUsers(), rawMetadata()])
|
|
||||||
const toWrite = []
|
|
||||||
for (let user of users) {
|
|
||||||
const combined = await combineMetadataAndUser(user, metadata)
|
|
||||||
if (combined) {
|
|
||||||
toWrite.push(combined)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await db.bulkDocs(toWrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function syncUser(ctx: BBContext) {
|
export async function syncUser(ctx: BBContext) {
|
||||||
let deleting = false,
|
let deleting = false,
|
||||||
|
@ -123,7 +66,7 @@ export async function syncUser(ctx: BBContext) {
|
||||||
metadata.roleId = roleId
|
metadata.roleId = roleId
|
||||||
}
|
}
|
||||||
let combined = !deleting
|
let combined = !deleting
|
||||||
? combineMetadataAndUser(user, metadata)
|
? sdk.users.combineMetadataAndUser(user, metadata)
|
||||||
: {
|
: {
|
||||||
...metadata,
|
...metadata,
|
||||||
status: constants.UserStatus.INACTIVE,
|
status: constants.UserStatus.INACTIVE,
|
||||||
|
@ -143,7 +86,7 @@ export async function syncUser(ctx: BBContext) {
|
||||||
|
|
||||||
export async function fetchMetadata(ctx: BBContext) {
|
export async function fetchMetadata(ctx: BBContext) {
|
||||||
const global = await getGlobalUsers()
|
const global = await getGlobalUsers()
|
||||||
const metadata = await rawMetadata()
|
const metadata = await sdk.users.rawUserMetadata()
|
||||||
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
|
||||||
|
|
|
@ -1,41 +1,23 @@
|
||||||
const jestOpenAPI = require("jest-openapi").default
|
const jestOpenAPI = require("jest-openapi").default
|
||||||
const generateSchema = require("../../../../../specs/generate")
|
const generateSchema = require("../../../../../specs/generate")
|
||||||
const setup = require("../../tests/utilities")
|
const setup = require("../../tests/utilities")
|
||||||
const { checkSlashesInUrl } = require("../../../../utilities")
|
const { generateMakeRequest } = require("./utils")
|
||||||
|
|
||||||
const yamlPath = generateSchema()
|
const yamlPath = generateSchema()
|
||||||
jestOpenAPI(yamlPath)
|
jestOpenAPI(yamlPath)
|
||||||
|
|
||||||
let request = setup.getRequest()
|
|
||||||
let config = setup.getConfig()
|
let config = setup.getConfig()
|
||||||
let apiKey, table, app
|
let apiKey, table, app, makeRequest
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
app = await config.init()
|
app = await config.init()
|
||||||
table = await config.updateTable()
|
table = await config.updateTable()
|
||||||
apiKey = await config.generateApiKey()
|
apiKey = await config.generateApiKey()
|
||||||
|
makeRequest = generateMakeRequest(apiKey, setup)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
async function makeRequest(method, endpoint, body, appId = config.getAppId()) {
|
|
||||||
const extraHeaders = {
|
|
||||||
"x-budibase-api-key": apiKey,
|
|
||||||
}
|
|
||||||
if (appId) {
|
|
||||||
extraHeaders["x-budibase-app-id"] = appId
|
|
||||||
}
|
|
||||||
const req = request
|
|
||||||
[method](checkSlashesInUrl(`/api/public/v1/${endpoint}`))
|
|
||||||
.set(config.defaultHeaders(extraHeaders))
|
|
||||||
if (body) {
|
|
||||||
req.send(body)
|
|
||||||
}
|
|
||||||
const res = await req
|
|
||||||
expect(res.body).toBeDefined()
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("check the applications endpoints", () => {
|
describe("check the applications endpoints", () => {
|
||||||
it("should allow retrieving applications through search", async () => {
|
it("should allow retrieving applications through search", async () => {
|
||||||
const res = await makeRequest("post", "/applications/search")
|
const res = await makeRequest("post", "/applications/search")
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
const setup = require("../../tests/utilities")
|
||||||
|
const { generateMakeRequest } = require("./utils")
|
||||||
|
|
||||||
|
const workerRequests = require("../../../../utilities/workerRequests")
|
||||||
|
|
||||||
|
let config = setup.getConfig()
|
||||||
|
let apiKey, globalUser, makeRequest
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
globalUser = await config.globalUser()
|
||||||
|
apiKey = await config.generateApiKey(globalUser._id)
|
||||||
|
makeRequest = generateMakeRequest(apiKey, setup)
|
||||||
|
workerRequests.readGlobalUser.mockReturnValue(globalUser)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
describe("check user endpoints", () => {
|
||||||
|
it("should not allow a user to update their own roles", async () => {
|
||||||
|
const res = await makeRequest("put", `/users/${globalUser._id}`, {
|
||||||
|
...globalUser,
|
||||||
|
roles: {
|
||||||
|
"app_1": "ADMIN",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(workerRequests.saveGlobalUser.mock.lastCall[0].body.data.roles["app_1"]).toBeUndefined()
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(res.body.data.roles["app_1"]).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not allow a user to delete themselves", async () => {
|
||||||
|
const res = await makeRequest("delete", `/users/${globalUser._id}`)
|
||||||
|
expect(res.status).toBe(405)
|
||||||
|
expect(workerRequests.deleteGlobalUser.mock.lastCall).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { checkSlashesInUrl } from "../../../../utilities"
|
||||||
|
|
||||||
|
export function generateMakeRequest(apiKey: string, setup: any) {
|
||||||
|
const request = setup.getRequest()
|
||||||
|
const config = setup.getConfig()
|
||||||
|
return async (
|
||||||
|
method: string,
|
||||||
|
endpoint: string,
|
||||||
|
body?: any,
|
||||||
|
intAppId: string = config.getAppId()
|
||||||
|
) => {
|
||||||
|
const extraHeaders: any = {
|
||||||
|
"x-budibase-api-key": apiKey,
|
||||||
|
}
|
||||||
|
if (intAppId) {
|
||||||
|
extraHeaders["x-budibase-app-id"] = intAppId
|
||||||
|
}
|
||||||
|
const req = request[method](
|
||||||
|
checkSlashesInUrl(`/api/public/v1/${endpoint}`)
|
||||||
|
).set(config.defaultHeaders(extraHeaders))
|
||||||
|
if (body) {
|
||||||
|
req.send(body)
|
||||||
|
}
|
||||||
|
const res = await req
|
||||||
|
expect(res.body).toBeDefined()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,6 @@ const {
|
||||||
checkBuilderEndpoint,
|
checkBuilderEndpoint,
|
||||||
} = require("./utilities/TestFunctions")
|
} = require("./utilities/TestFunctions")
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
const { basicScreen, basicLayout } = setup.structures
|
|
||||||
const { AppStatus } = require("../../../db/utils")
|
const { AppStatus } = require("../../../db/utils")
|
||||||
const { events } = require("@budibase/backend-core")
|
const { events } = require("@budibase/backend-core")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
export interface MSSQLTablesResponse {
|
||||||
|
TABLE_CATALOG: string
|
||||||
|
TABLE_SCHEMA: string
|
||||||
|
TABLE_NAME: string
|
||||||
|
TABLE_TYPE: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MSSQLColumn {
|
||||||
|
IS_COMPUTED: number
|
||||||
|
IS_IDENTITY: number
|
||||||
|
TABLE_CATALOG: string
|
||||||
|
TABLE_SCHEMA: string
|
||||||
|
TABLE_NAME: string
|
||||||
|
COLUMN_NAME: string
|
||||||
|
ORDINAL_POSITION: number
|
||||||
|
COLUMN_DEFAULT: null | any
|
||||||
|
IS_NULLABLE: "NO" | "YES"
|
||||||
|
DATA_TYPE: string
|
||||||
|
CHARACTER_MAXIMUM_LENGTH: null | number
|
||||||
|
CHARACTER_OCTET_LENGTH: null | number
|
||||||
|
NUMERIC_PRECISION: null | number
|
||||||
|
NUMERIC_PRECISION_RADIX: null | number
|
||||||
|
NUMERIC_SCALE: null | number
|
||||||
|
DATETIME_PRECISION: null | string
|
||||||
|
CHARACTER_SET_CATALOG: null | string
|
||||||
|
CHARACTER_SET_SCHEMA: null | string
|
||||||
|
CHARACTER_SET_NAME: null | string
|
||||||
|
COLLATION_CATALOG: null | string
|
||||||
|
COLLATION_SCHEMA: null | string
|
||||||
|
COLLATION_NAME: null | string
|
||||||
|
DOMAIN_CATALOG: null | string
|
||||||
|
DOMAIN_SCHEMA: null | string
|
||||||
|
DOMAIN_NAME: null | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostgresColumn {
|
||||||
|
table_catalog: string
|
||||||
|
table_schema: string
|
||||||
|
table_name: string
|
||||||
|
column_name: string
|
||||||
|
ordinal_position: number
|
||||||
|
column_default: null | any
|
||||||
|
is_nullable: "NO" | "YES"
|
||||||
|
data_type: string
|
||||||
|
character_maximum_length: null | number
|
||||||
|
character_octet_length: null | number
|
||||||
|
numeric_precision: null | number
|
||||||
|
numeric_precision_radix: null | number
|
||||||
|
numeric_scale: null | number
|
||||||
|
datetime_precision: null | string
|
||||||
|
interval_type: null | string
|
||||||
|
interval_precision: null | string
|
||||||
|
character_set_catalog: null | string
|
||||||
|
character_set_schema: null | string
|
||||||
|
character_set_name: null | string
|
||||||
|
collation_catalog: null | string
|
||||||
|
collation_schema: null | string
|
||||||
|
collation_name: null | string
|
||||||
|
domain_catalog: null | string
|
||||||
|
domain_schema: null | string
|
||||||
|
domain_name: null | string
|
||||||
|
udt_catalog: string
|
||||||
|
udt_schema: string
|
||||||
|
udt_name: string
|
||||||
|
scope_catalog: null | string
|
||||||
|
scope_schema: null | string
|
||||||
|
scope_name: null | string
|
||||||
|
maximum_cardinality: null | string
|
||||||
|
dtd_identifier: string
|
||||||
|
is_self_referencing: "NO" | "YES"
|
||||||
|
is_identity: "NO" | "YES"
|
||||||
|
identity_generation: null | number
|
||||||
|
identity_start: null | number
|
||||||
|
identity_increment: null | number
|
||||||
|
identity_maximum: null | number
|
||||||
|
identity_minimum: null | number
|
||||||
|
identity_cycle: "NO" | "YES"
|
||||||
|
is_generated: "NEVER"
|
||||||
|
generation_expression: null | string
|
||||||
|
is_updatable: "NO" | "YES"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MySQLColumn {
|
||||||
|
Field: string
|
||||||
|
Type: string
|
||||||
|
Null: "NO" | "YES"
|
||||||
|
Key: "PRI" | "MUL" | ""
|
||||||
|
Default: null | any
|
||||||
|
Extra: null | string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw query response
|
||||||
|
*/
|
||||||
|
export interface OracleColumnsResponse {
|
||||||
|
TABLE_NAME: string
|
||||||
|
COLUMN_NAME: string
|
||||||
|
DATA_TYPE: string
|
||||||
|
DATA_DEFAULT: null | string
|
||||||
|
COLUMN_ID: number
|
||||||
|
CONSTRAINT_NAME: null | string
|
||||||
|
CONSTRAINT_TYPE: null | string
|
||||||
|
R_CONSTRAINT_NAME: null | string
|
||||||
|
SEARCH_CONDITION: null | string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An oracle constraint
|
||||||
|
*/
|
||||||
|
export interface OracleConstraint {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
relatedConstraintName: null | string
|
||||||
|
searchCondition: null | string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An oracle column and it's related constraints
|
||||||
|
*/
|
||||||
|
export interface OracleColumn {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
default: null | string
|
||||||
|
id: number
|
||||||
|
constraints: { [key: string]: OracleConstraint }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An oracle table and it's related columns
|
||||||
|
*/
|
||||||
|
export interface OracleTable {
|
||||||
|
name: string
|
||||||
|
columns: { [key: string]: OracleColumn }
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import {
|
||||||
SqlClient,
|
SqlClient,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
import Sql from "./base/sql"
|
import Sql from "./base/sql"
|
||||||
|
import { MSSQLTablesResponse, MSSQLColumn } from "./base/types"
|
||||||
|
|
||||||
const sqlServer = require("mssql")
|
const sqlServer = require("mssql")
|
||||||
const DEFAULT_SCHEMA = "dbo"
|
const DEFAULT_SCHEMA = "dbo"
|
||||||
|
@ -31,13 +32,6 @@ interface MSSQLConfig {
|
||||||
encrypt?: boolean
|
encrypt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TablesResponse {
|
|
||||||
TABLE_CATALOG: string
|
|
||||||
TABLE_SCHEMA: string
|
|
||||||
TABLE_NAME: string
|
|
||||||
TABLE_TYPE: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const SCHEMA: Integration = {
|
const SCHEMA: Integration = {
|
||||||
docs: "https://github.com/tediousjs/node-mssql",
|
docs: "https://github.com/tediousjs/node-mssql",
|
||||||
plus: true,
|
plus: true,
|
||||||
|
@ -210,7 +204,7 @@ class SqlServerIntegration extends Sql implements DatasourcePlus {
|
||||||
*/
|
*/
|
||||||
async buildSchema(datasourceId: string, entities: Record<string, Table>) {
|
async buildSchema(datasourceId: string, entities: Record<string, Table>) {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
let tableInfo: TablesResponse[] = await this.runSQL(this.TABLES_SQL)
|
let tableInfo: MSSQLTablesResponse[] = await this.runSQL(this.TABLES_SQL)
|
||||||
if (tableInfo == null || !Array.isArray(tableInfo)) {
|
if (tableInfo == null || !Array.isArray(tableInfo)) {
|
||||||
throw "Unable to get list of tables in database"
|
throw "Unable to get list of tables in database"
|
||||||
}
|
}
|
||||||
|
@ -228,15 +222,20 @@ class SqlServerIntegration extends Sql implements DatasourcePlus {
|
||||||
// find primary key constraints
|
// find primary key constraints
|
||||||
const constraints = await this.runSQL(this.getConstraintsSQL(tableName))
|
const constraints = await this.runSQL(this.getConstraintsSQL(tableName))
|
||||||
// find the computed and identity columns (auto columns)
|
// find the computed and identity columns (auto columns)
|
||||||
const columns = await this.runSQL(this.getAutoColumnsSQL(tableName))
|
const columns: MSSQLColumn[] = await this.runSQL(
|
||||||
|
this.getAutoColumnsSQL(tableName)
|
||||||
|
)
|
||||||
const primaryKeys = constraints
|
const primaryKeys = constraints
|
||||||
.filter(
|
.filter(
|
||||||
(constraint: any) => constraint.CONSTRAINT_TYPE === "PRIMARY KEY"
|
(constraint: any) => constraint.CONSTRAINT_TYPE === "PRIMARY KEY"
|
||||||
)
|
)
|
||||||
.map((constraint: any) => constraint.COLUMN_NAME)
|
.map((constraint: any) => constraint.COLUMN_NAME)
|
||||||
const autoColumns = columns
|
const autoColumns = columns
|
||||||
.filter((col: any) => col.IS_COMPUTED || col.IS_IDENTITY)
|
.filter(col => col.IS_COMPUTED || col.IS_IDENTITY)
|
||||||
.map((col: any) => col.COLUMN_NAME)
|
.map(col => col.COLUMN_NAME)
|
||||||
|
const requiredColumns = columns
|
||||||
|
.filter(col => col.IS_NULLABLE === "NO")
|
||||||
|
.map(col => col.COLUMN_NAME)
|
||||||
|
|
||||||
let schema: TableSchema = {}
|
let schema: TableSchema = {}
|
||||||
for (let def of definition) {
|
for (let def of definition) {
|
||||||
|
@ -245,8 +244,11 @@ class SqlServerIntegration extends Sql implements DatasourcePlus {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
schema[name] = {
|
schema[name] = {
|
||||||
autocolumn: !!autoColumns.find((col: string) => col === name),
|
autocolumn: !!autoColumns.find(col => col === name),
|
||||||
name: name,
|
name: name,
|
||||||
|
constraints: {
|
||||||
|
presence: requiredColumns.find(col => col === name),
|
||||||
|
},
|
||||||
...convertSqlType(def.DATA_TYPE),
|
...convertSqlType(def.DATA_TYPE),
|
||||||
externalType: def.DATA_TYPE,
|
externalType: def.DATA_TYPE,
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
const { NUMBER_REGEX } = require("../utilities")
|
const { NUMBER_REGEX } = require("../utilities")
|
||||||
import Sql from "./base/sql"
|
import Sql from "./base/sql"
|
||||||
|
import { MySQLColumn } from "./base/types"
|
||||||
|
|
||||||
const mysql = require("mysql2/promise")
|
const mysql = require("mysql2/promise")
|
||||||
|
|
||||||
|
@ -203,11 +204,11 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// get the tables first
|
// get the tables first
|
||||||
const tablesResp = await this.internalQuery(
|
const tablesResp: Record<string, string>[] = await this.internalQuery(
|
||||||
{ sql: "SHOW TABLES;" },
|
{ sql: "SHOW TABLES;" },
|
||||||
{ connect: false }
|
{ connect: false }
|
||||||
)
|
)
|
||||||
const tableNames = tablesResp.map(
|
const tableNames: string[] = tablesResp.map(
|
||||||
(obj: any) =>
|
(obj: any) =>
|
||||||
obj[`Tables_in_${database}`] ||
|
obj[`Tables_in_${database}`] ||
|
||||||
obj[`Tables_in_${database.toLowerCase()}`]
|
obj[`Tables_in_${database.toLowerCase()}`]
|
||||||
|
@ -215,7 +216,7 @@ class MySQLIntegration extends Sql implements DatasourcePlus {
|
||||||
for (let tableName of tableNames) {
|
for (let tableName of tableNames) {
|
||||||
const primaryKeys = []
|
const primaryKeys = []
|
||||||
const schema: TableSchema = {}
|
const schema: TableSchema = {}
|
||||||
const descResp = await this.internalQuery(
|
const descResp: MySQLColumn[] = await this.internalQuery(
|
||||||
{ sql: `DESCRIBE \`${tableName}\`;` },
|
{ sql: `DESCRIBE \`${tableName}\`;` },
|
||||||
{ connect: false }
|
{ connect: false }
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,6 +24,12 @@ import {
|
||||||
ExecuteOptions,
|
ExecuteOptions,
|
||||||
Result,
|
Result,
|
||||||
} from "oracledb"
|
} from "oracledb"
|
||||||
|
import {
|
||||||
|
OracleTable,
|
||||||
|
OracleColumn,
|
||||||
|
OracleColumnsResponse,
|
||||||
|
OracleConstraint,
|
||||||
|
} from "./base/types"
|
||||||
let oracledb: any
|
let oracledb: any
|
||||||
try {
|
try {
|
||||||
oracledb = require("oracledb")
|
oracledb = require("oracledb")
|
||||||
|
@ -89,50 +95,6 @@ const SCHEMA: Integration = {
|
||||||
|
|
||||||
const UNSUPPORTED_TYPES = ["BLOB", "CLOB", "NCLOB"]
|
const UNSUPPORTED_TYPES = ["BLOB", "CLOB", "NCLOB"]
|
||||||
|
|
||||||
/**
|
|
||||||
* Raw query response
|
|
||||||
*/
|
|
||||||
interface ColumnsResponse {
|
|
||||||
TABLE_NAME: string
|
|
||||||
COLUMN_NAME: string
|
|
||||||
DATA_TYPE: string
|
|
||||||
DATA_DEFAULT: string | null
|
|
||||||
COLUMN_ID: number
|
|
||||||
CONSTRAINT_NAME: string | null
|
|
||||||
CONSTRAINT_TYPE: string | null
|
|
||||||
R_CONSTRAINT_NAME: string | null
|
|
||||||
SEARCH_CONDITION: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An oracle constraint
|
|
||||||
*/
|
|
||||||
interface OracleConstraint {
|
|
||||||
name: string
|
|
||||||
type: string
|
|
||||||
relatedConstraintName: string | null
|
|
||||||
searchCondition: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An oracle column and it's related constraints
|
|
||||||
*/
|
|
||||||
interface OracleColumn {
|
|
||||||
name: string
|
|
||||||
type: string
|
|
||||||
default: string | null
|
|
||||||
id: number
|
|
||||||
constraints: { [key: string]: OracleConstraint }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An oracle table and it's related columns
|
|
||||||
*/
|
|
||||||
interface OracleTable {
|
|
||||||
name: string
|
|
||||||
columns: { [key: string]: OracleColumn }
|
|
||||||
}
|
|
||||||
|
|
||||||
const OracleContraintTypes = {
|
const OracleContraintTypes = {
|
||||||
PRIMARY: "P",
|
PRIMARY: "P",
|
||||||
NOT_NULL_OR_CHECK: "C",
|
NOT_NULL_OR_CHECK: "C",
|
||||||
|
@ -195,7 +157,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
/**
|
/**
|
||||||
* Map the flat tabular columns and constraints data into a nested object
|
* Map the flat tabular columns and constraints data into a nested object
|
||||||
*/
|
*/
|
||||||
private mapColumns(result: Result<ColumnsResponse>): {
|
private mapColumns(result: Result<OracleColumnsResponse>): {
|
||||||
[key: string]: OracleTable
|
[key: string]: OracleTable
|
||||||
} {
|
} {
|
||||||
const oracleTables: { [key: string]: OracleTable } = {}
|
const oracleTables: { [key: string]: OracleTable } = {}
|
||||||
|
@ -299,7 +261,7 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
* @param entities - the tables that are to be built
|
* @param entities - the tables that are to be built
|
||||||
*/
|
*/
|
||||||
async buildSchema(datasourceId: string, entities: Record<string, Table>) {
|
async buildSchema(datasourceId: string, entities: Record<string, Table>) {
|
||||||
const columnsResponse = await this.internalQuery<ColumnsResponse>({
|
const columnsResponse = await this.internalQuery<OracleColumnsResponse>({
|
||||||
sql: this.COLUMNS_SQL,
|
sql: this.COLUMNS_SQL,
|
||||||
})
|
})
|
||||||
const oracleTables = this.mapColumns(columnsResponse)
|
const oracleTables = this.mapColumns(columnsResponse)
|
||||||
|
@ -334,6 +296,9 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
fieldSchema = {
|
fieldSchema = {
|
||||||
autocolumn: OracleIntegration.isAutoColumn(oracleColumn),
|
autocolumn: OracleIntegration.isAutoColumn(oracleColumn),
|
||||||
name: columnName,
|
name: columnName,
|
||||||
|
constraints: {
|
||||||
|
presence: false,
|
||||||
|
},
|
||||||
...this.internalConvertType(oracleColumn),
|
...this.internalConvertType(oracleColumn),
|
||||||
}
|
}
|
||||||
table.schema[columnName] = fieldSchema
|
table.schema[columnName] = fieldSchema
|
||||||
|
@ -343,6 +308,12 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
||||||
Object.values(oracleColumn.constraints).forEach(oracleConstraint => {
|
Object.values(oracleColumn.constraints).forEach(oracleConstraint => {
|
||||||
if (oracleConstraint.type === OracleContraintTypes.PRIMARY) {
|
if (oracleConstraint.type === OracleContraintTypes.PRIMARY) {
|
||||||
table.primary!.push(columnName)
|
table.primary!.push(columnName)
|
||||||
|
} else if (
|
||||||
|
oracleConstraint.type === OracleContraintTypes.NOT_NULL_OR_CHECK
|
||||||
|
) {
|
||||||
|
table.schema[columnName].constraints = {
|
||||||
|
presence: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,9 +15,10 @@ import {
|
||||||
SqlClient,
|
SqlClient,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
import Sql from "./base/sql"
|
import Sql from "./base/sql"
|
||||||
|
import { PostgresColumn } from "./base/types"
|
||||||
|
import { escapeDangerousCharacters } from "../utilities"
|
||||||
|
|
||||||
const { Client, types } = require("pg")
|
const { Client, types } = require("pg")
|
||||||
const { escapeDangerousCharacters } = require("../utilities")
|
|
||||||
|
|
||||||
// Return "date" and "timestamp" types as plain strings.
|
// Return "date" and "timestamp" types as plain strings.
|
||||||
// This lets us reference the original stored timezone.
|
// This lets us reference the original stored timezone.
|
||||||
|
@ -237,7 +238,8 @@ class PostgresIntegration extends Sql implements DatasourcePlus {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const columnsResponse = await this.client.query(this.COLUMNS_SQL)
|
const columnsResponse: { rows: PostgresColumn[] } =
|
||||||
|
await this.client.query(this.COLUMNS_SQL)
|
||||||
|
|
||||||
const tables: { [key: string]: Table } = {}
|
const tables: { [key: string]: Table } = {}
|
||||||
|
|
||||||
|
@ -260,6 +262,9 @@ class PostgresIntegration extends Sql implements DatasourcePlus {
|
||||||
column.identity_start ||
|
column.identity_start ||
|
||||||
column.identity_increment
|
column.identity_increment
|
||||||
)
|
)
|
||||||
|
const constraints = {
|
||||||
|
presence: column.is_nullable === "NO",
|
||||||
|
}
|
||||||
const hasDefault =
|
const hasDefault =
|
||||||
typeof column.column_default === "string" &&
|
typeof column.column_default === "string" &&
|
||||||
column.column_default.startsWith("nextval")
|
column.column_default.startsWith("nextval")
|
||||||
|
@ -269,6 +274,7 @@ class PostgresIntegration extends Sql implements DatasourcePlus {
|
||||||
tables[tableName].schema[columnName] = {
|
tables[tableName].schema[columnName] = {
|
||||||
autocolumn: isAuto,
|
autocolumn: isAuto,
|
||||||
name: columnName,
|
name: columnName,
|
||||||
|
constraints,
|
||||||
...convertSqlType(column.data_type),
|
...convertSqlType(column.data_type),
|
||||||
externalType: column.data_type,
|
externalType: column.data_type,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { db as dbCore } from "@budibase/backend-core"
|
import { db as dbCore } from "@budibase/backend-core"
|
||||||
import { getAppUrl } from "../../api/controllers/application"
|
import sdk from "../../sdk"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date:
|
* Date:
|
||||||
|
@ -20,14 +20,7 @@ export const run = async (appDb: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!metadata.url) {
|
if (!metadata.url) {
|
||||||
const context = {
|
metadata.url = sdk.applications.getAppUrl({ name: metadata.name })
|
||||||
request: {
|
|
||||||
body: {
|
|
||||||
name: metadata.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
metadata.url = getAppUrl(context)
|
|
||||||
console.log(`Adding url to app: ${metadata.url}`)
|
console.log(`Adding url to app: ${metadata.url}`)
|
||||||
await appDb.put(metadata)
|
await appDb.put(metadata)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import * as sync from "./sync"
|
||||||
|
import * as utils from "./utils"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...sync,
|
||||||
|
...utils,
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import env from "../../../environment"
|
||||||
|
import { db as dbCore, context } from "@budibase/backend-core"
|
||||||
|
import sdk from "../../"
|
||||||
|
|
||||||
|
export async function syncApp(
|
||||||
|
appId: string,
|
||||||
|
opts?: { automationOnly?: boolean }
|
||||||
|
) {
|
||||||
|
if (env.DISABLE_AUTO_PROD_APP_SYNC) {
|
||||||
|
return {
|
||||||
|
message:
|
||||||
|
"App sync disabled. You can reenable with the DISABLE_AUTO_PROD_APP_SYNC environment variable.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbCore.isProdAppID(appId)) {
|
||||||
|
throw new Error("This action cannot be performed for production apps")
|
||||||
|
}
|
||||||
|
|
||||||
|
// replicate prod to dev
|
||||||
|
const prodAppId = dbCore.getProdAppID(appId)
|
||||||
|
|
||||||
|
// specific case, want to make sure setup is skipped
|
||||||
|
const prodDb = context.getProdAppDB({ skip_setup: true })
|
||||||
|
const exists = await prodDb.exists()
|
||||||
|
if (!exists) {
|
||||||
|
// the database doesn't exist. Don't replicate
|
||||||
|
return {
|
||||||
|
message: "App sync not required, app not deployed.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const replication = new dbCore.Replication({
|
||||||
|
source: prodAppId,
|
||||||
|
target: appId,
|
||||||
|
})
|
||||||
|
let error
|
||||||
|
try {
|
||||||
|
const replOpts = replication.appReplicateOpts()
|
||||||
|
if (opts?.automationOnly) {
|
||||||
|
replOpts.filter = (doc: any) =>
|
||||||
|
doc._id.startsWith(dbCore.DocumentType.AUTOMATION)
|
||||||
|
}
|
||||||
|
await replication.replicate(replOpts)
|
||||||
|
} catch (err) {
|
||||||
|
error = err
|
||||||
|
} finally {
|
||||||
|
await replication.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync the users
|
||||||
|
await sdk.users.syncGlobalUsers()
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
message: "App sync completed successfully.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
const URL_REGEX_SLASH = /\/|\\/g
|
||||||
|
|
||||||
|
export function getAppUrl(opts?: { name?: string; url?: string }) {
|
||||||
|
// construct the url
|
||||||
|
let url
|
||||||
|
if (opts?.url) {
|
||||||
|
// if the url is provided, use that
|
||||||
|
url = encodeURI(opts?.url)
|
||||||
|
} else if (opts?.name) {
|
||||||
|
// otherwise use the name
|
||||||
|
url = encodeURI(`${opts?.name}`)
|
||||||
|
}
|
||||||
|
if (url) {
|
||||||
|
url = `/${url.replace(URL_REGEX_SLASH, "")}`.toLowerCase()
|
||||||
|
}
|
||||||
|
return url as string
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
import { default as backups } from "./app/backups"
|
import { default as backups } from "./app/backups"
|
||||||
import { default as tables } from "./app/tables"
|
import { default as tables } from "./app/tables"
|
||||||
import { default as automations } from "./app/automations"
|
import { default as automations } from "./app/automations"
|
||||||
|
import { default as applications } from "./app/applications"
|
||||||
|
import { default as users } from "./users"
|
||||||
|
|
||||||
const sdk = {
|
const sdk = {
|
||||||
backups,
|
backups,
|
||||||
tables,
|
tables,
|
||||||
automations,
|
automations,
|
||||||
|
applications,
|
||||||
|
users,
|
||||||
}
|
}
|
||||||
|
|
||||||
// default export for TS
|
// default export for TS
|
||||||
export default sdk
|
export default sdk
|
||||||
|
|
||||||
// default export for JS
|
|
||||||
module.exports = sdk
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import * as utils from "./utils"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...utils,
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { getGlobalUsers } from "../../utilities/global"
|
||||||
|
import { context, roles as rolesCore } from "@budibase/backend-core"
|
||||||
|
import {
|
||||||
|
generateUserMetadataID,
|
||||||
|
getUserMetadataParams,
|
||||||
|
InternalTables,
|
||||||
|
} from "../../db/utils"
|
||||||
|
import { isEqual } from "lodash"
|
||||||
|
|
||||||
|
export function combineMetadataAndUser(user: any, metadata: any) {
|
||||||
|
// skip users with no access
|
||||||
|
if (user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
delete user._rev
|
||||||
|
const metadataId = generateUserMetadataID(user._id)
|
||||||
|
const newDoc = {
|
||||||
|
...user,
|
||||||
|
_id: metadataId,
|
||||||
|
tableId: InternalTables.USER_METADATA,
|
||||||
|
}
|
||||||
|
const found = Array.isArray(metadata)
|
||||||
|
? metadata.find(doc => doc._id === metadataId)
|
||||||
|
: metadata
|
||||||
|
// copy rev over for the purposes of equality check
|
||||||
|
if (found) {
|
||||||
|
newDoc._rev = found._rev
|
||||||
|
}
|
||||||
|
if (found == null || !isEqual(newDoc, found)) {
|
||||||
|
return {
|
||||||
|
...found,
|
||||||
|
...newDoc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rawUserMetadata() {
|
||||||
|
const db = context.getAppDB()
|
||||||
|
return (
|
||||||
|
await db.allDocs(
|
||||||
|
getUserMetadataParams(null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(row => row.doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncGlobalUsers() {
|
||||||
|
// sync user metadata
|
||||||
|
const db = context.getAppDB()
|
||||||
|
const [users, metadata] = await Promise.all([
|
||||||
|
getGlobalUsers(),
|
||||||
|
rawUserMetadata(),
|
||||||
|
])
|
||||||
|
const toWrite = []
|
||||||
|
for (let user of users) {
|
||||||
|
const combined = await combineMetadataAndUser(user, metadata)
|
||||||
|
if (combined) {
|
||||||
|
toWrite.push(combined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await db.bulkDocs(toWrite)
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import env from "../environment"
|
||||||
import { checkSlashesInUrl } from "./index"
|
import { checkSlashesInUrl } from "./index"
|
||||||
import { db as dbCore, constants, tenancy } from "@budibase/backend-core"
|
import { db as dbCore, constants, tenancy } from "@budibase/backend-core"
|
||||||
import { updateAppRole } from "./global"
|
import { updateAppRole } from "./global"
|
||||||
import { BBContext, Automation } from "@budibase/types"
|
import { BBContext, User } from "@budibase/types"
|
||||||
|
|
||||||
export function request(ctx?: BBContext, request?: any) {
|
export function request(ctx?: BBContext, request?: any) {
|
||||||
if (!request.headers) {
|
if (!request.headers) {
|
||||||
|
@ -138,7 +138,7 @@ export async function deleteGlobalUser(ctx: BBContext) {
|
||||||
return checkResponse(response, "delete user", { ctx })
|
return checkResponse(response, "delete user", { ctx })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readGlobalUser(ctx: BBContext) {
|
export async function readGlobalUser(ctx: BBContext): Promise<User> {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
checkSlashesInUrl(
|
checkSlashesInUrl(
|
||||||
env.WORKER_URL + `/api/global/users/${ctx.params.userId}`
|
env.WORKER_URL + `/api/global/users/${ctx.params.userId}`
|
||||||
|
|
|
@ -189,9 +189,9 @@
|
||||||
integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==
|
integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==
|
||||||
|
|
||||||
"@babel/compat-data@^7.20.0":
|
"@babel/compat-data@^7.20.0":
|
||||||
version "7.20.1"
|
version "7.20.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30"
|
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733"
|
||||||
integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==
|
integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==
|
||||||
|
|
||||||
"@babel/core@7.17.4":
|
"@babel/core@7.17.4":
|
||||||
version "7.17.4"
|
version "7.17.4"
|
||||||
|
@ -236,20 +236,20 @@
|
||||||
semver "^6.3.0"
|
semver "^6.3.0"
|
||||||
|
|
||||||
"@babel/core@^7.11.6":
|
"@babel/core@^7.11.6":
|
||||||
version "7.20.2"
|
version "7.20.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92"
|
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113"
|
||||||
integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==
|
integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ampproject/remapping" "^2.1.0"
|
"@ampproject/remapping" "^2.1.0"
|
||||||
"@babel/code-frame" "^7.18.6"
|
"@babel/code-frame" "^7.18.6"
|
||||||
"@babel/generator" "^7.20.2"
|
"@babel/generator" "^7.20.5"
|
||||||
"@babel/helper-compilation-targets" "^7.20.0"
|
"@babel/helper-compilation-targets" "^7.20.0"
|
||||||
"@babel/helper-module-transforms" "^7.20.2"
|
"@babel/helper-module-transforms" "^7.20.2"
|
||||||
"@babel/helpers" "^7.20.1"
|
"@babel/helpers" "^7.20.5"
|
||||||
"@babel/parser" "^7.20.2"
|
"@babel/parser" "^7.20.5"
|
||||||
"@babel/template" "^7.18.10"
|
"@babel/template" "^7.18.10"
|
||||||
"@babel/traverse" "^7.20.1"
|
"@babel/traverse" "^7.20.5"
|
||||||
"@babel/types" "^7.20.2"
|
"@babel/types" "^7.20.5"
|
||||||
convert-source-map "^1.7.0"
|
convert-source-map "^1.7.0"
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
gensync "^1.0.0-beta.2"
|
gensync "^1.0.0-beta.2"
|
||||||
|
@ -265,12 +265,12 @@
|
||||||
"@jridgewell/gen-mapping" "^0.3.0"
|
"@jridgewell/gen-mapping" "^0.3.0"
|
||||||
jsesc "^2.5.1"
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
"@babel/generator@^7.20.1", "@babel/generator@^7.20.2":
|
"@babel/generator@^7.20.5":
|
||||||
version "7.20.4"
|
version "7.20.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8"
|
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95"
|
||||||
integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==
|
integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.20.2"
|
"@babel/types" "^7.20.5"
|
||||||
"@jridgewell/gen-mapping" "^0.3.2"
|
"@jridgewell/gen-mapping" "^0.3.2"
|
||||||
jsesc "^2.5.1"
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
|
@ -551,14 +551,14 @@
|
||||||
"@babel/traverse" "^7.18.2"
|
"@babel/traverse" "^7.18.2"
|
||||||
"@babel/types" "^7.18.2"
|
"@babel/types" "^7.18.2"
|
||||||
|
|
||||||
"@babel/helpers@^7.20.1":
|
"@babel/helpers@^7.20.5":
|
||||||
version "7.20.1"
|
version "7.20.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9"
|
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763"
|
||||||
integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==
|
integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/template" "^7.18.10"
|
"@babel/template" "^7.18.10"
|
||||||
"@babel/traverse" "^7.20.1"
|
"@babel/traverse" "^7.20.5"
|
||||||
"@babel/types" "^7.20.0"
|
"@babel/types" "^7.20.5"
|
||||||
|
|
||||||
"@babel/highlight@^7.16.7":
|
"@babel/highlight@^7.16.7":
|
||||||
version "7.17.12"
|
version "7.17.12"
|
||||||
|
@ -583,10 +583,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef"
|
||||||
integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==
|
integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==
|
||||||
|
|
||||||
"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2":
|
"@babel/parser@^7.18.10", "@babel/parser@^7.20.5":
|
||||||
version "7.20.3"
|
version "7.20.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8"
|
||||||
integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==
|
integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==
|
||||||
|
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7":
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7":
|
||||||
version "7.17.12"
|
version "7.17.12"
|
||||||
|
@ -1235,19 +1235,19 @@
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
|
|
||||||
"@babel/traverse@^7.20.1":
|
"@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5":
|
||||||
version "7.20.1"
|
version "7.20.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8"
|
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133"
|
||||||
integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==
|
integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.18.6"
|
"@babel/code-frame" "^7.18.6"
|
||||||
"@babel/generator" "^7.20.1"
|
"@babel/generator" "^7.20.5"
|
||||||
"@babel/helper-environment-visitor" "^7.18.9"
|
"@babel/helper-environment-visitor" "^7.18.9"
|
||||||
"@babel/helper-function-name" "^7.19.0"
|
"@babel/helper-function-name" "^7.19.0"
|
||||||
"@babel/helper-hoist-variables" "^7.18.6"
|
"@babel/helper-hoist-variables" "^7.18.6"
|
||||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
"@babel/helper-split-export-declaration" "^7.18.6"
|
||||||
"@babel/parser" "^7.20.1"
|
"@babel/parser" "^7.20.5"
|
||||||
"@babel/types" "^7.20.0"
|
"@babel/types" "^7.20.5"
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
|
|
||||||
|
@ -1259,10 +1259,10 @@
|
||||||
"@babel/helper-validator-identifier" "^7.16.7"
|
"@babel/helper-validator-identifier" "^7.16.7"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2":
|
"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5":
|
||||||
version "7.20.2"
|
version "7.20.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84"
|
||||||
integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==
|
integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-string-parser" "^7.19.4"
|
"@babel/helper-string-parser" "^7.19.4"
|
||||||
"@babel/helper-validator-identifier" "^7.19.1"
|
"@babel/helper-validator-identifier" "^7.19.1"
|
||||||
|
@ -1273,12 +1273,12 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/backend-core@2.1.40-alpha.7":
|
"@budibase/backend-core@2.1.46-alpha.0":
|
||||||
version "2.1.40-alpha.7"
|
version "2.1.46-alpha.0"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.40-alpha.7.tgz#383ce7b679395bcbd5a2dbe52c73915e2a8e456f"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.1.46-alpha.0.tgz#d9063b055cb3c4edb3994a13664c6a070e701a71"
|
||||||
integrity sha512-JWKm47ypnBiLLbdZxPc/Ca08vT14kh2yk3o9f30zH8Uq7O5STu/bbffBVuS/wxgLD8CSKQS6Hbocz3/nADWbIQ==
|
integrity sha512-ulpK2u+5+yIjIyMWhr2cre2ncdZsq5uPt1meNO+kvsetmv37ZnLtmM2gqIe1RNjYkf+mIZlUDA9QDkZowIiTUg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/types" "2.1.40-alpha.7"
|
"@budibase/types" "2.1.46-alpha.0"
|
||||||
"@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"
|
||||||
|
@ -1360,13 +1360,13 @@
|
||||||
svelte-flatpickr "^3.2.3"
|
svelte-flatpickr "^3.2.3"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/pro@2.1.40-alpha.7":
|
"@budibase/pro@2.1.46-alpha.0":
|
||||||
version "2.1.40-alpha.7"
|
version "2.1.46-alpha.0"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.40-alpha.7.tgz#697fc833de5efd84c94ed316d010683112394dd1"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.1.46-alpha.0.tgz#69bf09007edc0f2be275824ba09f27422081c490"
|
||||||
integrity sha512-S7UJNZMdN3PebVmGI7MNl41C9jkgJ1dt9F3tIWQsogRA2bJKjaGJzHWWcQ2qFPNNFgMBdYvVk77UQerLS2HfpA==
|
integrity sha512-04iCZVU6BN0afei0wFITgvjRhQ46G/vI51FCBZ9ZiuyTsWBPuAhNqXVAQ/Y48+OVDBMtIvsvJ/S1kCbWUa6x3g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/backend-core" "2.1.40-alpha.7"
|
"@budibase/backend-core" "2.1.46-alpha.0"
|
||||||
"@budibase/types" "2.1.40-alpha.7"
|
"@budibase/types" "2.1.46-alpha.0"
|
||||||
"@koa/router" "8.0.8"
|
"@koa/router" "8.0.8"
|
||||||
bull "4.10.1"
|
bull "4.10.1"
|
||||||
joi "17.6.0"
|
joi "17.6.0"
|
||||||
|
@ -1390,10 +1390,10 @@
|
||||||
svelte-apexcharts "^1.0.2"
|
svelte-apexcharts "^1.0.2"
|
||||||
svelte-flatpickr "^3.1.0"
|
svelte-flatpickr "^3.1.0"
|
||||||
|
|
||||||
"@budibase/types@2.1.40-alpha.7":
|
"@budibase/types@2.1.46-alpha.0":
|
||||||
version "2.1.40-alpha.7"
|
version "2.1.46-alpha.0"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.40-alpha.7.tgz#96b01c934d43dd0467ed3ab2b19676a65e97dd68"
|
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.1.46-alpha.0.tgz#83ecd2c1a26e933fbf27ba6b7a7ef1fed82a8840"
|
||||||
integrity sha512-3t4MOHlgHywGiz9qZEd7Zs0PtWgSSVpwNvmEEOe/vSL2xPptFJuT5M90KKgdlkND6vUTJ8wiLA9+OToegk0ndw==
|
integrity sha512-TTpyax8U0EAsaXqCLACHcfuejZ2x2isyHiM/ia8Ua6R+1rDK9kjYPsiTlJ169/Jq2hv65TzScyaqnHX+g+eJnQ==
|
||||||
|
|
||||||
"@bull-board/api@3.7.0":
|
"@bull-board/api@3.7.0":
|
||||||
version "3.7.0"
|
version "3.7.0"
|
||||||
|
@ -3167,10 +3167,10 @@
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
form-data "^3.0.0"
|
form-data "^3.0.0"
|
||||||
|
|
||||||
"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12.12.47", "@types/node@>=13.13.4", "@types/node@>=13.7.0":
|
"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.13.4", "@types/node@>=13.7.0":
|
||||||
version "18.11.9"
|
version "17.0.41"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.41.tgz#1607b2fd3da014ae5d4d1b31bc792a39348dfb9b"
|
||||||
integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==
|
integrity sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==
|
||||||
|
|
||||||
"@types/node@14.18.20":
|
"@types/node@14.18.20":
|
||||||
version "14.18.20"
|
version "14.18.20"
|
||||||
|
@ -3182,6 +3182,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
|
||||||
integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==
|
integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==
|
||||||
|
|
||||||
|
"@types/node@>=10.0.0":
|
||||||
|
version "18.7.18"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.18.tgz#633184f55c322e4fb08612307c274ee6d5ed3154"
|
||||||
|
integrity sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==
|
||||||
|
|
||||||
"@types/node@>=8.0.0 <15":
|
"@types/node@>=8.0.0 <15":
|
||||||
version "14.18.21"
|
version "14.18.21"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41"
|
||||||
|
@ -3464,9 +3469,9 @@
|
||||||
"@types/yargs-parser" "*"
|
"@types/yargs-parser" "*"
|
||||||
|
|
||||||
"@types/yargs@^17.0.8":
|
"@types/yargs@^17.0.8":
|
||||||
version "17.0.13"
|
version "17.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76"
|
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.15.tgz#5b62c89fb049e2fc8378394a2861a593055f0866"
|
||||||
integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==
|
integrity sha512-ZHc4W2dnEQPfhn06TBEdWaiUHEZAocYaiVMfwOipY5jcJt/251wVrKCBWBetGZWO5CF8tdb7L3DmdxVlZ2BOIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/yargs-parser" "*"
|
"@types/yargs-parser" "*"
|
||||||
|
|
||||||
|
@ -4919,9 +4924,9 @@ caniuse-lite@^1.0.30001349:
|
||||||
integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==
|
integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==
|
||||||
|
|
||||||
caniuse-lite@^1.0.30001400:
|
caniuse-lite@^1.0.30001400:
|
||||||
version "1.0.30001431"
|
version "1.0.30001435"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz#e7c59bd1bc518fae03a4656be442ce6c4887a795"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001435.tgz#502c93dbd2f493bee73a408fe98e98fb1dad10b2"
|
||||||
integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==
|
integrity sha512-kdCkUTjR+v4YAJelyiDTqiu82BDr4W4CP5sgTA0ZBmqn30XfS2ZghPLMowik9TPhS+psWJiUNxsqLyurDbmutA==
|
||||||
|
|
||||||
capture-exit@^2.0.0:
|
capture-exit@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
|
@ -5568,9 +5573,9 @@ decamelize@^1.2.0:
|
||||||
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
|
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
|
||||||
|
|
||||||
decode-uri-component@^0.2.0:
|
decode-uri-component@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
|
||||||
integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==
|
integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
|
||||||
|
|
||||||
decompress-response@^3.3.0:
|
decompress-response@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
|
@ -6073,9 +6078,9 @@ engine.io-parser@~5.0.3:
|
||||||
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
|
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
|
||||||
|
|
||||||
engine.io@~6.2.0:
|
engine.io@~6.2.0:
|
||||||
version "6.2.1"
|
version "6.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f"
|
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0"
|
||||||
integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==
|
integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/cookie" "^0.4.1"
|
"@types/cookie" "^0.4.1"
|
||||||
"@types/cors" "^2.8.12"
|
"@types/cors" "^2.8.12"
|
||||||
|
@ -10049,9 +10054,9 @@ loader-runner@^4.2.0:
|
||||||
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
|
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
|
||||||
|
|
||||||
loader-utils@^2.0.0:
|
loader-utils@^2.0.0:
|
||||||
version "2.0.4"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
|
||||||
integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
|
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
|
||||||
dependencies:
|
dependencies:
|
||||||
big.js "^5.2.2"
|
big.js "^5.2.2"
|
||||||
emojis-list "^3.0.0"
|
emojis-list "^3.0.0"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue