Merge branch 'develop' of github.com:Budibase/budibase into feature/budibase-api
This commit is contained in:
commit
f0b02cf9a9
62 changed files with 2369 additions and 792 deletions
3
.github/workflows/release-selfhost.yml
vendored
3
.github/workflows/release-selfhost.yml
vendored
|
@ -25,14 +25,17 @@ jobs:
|
||||||
# Pull apps and worker images
|
# Pull apps and worker images
|
||||||
docker pull budibase/apps:$release_tag
|
docker pull budibase/apps:$release_tag
|
||||||
docker pull budibase/worker:$release_tag
|
docker pull budibase/worker:$release_tag
|
||||||
|
docker pull budibase/proxy:$release_tag
|
||||||
|
|
||||||
# Tag apps and worker images
|
# Tag apps and worker images
|
||||||
docker tag budibase/apps:$release_tag budibase/apps:$SELFHOST_TAG
|
docker tag budibase/apps:$release_tag budibase/apps:$SELFHOST_TAG
|
||||||
docker tag budibase/worker:$release_tag budibase/worker:$SELFHOST_TAG
|
docker tag budibase/worker:$release_tag budibase/worker:$SELFHOST_TAG
|
||||||
|
docker tag budibase/proxy:$release_tag budibase/proxy:$SELFHOST_TAG
|
||||||
|
|
||||||
# Push images
|
# Push images
|
||||||
docker push budibase/apps:$SELFHOST_TAG
|
docker push budibase/apps:$SELFHOST_TAG
|
||||||
docker push budibase/worker:$SELFHOST_TAG
|
docker push budibase/worker:$SELFHOST_TAG
|
||||||
|
docker push budibase/proxy:$SELFHOST_TAG
|
||||||
env:
|
env:
|
||||||
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
|
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
|
||||||
|
|
152
hosting/envoy.yaml
Normal file
152
hosting/envoy.yaml
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
static_resources:
|
||||||
|
listeners:
|
||||||
|
- name: main_listener
|
||||||
|
address:
|
||||||
|
socket_address: { address: 0.0.0.0, port_value: 10000 }
|
||||||
|
filter_chains:
|
||||||
|
- filters:
|
||||||
|
- name: envoy.filters.network.http_connection_manager
|
||||||
|
typed_config:
|
||||||
|
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
|
||||||
|
stat_prefix: ingress
|
||||||
|
codec_type: auto
|
||||||
|
route_config:
|
||||||
|
name: local_route
|
||||||
|
virtual_hosts:
|
||||||
|
- name: local_services
|
||||||
|
domains: ["*"]
|
||||||
|
routes:
|
||||||
|
- match: { prefix: "/app/" }
|
||||||
|
route:
|
||||||
|
cluster: app-service
|
||||||
|
prefix_rewrite: "/"
|
||||||
|
|
||||||
|
- match: { path: "/v1/update" }
|
||||||
|
route:
|
||||||
|
cluster: watchtower-service
|
||||||
|
|
||||||
|
- match: { prefix: "/builder/" }
|
||||||
|
route:
|
||||||
|
cluster: app-service
|
||||||
|
|
||||||
|
- match: { prefix: "/builder" }
|
||||||
|
route:
|
||||||
|
cluster: app-service
|
||||||
|
|
||||||
|
- match: { prefix: "/app_" }
|
||||||
|
route:
|
||||||
|
cluster: app-service
|
||||||
|
|
||||||
|
# special cases for worker admin (deprecated), global and system API
|
||||||
|
- match: { prefix: "/api/global/" }
|
||||||
|
route:
|
||||||
|
cluster: worker-service
|
||||||
|
|
||||||
|
- match: { prefix: "/api/admin/" }
|
||||||
|
route:
|
||||||
|
cluster: worker-service
|
||||||
|
|
||||||
|
- match: { prefix: "/api/system/" }
|
||||||
|
route:
|
||||||
|
cluster: worker-service
|
||||||
|
|
||||||
|
- match: { path: "/" }
|
||||||
|
route:
|
||||||
|
cluster: app-service
|
||||||
|
|
||||||
|
# special case for when API requests are made, can just forward, not to minio
|
||||||
|
- match: { prefix: "/api/" }
|
||||||
|
route:
|
||||||
|
cluster: app-service
|
||||||
|
timeout: 120s
|
||||||
|
|
||||||
|
- match: { prefix: "/worker/" }
|
||||||
|
route:
|
||||||
|
cluster: worker-service
|
||||||
|
prefix_rewrite: "/"
|
||||||
|
|
||||||
|
- match: { prefix: "/db/" }
|
||||||
|
route:
|
||||||
|
cluster: couchdb-service
|
||||||
|
prefix_rewrite: "/"
|
||||||
|
|
||||||
|
# minio is on the default route because this works
|
||||||
|
# best, minio + AWS SDK doesn't handle path proxy
|
||||||
|
- match: { prefix: "/" }
|
||||||
|
route:
|
||||||
|
cluster: minio-service
|
||||||
|
|
||||||
|
http_filters:
|
||||||
|
- name: envoy.filters.http.router
|
||||||
|
|
||||||
|
clusters:
|
||||||
|
- name: app-service
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: strict_dns
|
||||||
|
lb_policy: round_robin
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: app-service
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: app-service
|
||||||
|
port_value: 4002
|
||||||
|
|
||||||
|
- name: minio-service
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: strict_dns
|
||||||
|
lb_policy: round_robin
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: minio-service
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: minio-service
|
||||||
|
port_value: 9000
|
||||||
|
|
||||||
|
- name: worker-service
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: strict_dns
|
||||||
|
lb_policy: round_robin
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: worker-service
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: worker-service
|
||||||
|
port_value: 4003
|
||||||
|
|
||||||
|
- name: couchdb-service
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: strict_dns
|
||||||
|
lb_policy: round_robin
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: couchdb-service
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: couchdb-service
|
||||||
|
port_value: 5984
|
||||||
|
|
||||||
|
- name: watchtower-service
|
||||||
|
connect_timeout: 0.25s
|
||||||
|
type: strict_dns
|
||||||
|
lb_policy: round_robin
|
||||||
|
load_assignment:
|
||||||
|
cluster_name: watchtower-service
|
||||||
|
endpoints:
|
||||||
|
- lb_endpoints:
|
||||||
|
- endpoint:
|
||||||
|
address:
|
||||||
|
socket_address:
|
||||||
|
address: watchtower-service
|
||||||
|
port_value: 8080
|
||||||
|
|
|
@ -22,7 +22,7 @@ http {
|
||||||
# buffering
|
# buffering
|
||||||
client_body_buffer_size 1K;
|
client_body_buffer_size 1K;
|
||||||
client_header_buffer_size 1k;
|
client_header_buffer_size 1k;
|
||||||
client_max_body_size 1k;
|
client_max_body_size 10M;
|
||||||
ignore_invalid_headers off;
|
ignore_invalid_headers off;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
|
|
||||||
|
@ -36,20 +36,28 @@ http {
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 10000 default_server;
|
listen 10000 default_server;
|
||||||
|
listen [::]:10000 default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
port_in_redirect off;
|
||||||
|
|
||||||
# Security Headers
|
# Security Headers
|
||||||
add_header X-Frame-Options SAMEORIGIN always;
|
add_header X-Frame-Options SAMEORIGIN always;
|
||||||
add_header X-Content-Type-Options nosniff always;
|
add_header X-Content-Type-Options nosniff always;
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me; object-src 'none'; base-uri 'self'; connect-src 'self' https://api-iam.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io; font-src 'self' https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me; frame-src 'self'; img-src https: data:; manifest-src 'self'; media-src 'self'; worker-src 'none';" always;
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me; object-src 'none'; base-uri 'self'; connect-src 'self' https://api-iam.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io; font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me; frame-src 'self'; img-src http: https: data:; manifest-src 'self'; media-src 'self'; worker-src 'none';" always;
|
||||||
|
|
||||||
location /app {
|
location /app {
|
||||||
proxy_pass http://app-service:4002;
|
proxy_pass http://app-service.budibase.svc.cluster.local:4002;
|
||||||
rewrite ^/app/(.*)$ /$1 break;
|
rewrite ^/app/(.*)$ /$1 break;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = / {
|
location = / {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_pass http://app-service.budibase.svc.cluster.local:4002;
|
proxy_pass http://app-service.budibase.svc.cluster.local:4002;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,16 +71,22 @@ http {
|
||||||
proxy_pass http://app-service.budibase.svc.cluster.local:4002;
|
proxy_pass http://app-service.budibase.svc.cluster.local:4002;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ^/(builder|app_) {
|
location ~ ^/(builder|app_) {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_pass http://app-service.budibase.svc.cluster.local:4002;
|
proxy_pass http://app-service.budibase.svc.cluster.local:4002;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ ^/api/(system|admin|global)/ {
|
location ~ ^/api/(system|admin|global)/ {
|
||||||
proxy_pass http://worker-service.budibase.svc.cluster.local:4003;
|
proxy_pass http://worker-service.budibase.svc.cluster.local:4001;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /worker/ {
|
location /worker/ {
|
||||||
proxy_pass http://worker-service.budibase.svc.cluster.local:4003;
|
proxy_pass http://worker-service.budibase.svc.cluster.local:4001;
|
||||||
rewrite ^/worker/(.*)$ /$1 break;
|
rewrite ^/worker/(.*)$ /$1 break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,11 +119,11 @@ http {
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
chunked_transfer_encoding off;
|
||||||
|
|
||||||
proxy_connect_timeout 300;
|
proxy_connect_timeout 300;
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
chunked_transfer_encoding off;
|
|
||||||
proxy_pass http://minio-service.budibase.svc.cluster.local:9000;
|
proxy_pass http://minio-service.budibase.svc.cluster.local:9000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ http {
|
||||||
client_header_buffer_size 1k;
|
client_header_buffer_size 1k;
|
||||||
client_max_body_size 1k;
|
client_max_body_size 1k;
|
||||||
ignore_invalid_headers off;
|
ignore_invalid_headers off;
|
||||||
proxy_buffering off;
|
|
||||||
|
|
||||||
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
@ -37,16 +36,18 @@ http {
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 10000 default_server;
|
listen 10000 default_server;
|
||||||
|
listen [::]:10000 default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
client_max_body_size 1000m;
|
client_max_body_size 1000m;
|
||||||
ignore_invalid_headers off;
|
ignore_invalid_headers off;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
|
port_in_redirect off;
|
||||||
|
|
||||||
# Security Headers
|
# Security Headers
|
||||||
add_header X-Frame-Options SAMEORIGIN always;
|
add_header X-Frame-Options SAMEORIGIN always;
|
||||||
add_header X-Content-Type-Options nosniff always;
|
add_header X-Content-Type-Options nosniff always;
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
add_header Content-Security-Policy "default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' 'self' https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me; object-src 'none'; base-uri 'self'; connect-src 'self' https://api-iam.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io; font-src 'self' https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me; frame-src 'self'; img-src https: data:; manifest-src 'self'; media-src 'self'; worker-src 'none';" always;
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me; object-src 'none'; base-uri 'self'; connect-src 'self' https://api-iam.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io; font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me; frame-src 'self'; img-src http: https: data:; manifest-src 'self'; media-src 'self'; worker-src 'none';" always;
|
||||||
|
|
||||||
location /app {
|
location /app {
|
||||||
proxy_pass http://app-service:4002;
|
proxy_pass http://app-service:4002;
|
||||||
|
@ -54,6 +55,7 @@ http {
|
||||||
}
|
}
|
||||||
|
|
||||||
location = / {
|
location = / {
|
||||||
|
port_in_redirect off;
|
||||||
proxy_pass http://app-service:4002;
|
proxy_pass http://app-service:4002;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +64,7 @@ http {
|
||||||
}
|
}
|
||||||
|
|
||||||
location /builder/ {
|
location /builder/ {
|
||||||
|
port_in_redirect off;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Connection $connection_upgrade;
|
proxy_set_header Connection $connection_upgrade;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
@ -71,7 +74,14 @@ http {
|
||||||
proxy_pass http://app-service:4002;
|
proxy_pass http://app-service:4002;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ^/(builder|app_) {
|
location ~ ^/(builder|app_) {
|
||||||
|
port_in_redirect off;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_pass http://app-service:4002;
|
proxy_pass http://app-service:4002;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
|
|
|
@ -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": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"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",
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
let showTooltip = false
|
let showTooltip = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class:container={!!tooltip}>
|
|
||||||
<button
|
<button
|
||||||
class:spectrum-Button--cta={cta}
|
class:spectrum-Button--cta={cta}
|
||||||
class:spectrum-Button--primary={primary}
|
class:spectrum-Button--primary={primary}
|
||||||
|
@ -57,21 +56,16 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
|
||||||
{#if showTooltip && tooltip}
|
{#if showTooltip && tooltip}
|
||||||
<div class="position">
|
|
||||||
<div class="tooltip">
|
<div class="tooltip">
|
||||||
<Tooltip textWrapping={true} direction={"bottom"} text={tooltip} />
|
<Tooltip textWrapping={true} direction={"bottom"} text={tooltip} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.container {
|
button {
|
||||||
display: flex;
|
position: relative;
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
.spectrum-Button-label {
|
.spectrum-Button-label {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -89,12 +83,8 @@
|
||||||
width: 160px;
|
width: 160px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
top: -5px;
|
left: 50%;
|
||||||
}
|
top: calc(100% - 3px);
|
||||||
.position {
|
|
||||||
position: relative;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
}
|
||||||
.tooltip-icon {
|
.tooltip-icon {
|
||||||
padding-left: var(--spacing-m);
|
padding-left: var(--spacing-m);
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
<script>
|
<script>
|
||||||
import "@spectrum-css/buttongroup/dist/index-vars.css"
|
import "@spectrum-css/buttongroup/dist/index-vars.css"
|
||||||
export let vertical = false
|
export let vertical = false
|
||||||
|
export let gap = ""
|
||||||
|
|
||||||
|
$: gapStyle =
|
||||||
|
gap === "L"
|
||||||
|
? "var(--spacing-l)"
|
||||||
|
: gap === "M"
|
||||||
|
? "var(--spacing-m)"
|
||||||
|
: gap === "S"
|
||||||
|
? "var(--spacing-s)"
|
||||||
|
: null
|
||||||
|
|
||||||
function group(element) {
|
function group(element) {
|
||||||
const buttons = Array.from(element.getElementsByTagName("button"))
|
const buttons = Array.from(element.getElementsByTagName("button"))
|
||||||
|
@ -14,6 +24,7 @@
|
||||||
use:group
|
use:group
|
||||||
class="spectrum-ButtonGroup"
|
class="spectrum-ButtonGroup"
|
||||||
class:spectrum-ButtonGroup--vertical={vertical}
|
class:spectrum-ButtonGroup--vertical={vertical}
|
||||||
|
style={gapStyle ? `gap: ${gapStyle};` : null}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -44,6 +44,11 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
.drawer {
|
.drawer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -74,4 +79,12 @@
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: var(--spacing-xs);
|
gap: var(--spacing-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
import "@spectrum-css/table/dist/index-vars.css"
|
import "@spectrum-css/table/dist/index-vars.css"
|
||||||
import CellRenderer from "./CellRenderer.svelte"
|
import CellRenderer from "./CellRenderer.svelte"
|
||||||
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
||||||
import { cloneDeep } from "lodash"
|
import { cloneDeep, deepGet } from "../helpers"
|
||||||
import { deepGet } from "../helpers"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The expected schema is our normal couch schemas for our tables.
|
* The expected schema is our normal couch schemas for our tables.
|
||||||
|
|
|
@ -98,3 +98,11 @@ export const deepSet = (obj, key, value) => {
|
||||||
}
|
}
|
||||||
obj[split[split.length - 1]] = value
|
obj[split[split.length - 1]] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deeply clones an object. Functions are not supported.
|
||||||
|
* @param obj the object to clone
|
||||||
|
*/
|
||||||
|
export const cloneDeep = obj => {
|
||||||
|
return JSON.parse(JSON.stringify(obj))
|
||||||
|
}
|
||||||
|
|
|
@ -1601,9 +1601,9 @@ ms@^2.1.1:
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
|
||||||
nanoid@^3.1.22:
|
nanoid@^3.1.22:
|
||||||
version "3.1.22"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.0.tgz#5906f776fd886c66c24f3653e0c46fcb1d4ad6b0"
|
||||||
integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==
|
integrity sha512-JzxqqT5u/x+/KOFSd7JP15DOo9nOoHpx6DYatqIHUW2+flybkm+mdcraotSQR5WcnZr+qhGVh8Ted0KdfSMxlg==
|
||||||
|
|
||||||
negotiator@0.6.2:
|
negotiator@0.6.2:
|
||||||
version "0.6.2"
|
version "0.6.2"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -64,10 +64,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.58-alpha.4",
|
"@budibase/bbui": "^1.0.66-alpha.0",
|
||||||
"@budibase/client": "^1.0.58-alpha.4",
|
"@budibase/client": "^1.0.66-alpha.0",
|
||||||
"@budibase/frontend-core": "^1.0.58-alpha.4",
|
"@budibase/frontend-core": "^1.0.66-alpha.0",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.4",
|
"@budibase/string-templates": "^1.0.66-alpha.0",
|
||||||
"@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",
|
||||||
|
|
|
@ -275,10 +275,7 @@ const getProviderContextBindings = (asset, dataProviders) => {
|
||||||
*/
|
*/
|
||||||
const getUserBindings = () => {
|
const getUserBindings = () => {
|
||||||
let bindings = []
|
let bindings = []
|
||||||
const { schema } = getSchemaForDatasource(null, {
|
const { schema } = getSchemaForTable(TableNames.USERS)
|
||||||
type: "table",
|
|
||||||
tableId: TableNames.USERS,
|
|
||||||
})
|
|
||||||
const keys = Object.keys(schema).sort()
|
const keys = Object.keys(schema).sort()
|
||||||
const safeUser = makePropSafe("user")
|
const safeUser = makePropSafe("user")
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
|
@ -385,9 +382,33 @@ export const getButtonContextBindings = (actions, actionId) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a schema for a datasource object.
|
* Gets the schema for a certain table ID.
|
||||||
|
* The options which can be passed in are:
|
||||||
|
* formSchema: whether the schema is for a form
|
||||||
|
* searchableSchema: whether to generate a searchable schema, which may have
|
||||||
|
* fewer fields than a readable schema
|
||||||
|
* @param tableId the table ID to get the schema for
|
||||||
|
* @param options options for generating the schema
|
||||||
|
* @return {{schema: Object, table: Object}}
|
||||||
*/
|
*/
|
||||||
export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
export const getSchemaForTable = (tableId, options) => {
|
||||||
|
return getSchemaForDatasource(null, { type: "table", tableId }, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a schema for a datasource object.
|
||||||
|
* The options which can be passed in are:
|
||||||
|
* formSchema: whether the schema is for a form
|
||||||
|
* searchableSchema: whether to generate a searchable schema, which may have
|
||||||
|
* fewer fields than a readable schema
|
||||||
|
* @param asset the current root client app asset (layout or screen). This is
|
||||||
|
* optional and only needed for "provider" datasource types.
|
||||||
|
* @param datasource the datasource definition
|
||||||
|
* @param options options for generating the schema
|
||||||
|
* @return {{schema: Object, table: Object}}
|
||||||
|
*/
|
||||||
|
export const getSchemaForDatasource = (asset, datasource, options) => {
|
||||||
|
options = options || {}
|
||||||
let schema, table
|
let schema, table
|
||||||
|
|
||||||
if (datasource) {
|
if (datasource) {
|
||||||
|
@ -399,7 +420,7 @@ export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
||||||
if (type === "provider") {
|
if (type === "provider") {
|
||||||
const component = findComponent(asset.props, datasource.providerId)
|
const component = findComponent(asset.props, datasource.providerId)
|
||||||
const source = getDatasourceForProvider(asset, component)
|
const source = getDatasourceForProvider(asset, component)
|
||||||
return getSchemaForDatasource(asset, source, isForm)
|
return getSchemaForDatasource(asset, source, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// "query" datasources are those targeting non-plus datasources or
|
// "query" datasources are those targeting non-plus datasources or
|
||||||
|
@ -448,8 +469,16 @@ export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
||||||
// Determine the schema from the backing entity if not already determined
|
// Determine the schema from the backing entity if not already determined
|
||||||
if (table && !schema) {
|
if (table && !schema) {
|
||||||
if (type === "view") {
|
if (type === "view") {
|
||||||
|
// For views, the schema is pulled from the `views` property of the
|
||||||
|
// table
|
||||||
schema = cloneDeep(table.views?.[datasource.name]?.schema)
|
schema = cloneDeep(table.views?.[datasource.name]?.schema)
|
||||||
} else if (type === "query" && isForm) {
|
} else if (
|
||||||
|
type === "query" &&
|
||||||
|
(options.formSchema || options.searchableSchema)
|
||||||
|
) {
|
||||||
|
// For queries, if we are generating a schema for a form or a searchable
|
||||||
|
// schema then we want to use the query parameters rather than the
|
||||||
|
// query schema
|
||||||
schema = {}
|
schema = {}
|
||||||
const params = table.parameters || []
|
const params = table.parameters || []
|
||||||
params.forEach(param => {
|
params.forEach(param => {
|
||||||
|
@ -458,6 +487,7 @@ export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
// Otherwise we just want the schema of the table
|
||||||
schema = cloneDeep(table.schema)
|
schema = cloneDeep(table.schema)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,9 +515,31 @@ export const getSchemaForDatasource = (asset, datasource, isForm = false) => {
|
||||||
schema = { ...schema, ...jsonAdditions }
|
schema = { ...schema, ...jsonAdditions }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add _id and _rev fields for certain types
|
// Determine if we should add ID and rev to the schema
|
||||||
if (schema && !isForm && ["table", "link"].includes(datasource.type)) {
|
const isInternal = table && !table.sql
|
||||||
|
const isTable = ["table", "link"].includes(datasource.type)
|
||||||
|
|
||||||
|
// ID is part of the readable schema for all tables
|
||||||
|
// Rev is part of the readable schema for internal tables only
|
||||||
|
let addId = isTable
|
||||||
|
let addRev = isTable && isInternal
|
||||||
|
|
||||||
|
// Don't add ID or rev for form schemas
|
||||||
|
if (options.formSchema) {
|
||||||
|
addId = false
|
||||||
|
addRev = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID is only searchable for internal tables
|
||||||
|
else if (options.searchableSchema) {
|
||||||
|
addId = isTable && isInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add schema properties if required
|
||||||
|
if (addId) {
|
||||||
schema["_id"] = { type: "string" }
|
schema["_id"] = { type: "string" }
|
||||||
|
}
|
||||||
|
if (addRev) {
|
||||||
schema["_rev"] = { type: "string" }
|
schema["_rev"] = { type: "string" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,9 @@ const fieldTypeToComponentMap = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeDatasourceFormComponents(datasource) {
|
export function makeDatasourceFormComponents(datasource) {
|
||||||
const { schema } = getSchemaForDatasource(null, datasource, true)
|
const { schema } = getSchemaForDatasource(null, datasource, {
|
||||||
|
formSchema: true,
|
||||||
|
})
|
||||||
let components = []
|
let components = []
|
||||||
let fields = Object.keys(schema || {})
|
let fields = Object.keys(schema || {})
|
||||||
fields.forEach(field => {
|
fields.forEach(field => {
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
import { debounce } from "lodash"
|
import { debounce } from "lodash"
|
||||||
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
||||||
import FilterDrawer from "components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte"
|
import FilterDrawer from "components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte"
|
||||||
// need the client lucene builder to convert to the structure API expects
|
|
||||||
import { LuceneUtils } from "@budibase/frontend-core"
|
import { LuceneUtils } from "@budibase/frontend-core"
|
||||||
|
import { getSchemaForTable } from "builderStore/dataBinding"
|
||||||
|
|
||||||
export let block
|
export let block
|
||||||
export let testData
|
export let testData
|
||||||
|
@ -46,13 +46,13 @@
|
||||||
block || $automationStore.selectedBlock,
|
block || $automationStore.selectedBlock,
|
||||||
$automationStore.selectedAutomation?.automation?.definition
|
$automationStore.selectedAutomation?.automation?.definition
|
||||||
)
|
)
|
||||||
|
|
||||||
$: inputData = testData ? testData : block.inputs
|
$: inputData = testData ? testData : block.inputs
|
||||||
$: tableId = inputData ? inputData.tableId : null
|
$: tableId = inputData ? inputData.tableId : null
|
||||||
$: table = tableId
|
$: table = tableId
|
||||||
? $tables.list.find(table => table._id === inputData.tableId)
|
? $tables.list.find(table => table._id === inputData.tableId)
|
||||||
: { schema: {} }
|
: { schema: {} }
|
||||||
$: schemaFields = table ? Object.values(table.schema) : []
|
$: schema = getSchemaForTable(tableId, { searchableSchema: true }).schema
|
||||||
|
$: schemaFields = Object.values(schema || {})
|
||||||
|
|
||||||
const onChange = debounce(async function (e, key) {
|
const onChange = debounce(async function (e, key) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -48,6 +48,3 @@
|
||||||
<TableDataImport bind:dataImport bind:existingTableId={tableId} />
|
<TableDataImport bind:dataImport bind:existingTableId={tableId} />
|
||||||
</Layout>
|
</Layout>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -213,6 +213,3 @@
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -130,6 +130,3 @@
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Layout>
|
</Layout>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let bindings
|
export let bindings
|
||||||
|
// jsValue/hbsValue are the state of the value that is being built
|
||||||
|
// within this binding panel - the value should not be updated until
|
||||||
|
// the binding panel is saved. This is the default value of the
|
||||||
|
// expression when the binding panel is opened, but shouldn't be updated.
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let valid
|
export let valid
|
||||||
export let allowJS = false
|
export let allowJS = false
|
||||||
|
@ -51,8 +55,8 @@
|
||||||
})
|
})
|
||||||
$: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`)
|
$: codeMirrorHints = bindings?.map(x => `$("${x.readableBinding}")`)
|
||||||
|
|
||||||
const updateValue = value => {
|
const updateValue = val => {
|
||||||
valid = isValid(readableToRuntimeBinding(bindings, value))
|
valid = isValid(readableToRuntimeBinding(bindings, val))
|
||||||
if (valid) {
|
if (valid) {
|
||||||
dispatch("change", value)
|
dispatch("change", value)
|
||||||
}
|
}
|
||||||
|
@ -60,7 +64,7 @@
|
||||||
|
|
||||||
// Adds a HBS helper to the expression
|
// Adds a HBS helper to the expression
|
||||||
const addHelper = helper => {
|
const addHelper = helper => {
|
||||||
hbsValue = addHBSBinding(value, getCaretPosition(), helper.text)
|
hbsValue = addHBSBinding(hbsValue, getCaretPosition(), helper.text)
|
||||||
updateValue(hbsValue)
|
updateValue(hbsValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
export function addHBSBinding(value, caretPos, binding) {
|
export function addHBSBinding(value, caretPos, binding) {
|
||||||
binding = typeof binding === "string" ? binding : binding.path
|
binding = typeof binding === "string" ? binding : binding.path
|
||||||
value = value == null ? "" : value
|
value = value == null ? "" : value
|
||||||
if (!value.includes("{{") && !value.includes("}}")) {
|
|
||||||
|
const left = caretPos?.start ? value.substring(0, caretPos.start) : ""
|
||||||
|
const right = caretPos?.end ? value.substring(caretPos.end) : ""
|
||||||
|
if (!left.includes("{{") || !right.includes("}}")) {
|
||||||
binding = `{{ ${binding} }}`
|
binding = `{{ ${binding} }}`
|
||||||
}
|
}
|
||||||
if (caretPos.start) {
|
if (caretPos.start) {
|
||||||
|
|
|
@ -126,6 +126,7 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
{#if selectedActionComponent}
|
{#if selectedActionComponent}
|
||||||
|
{#key selectedAction.id}
|
||||||
<div class="selected-action-container">
|
<div class="selected-action-container">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={selectedActionComponent}
|
this={selectedActionComponent}
|
||||||
|
@ -133,6 +134,7 @@
|
||||||
bindings={allBindings}
|
bindings={allBindings}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { tables } from "stores/backend"
|
import { tables } from "stores/backend"
|
||||||
import {
|
import {
|
||||||
getContextProviderComponents,
|
getContextProviderComponents,
|
||||||
getSchemaForDatasource,
|
getSchemaForTable,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
import SaveFields from "./SaveFields.svelte"
|
import SaveFields from "./SaveFields.svelte"
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSchemaFields = (asset, tableId) => {
|
const getSchemaFields = (asset, tableId) => {
|
||||||
const { schema } = getSchemaForDatasource(asset, { type: "table", tableId })
|
const { schema } = getSchemaForTable(tableId)
|
||||||
delete schema._id
|
delete schema._id
|
||||||
delete schema._rev
|
delete schema._rev
|
||||||
return Object.values(schema || {})
|
return Object.values(schema || {})
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { tables } from "stores/backend"
|
import { tables } from "stores/backend"
|
||||||
import {
|
import {
|
||||||
getContextProviderComponents,
|
getContextProviderComponents,
|
||||||
getSchemaForDatasource,
|
getSchemaForTable,
|
||||||
} from "builderStore/dataBinding"
|
} from "builderStore/dataBinding"
|
||||||
import SaveFields from "./SaveFields.svelte"
|
import SaveFields from "./SaveFields.svelte"
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSchemaFields = (asset, tableId) => {
|
const getSchemaFields = (asset, tableId) => {
|
||||||
const { schema } = getSchemaForDatasource(asset, { type: "table", tableId })
|
const { schema } = getSchemaForTable(tableId)
|
||||||
return Object.values(schema || {})
|
return Object.values(schema || {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
DrawerContent,
|
||||||
|
Layout,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
Label,
|
||||||
|
Body,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { flip } from "svelte/animate"
|
||||||
|
import { dndzone } from "svelte-dnd-action"
|
||||||
|
import { generate } from "shortid"
|
||||||
|
|
||||||
|
export let columns = []
|
||||||
|
export let options = []
|
||||||
|
|
||||||
|
const flipDurationMs = 150
|
||||||
|
let dragDisabled = true
|
||||||
|
|
||||||
|
$: unselectedColumns = getUnselectedColumns(options, columns)
|
||||||
|
$: columns.forEach(column => {
|
||||||
|
if (!column.id) {
|
||||||
|
column.id = generate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getUnselectedColumns = (allColumns, selectedColumns) => {
|
||||||
|
let optionsObj = {}
|
||||||
|
allColumns.forEach(option => {
|
||||||
|
optionsObj[option] = true
|
||||||
|
})
|
||||||
|
selectedColumns?.forEach(column => {
|
||||||
|
delete optionsObj[column.name]
|
||||||
|
})
|
||||||
|
return Object.keys(optionsObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRemainingColumnOptions = selectedColumn => {
|
||||||
|
if (!selectedColumn || unselectedColumns.includes(selectedColumn)) {
|
||||||
|
return unselectedColumns
|
||||||
|
}
|
||||||
|
return [selectedColumn, ...unselectedColumns]
|
||||||
|
}
|
||||||
|
|
||||||
|
const addColumn = () => {
|
||||||
|
columns = [...columns, {}]
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeColumn = id => {
|
||||||
|
columns = columns.filter(column => column.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateColumnOrder = e => {
|
||||||
|
columns = e.detail.items
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFinalize = e => {
|
||||||
|
updateColumnOrder(e)
|
||||||
|
dragDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
columns = options.map(col => ({
|
||||||
|
name: col,
|
||||||
|
displayName: col,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerContent>
|
||||||
|
<div class="container">
|
||||||
|
<Layout noPadding gap="S">
|
||||||
|
{#if columns?.length}
|
||||||
|
<Layout noPadding gap="XS">
|
||||||
|
<div class="column">
|
||||||
|
<div />
|
||||||
|
<Label size="L">Column</Label>
|
||||||
|
<Label size="L">Label</Label>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="columns"
|
||||||
|
use:dndzone={{
|
||||||
|
items: columns,
|
||||||
|
flipDurationMs,
|
||||||
|
dropTargetStyle: { outline: "none" },
|
||||||
|
dragDisabled,
|
||||||
|
}}
|
||||||
|
on:finalize={handleFinalize}
|
||||||
|
on:consider={updateColumnOrder}
|
||||||
|
>
|
||||||
|
{#each columns as column (column.id)}
|
||||||
|
<div class="column" animate:flip={{ duration: flipDurationMs }}>
|
||||||
|
<div
|
||||||
|
class="handle"
|
||||||
|
aria-label="drag-handle"
|
||||||
|
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
|
||||||
|
on:mousedown={() => (dragDisabled = false)}
|
||||||
|
>
|
||||||
|
<Icon name="DragHandle" size="XL" />
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
bind:value={column.name}
|
||||||
|
placeholder="Column"
|
||||||
|
options={getRemainingColumnOptions(column.name)}
|
||||||
|
on:change={e => (column.displayName = e.detail)}
|
||||||
|
/>
|
||||||
|
<Input bind:value={column.displayName} placeholder="Label" />
|
||||||
|
<Icon
|
||||||
|
name="Close"
|
||||||
|
hoverable
|
||||||
|
size="S"
|
||||||
|
on:click={() => removeColumn(column.id)}
|
||||||
|
disabled={columns.length === 1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
{:else}
|
||||||
|
<div class="column">
|
||||||
|
<div />
|
||||||
|
<Body size="S">Add the first column to your table.</Body>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<div />
|
||||||
|
<div class="buttons">
|
||||||
|
<Button secondary icon="Add" on:click={addColumn}>
|
||||||
|
Add column
|
||||||
|
</Button>
|
||||||
|
<Button secondary quiet on:click={reset}>Reset columns</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.columns {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.column {
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 20px 1fr 1fr 20px;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: var(--border-radius-s);
|
||||||
|
transition: background-color ease-in-out 130ms;
|
||||||
|
}
|
||||||
|
.column:hover {
|
||||||
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
|
}
|
||||||
|
.handle {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script>
|
||||||
|
import { Button, ActionButton, Drawer } from "@budibase/bbui"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
import ColumnDrawer from "./ColumnDrawer.svelte"
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import {
|
||||||
|
getDatasourceForProvider,
|
||||||
|
getSchemaForDatasource,
|
||||||
|
} from "builderStore/dataBinding"
|
||||||
|
import { currentAsset } from "builderStore"
|
||||||
|
|
||||||
|
export let componentInstance
|
||||||
|
export let value = []
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let drawer
|
||||||
|
let boundValue
|
||||||
|
|
||||||
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
|
$: schema = getSchemaForDatasource($currentAsset, datasource).schema
|
||||||
|
$: options = Object.keys(schema || {})
|
||||||
|
$: sanitisedValue = getValidColumns(value, options)
|
||||||
|
$: updateBoundValue(sanitisedValue)
|
||||||
|
|
||||||
|
const updateBoundValue = value => {
|
||||||
|
boundValue = cloneDeep(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getValidColumns = (columns, options) => {
|
||||||
|
// If no columns then default to all columns
|
||||||
|
if (!Array.isArray(columns) || !columns.length) {
|
||||||
|
return options.map(col => ({
|
||||||
|
name: col,
|
||||||
|
displayName: col,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
// We need to account for legacy configs which would just be an array
|
||||||
|
// of strings
|
||||||
|
if (typeof columns[0] === "string") {
|
||||||
|
columns = columns.map(col => ({
|
||||||
|
name: col,
|
||||||
|
displayName: col,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return columns.filter(column => {
|
||||||
|
return options.includes(column.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
dispatch("change", getValidColumns(boundValue, options))
|
||||||
|
drawer.hide()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ActionButton on:click={drawer.show}>Configure columns</ActionButton>
|
||||||
|
<Drawer bind:this={drawer} title="Table Columns">
|
||||||
|
<svelte:fragment slot="description">
|
||||||
|
Configure the columns in your table.
|
||||||
|
</svelte:fragment>
|
||||||
|
<Button cta slot="buttons" on:click={save}>Save</Button>
|
||||||
|
<ColumnDrawer slot="body" bind:columns={boundValue} {options} />
|
||||||
|
</Drawer>
|
|
@ -90,7 +90,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Body size="S">
|
<Body size="S">
|
||||||
{#if !filters?.length}
|
{#if !filters?.length}
|
||||||
|
@ -184,6 +184,7 @@
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fields {
|
.fields {
|
||||||
display: grid;
|
display: grid;
|
||||||
column-gap: var(--spacing-l);
|
column-gap: var(--spacing-l);
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
let tempValue = value || []
|
let tempValue = value || []
|
||||||
|
|
||||||
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
|
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
$: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
|
$: schema = getSchemaForDatasource($currentAsset, dataSource, {
|
||||||
|
searchableSchema: true,
|
||||||
|
})?.schema
|
||||||
$: schemaFields = Object.values(schema || {})
|
$: schemaFields = Object.values(schema || {})
|
||||||
|
|
||||||
const saveFilter = async () => {
|
const saveFilter = async () => {
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
component => component._component === "@budibase/standard-components/form"
|
component => component._component === "@budibase/standard-components/form"
|
||||||
)
|
)
|
||||||
$: datasource = getDatasourceForProvider($currentAsset, form)
|
$: datasource = getDatasourceForProvider($currentAsset, form)
|
||||||
$: schema = getSchemaForDatasource($currentAsset, datasource, true).schema
|
$: schema = getSchemaForDatasource($currentAsset, datasource, {
|
||||||
|
formSchema: true,
|
||||||
|
}).schema
|
||||||
$: options = getOptions(schema, type)
|
$: options = getOptions(schema, type)
|
||||||
|
|
||||||
const getOptions = (schema, type) => {
|
const getOptions = (schema, type) => {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ActionButton on:click={drawer.show}>Configure Links</ActionButton>
|
<ActionButton on:click={drawer.show}>Configure links</ActionButton>
|
||||||
<Drawer bind:this={drawer} title={"Navigation Links"}>
|
<Drawer bind:this={drawer} title={"Navigation Links"}>
|
||||||
<svelte:fragment slot="description">
|
<svelte:fragment slot="description">
|
||||||
Configure the links in your navigation bar.
|
Configure the links in your navigation bar.
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||||
$: schema = getSchemaForDatasource($currentAsset, datasource).schema
|
$: schema = getSchemaForDatasource($currentAsset, datasource, {
|
||||||
|
searchableSchema: true,
|
||||||
|
}).schema
|
||||||
$: options = getOptions(datasource, schema || {})
|
$: options = getOptions(datasource, schema || {})
|
||||||
$: boundValue = getSelectedOption(value, options)
|
$: boundValue = getSelectedOption(value, options)
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,7 @@
|
||||||
$: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {})
|
$: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {})
|
||||||
$: fieldType = type?.split("/")[1] || "string"
|
$: fieldType = type?.split("/")[1] || "string"
|
||||||
$: constraintOptions = getConstraintsForType(fieldType)
|
$: constraintOptions = getConstraintsForType(fieldType)
|
||||||
|
|
||||||
const getConstraintsForType = type => {
|
const getConstraintsForType = type => {
|
||||||
return ConstraintMap[type]
|
return ConstraintMap[type]
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import OptionsEditor from "./OptionsEditor/OptionsEditor.svelte"
|
||||||
import FormFieldSelect from "./FormFieldSelect.svelte"
|
import FormFieldSelect from "./FormFieldSelect.svelte"
|
||||||
import ValidationEditor from "./ValidationEditor/ValidationEditor.svelte"
|
import ValidationEditor from "./ValidationEditor/ValidationEditor.svelte"
|
||||||
import DrawerBindableCombobox from "components/common/bindings/DrawerBindableCombobox.svelte"
|
import DrawerBindableCombobox from "components/common/bindings/DrawerBindableCombobox.svelte"
|
||||||
|
import ColumnEditor from "./ColumnEditor/ColumnEditor.svelte"
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
text: DrawerBindableCombobox,
|
text: DrawerBindableCombobox,
|
||||||
|
@ -40,6 +41,7 @@ const componentMap = {
|
||||||
navigation: NavigationEditor,
|
navigation: NavigationEditor,
|
||||||
filter: FilterEditor,
|
filter: FilterEditor,
|
||||||
url: URLSelect,
|
url: URLSelect,
|
||||||
|
columns: ColumnEditor,
|
||||||
"field/string": FormFieldSelect,
|
"field/string": FormFieldSelect,
|
||||||
"field/number": FormFieldSelect,
|
"field/number": FormFieldSelect,
|
||||||
"field/options": FormFieldSelect,
|
"field/options": FormFieldSelect,
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
$: datasource = $datasources.list.find(ds => ds._id === query.datasourceId)
|
$: datasource = $datasources.list.find(ds => ds._id === query.datasourceId)
|
||||||
$: query.schema = fieldsToSchema(fields)
|
$: query.schema = fieldsToSchema(fields)
|
||||||
$: datasourceType = datasource?.source
|
$: datasourceType = datasource?.source
|
||||||
$: integrationInfo = $integrations[datasourceType]
|
$: integrationInfo = datasourceType ? $integrations[datasourceType] : null
|
||||||
$: queryConfig = integrationInfo?.query
|
$: queryConfig = integrationInfo?.query
|
||||||
$: shouldShowQueryConfig = queryConfig && query.queryVerb
|
$: shouldShowQueryConfig = queryConfig && query.queryVerb
|
||||||
$: readQuery = query.queryVerb === "read" || query.readable
|
$: readQuery = query.queryVerb === "read" || query.readable
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="viewer-controls">
|
<div class="viewer-controls">
|
||||||
<Heading size="S">Results</Heading>
|
<Heading size="S">Results</Heading>
|
||||||
<ButtonGroup>
|
<ButtonGroup gap="M">
|
||||||
<Button cta disabled={queryInvalid} on:click={saveQuery}>
|
<Button cta disabled={queryInvalid} on:click={saveQuery}>
|
||||||
Save Query
|
Save Query
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -15,6 +15,7 @@ export default ({ mode }) => {
|
||||||
build: {
|
build: {
|
||||||
minify: isProduction,
|
minify: isProduction,
|
||||||
outDir: "../server/builder",
|
outDir: "../server/builder",
|
||||||
|
sourcemap: !isProduction,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
svelte({
|
svelte({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"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": {
|
||||||
|
|
|
@ -15,7 +15,7 @@ const makeEnv = require("./makeEnv")
|
||||||
const axios = require("axios")
|
const axios = require("axios")
|
||||||
const AnalyticsClient = require("../analytics/Client")
|
const AnalyticsClient = require("../analytics/Client")
|
||||||
|
|
||||||
const BUDIBASE_SERVICES = ["app-service", "worker-service"]
|
const BUDIBASE_SERVICES = ["app-service", "worker-service", "proxy-service"]
|
||||||
const ERROR_FILE = "docker-error.log"
|
const ERROR_FILE = "docker-error.log"
|
||||||
const FILE_URLS = [
|
const FILE_URLS = [
|
||||||
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml",
|
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml",
|
||||||
|
|
1
packages/client/.gitignore
vendored
1
packages/client/.gitignore
vendored
|
@ -3,3 +3,4 @@ node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
release/
|
release/
|
||||||
dist/
|
dist/
|
||||||
|
stats.html
|
|
@ -851,16 +851,12 @@
|
||||||
"type": "color",
|
"type": "color",
|
||||||
"label": "Color",
|
"label": "Color",
|
||||||
"key": "color",
|
"key": "color",
|
||||||
"showInBar": true,
|
"showInBar": true
|
||||||
"barSeparator": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"label": "Show delete icon",
|
"label": "Show delete icon",
|
||||||
"key": "closable",
|
"key": "closable"
|
||||||
"showInBar": true,
|
|
||||||
"barIcon": "TagItalic",
|
|
||||||
"barTitle": "Show delete icon"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "event",
|
"type": "event",
|
||||||
|
@ -2680,11 +2676,10 @@
|
||||||
"defaultValue": 8
|
"defaultValue": 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "multifield",
|
"type": "columns",
|
||||||
"label": "Columns",
|
"label": "Columns",
|
||||||
"key": "columns",
|
"key": "columns",
|
||||||
"dependsOn": "dataProvider",
|
"dependsOn": "dataProvider"
|
||||||
"placeholder": "All columns"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
@ -2951,7 +2946,7 @@
|
||||||
"defaultValue": 8
|
"defaultValue": 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "multifield",
|
"type": "columns",
|
||||||
"label": "Table Columns",
|
"label": "Table Columns",
|
||||||
"key": "tableColumns",
|
"key": "tableColumns",
|
||||||
"dependsOn": "dataSource",
|
"dependsOn": "dataSource",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"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,18 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.58-alpha.4",
|
"@budibase/bbui": "^1.0.66-alpha.0",
|
||||||
"@budibase/frontend-core": "^1.0.58-alpha.4",
|
"@budibase/frontend-core": "^1.0.66-alpha.0",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.4",
|
"@budibase/string-templates": "^1.0.66-alpha.0",
|
||||||
"regexparam": "^1.3.0",
|
|
||||||
"rollup-plugin-polyfill-node": "^0.8.0",
|
|
||||||
"shortid": "^2.2.15",
|
|
||||||
"svelte-spa-router": "^3.0.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@rollup/plugin-alias": "^3.1.5",
|
|
||||||
"@rollup/plugin-commonjs": "^18.0.0",
|
|
||||||
"@rollup/plugin-node-resolve": "^11.2.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",
|
||||||
|
@ -41,8 +32,18 @@
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
"apexcharts": "^3.22.1",
|
"apexcharts": "^3.22.1",
|
||||||
"dayjs": "^1.10.5",
|
"dayjs": "^1.10.5",
|
||||||
"fs-extra": "^8.1.0",
|
"regexparam": "^1.3.0",
|
||||||
"jsdom": "^16.0.1",
|
"rollup-plugin-polyfill-node": "^0.8.0",
|
||||||
|
"shortid": "^2.2.15",
|
||||||
|
"svelte": "^3.38.2",
|
||||||
|
"svelte-apexcharts": "^1.0.2",
|
||||||
|
"svelte-flatpickr": "^3.1.0",
|
||||||
|
"svelte-spa-router": "^3.0.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-alias": "^3.1.5",
|
||||||
|
"@rollup/plugin-commonjs": "^18.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^11.2.1",
|
||||||
"postcss": "^8.2.10",
|
"postcss": "^8.2.10",
|
||||||
"rollup": "^2.44.0",
|
"rollup": "^2.44.0",
|
||||||
"rollup-plugin-json": "^4.0.0",
|
"rollup-plugin-json": "^4.0.0",
|
||||||
|
@ -50,9 +51,7 @@
|
||||||
"rollup-plugin-svelte": "^7.1.0",
|
"rollup-plugin-svelte": "^7.1.0",
|
||||||
"rollup-plugin-svg": "^2.0.0",
|
"rollup-plugin-svg": "^2.0.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"svelte": "^3.38.2",
|
"rollup-plugin-visualizer": "^5.5.4"
|
||||||
"svelte-apexcharts": "^1.0.2",
|
|
||||||
"svelte-flatpickr": "^3.1.0"
|
|
||||||
},
|
},
|
||||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
|
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import svg from "rollup-plugin-svg"
|
||||||
import json from "rollup-plugin-json"
|
import json from "rollup-plugin-json"
|
||||||
import nodePolyfills from "rollup-plugin-polyfill-node"
|
import nodePolyfills from "rollup-plugin-polyfill-node"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
import { visualizer } from "rollup-plugin-visualizer"
|
||||||
|
|
||||||
const production = !process.env.ROLLUP_WATCH
|
const production = !process.env.ROLLUP_WATCH
|
||||||
const ignoredWarnings = [
|
const ignoredWarnings = [
|
||||||
|
@ -79,6 +80,7 @@ export default {
|
||||||
svg(),
|
svg(),
|
||||||
json(),
|
json(),
|
||||||
production && terser(),
|
production && terser(),
|
||||||
|
!production && visualizer(),
|
||||||
],
|
],
|
||||||
watch: {
|
watch: {
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
<script context="module">
|
<script context="module">
|
||||||
|
// Cache the definition of settings for each component type
|
||||||
let SettingsDefinitionCache = {}
|
let SettingsDefinitionCache = {}
|
||||||
|
|
||||||
|
// Cache the settings of each component ID.
|
||||||
|
// This speeds up remounting as well as repeaters.
|
||||||
|
let InstanceSettingsCache = {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getContext, setContext } from "svelte"
|
import { getContext, setContext } from "svelte"
|
||||||
import { writable } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import * as AppComponents from "components/app"
|
import * as AppComponents from "components/app"
|
||||||
import Router from "./Router.svelte"
|
import Router from "./Router.svelte"
|
||||||
import { enrichProps, propsAreSame } from "utils/componentProps"
|
import { enrichProps, propsAreSame } from "utils/componentProps"
|
||||||
|
@ -19,6 +24,14 @@
|
||||||
export let isScreen = false
|
export let isScreen = false
|
||||||
export let isBlock = false
|
export let isBlock = false
|
||||||
|
|
||||||
|
// Get parent contexts
|
||||||
|
const context = getContext("context")
|
||||||
|
const insideScreenslot = !!getContext("screenslot")
|
||||||
|
|
||||||
|
// Create component context
|
||||||
|
const componentStore = writable({})
|
||||||
|
setContext("component", componentStore)
|
||||||
|
|
||||||
// Ref to the svelte component
|
// Ref to the svelte component
|
||||||
let ref
|
let ref
|
||||||
|
|
||||||
|
@ -29,12 +42,11 @@
|
||||||
// would happen if we spread cachedSettings directly to the component.
|
// would happen if we spread cachedSettings directly to the component.
|
||||||
let initialSettings
|
let initialSettings
|
||||||
|
|
||||||
// Component settings are the un-enriched settings for this component that
|
// Dynamic settings contain bindings and need enriched
|
||||||
// need to be enriched at this level.
|
let dynamicSettings
|
||||||
// Nested settings are the un-enriched block settings that are to be passed on
|
|
||||||
// and enriched at a deeper level.
|
// Static settings do not contain any bindings and can be passed on down
|
||||||
let componentSettings
|
let staticSettings
|
||||||
let nestedSettings
|
|
||||||
|
|
||||||
// The enriched component settings
|
// The enriched component settings
|
||||||
let enrichedSettings
|
let enrichedSettings
|
||||||
|
@ -59,18 +71,14 @@
|
||||||
// Visibility flag used by conditional UI
|
// Visibility flag used by conditional UI
|
||||||
let visible = true
|
let visible = true
|
||||||
|
|
||||||
// Get contexts
|
// Component information derived during initialisation
|
||||||
const context = getContext("context")
|
let constructor
|
||||||
const insideScreenslot = !!getContext("screenslot")
|
let definition
|
||||||
|
|
||||||
// Create component context
|
// Set up initial state for each new component instance
|
||||||
const componentStore = writable({})
|
$: initialise(instance)
|
||||||
setContext("component", componentStore)
|
|
||||||
|
|
||||||
// Extract component instance info
|
// Extract component instance info
|
||||||
$: constructor = getComponentConstructor(instance._component)
|
|
||||||
$: definition = getComponentDefinition(instance._component)
|
|
||||||
$: settingsDefinition = getSettingsDefinition(definition)
|
|
||||||
$: children = instance._children || []
|
$: children = instance._children || []
|
||||||
$: id = instance._id
|
$: id = instance._id
|
||||||
$: name = instance._instanceName
|
$: name = instance._instanceName
|
||||||
|
@ -104,19 +112,17 @@
|
||||||
$: empty = interactive && !children.length && hasChildren
|
$: empty = interactive && !children.length && hasChildren
|
||||||
$: emptyState = empty && showEmptyState
|
$: emptyState = empty && showEmptyState
|
||||||
|
|
||||||
// Raw settings are all settings excluding internal props and children
|
// Enrich component settings
|
||||||
$: rawSettings = getRawSettings(instance)
|
$: enrichComponentSettings($context)
|
||||||
$: instanceKey = Helpers.hashString(JSON.stringify(rawSettings))
|
|
||||||
|
|
||||||
// Update and enrich component settings
|
|
||||||
$: updateSettings(rawSettings, instanceKey, settingsDefinition, $context)
|
|
||||||
|
|
||||||
// Evaluate conditional UI settings and store any component setting changes
|
// Evaluate conditional UI settings and store any component setting changes
|
||||||
// which need to be made
|
// which need to be made. This is broken into 2 lines to avoid svelte
|
||||||
$: evaluateConditions(enrichedSettings?._conditions)
|
// reactivity re-evaluating conditions more often than necessary.
|
||||||
|
$: conditions = enrichedSettings?._conditions
|
||||||
|
$: evaluateConditions(conditions)
|
||||||
|
|
||||||
// Build up the final settings object to be passed to the component
|
// Determine and apply settings to the component
|
||||||
$: cacheSettings(enrichedSettings, nestedSettings, conditionalSettings)
|
$: applySettings(staticSettings, enrichedSettings, conditionalSettings)
|
||||||
|
|
||||||
// Update component context
|
// Update component context
|
||||||
$: componentStore.set({
|
$: componentStore.set({
|
||||||
|
@ -136,15 +142,50 @@
|
||||||
editing,
|
editing,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Extracts all settings from the component instance
|
const initialise = instance => {
|
||||||
const getRawSettings = instance => {
|
if (instance == null) {
|
||||||
let validSettings = {}
|
return
|
||||||
Object.entries(instance)
|
}
|
||||||
.filter(([name]) => name === "_conditions" || !name.startsWith("_"))
|
|
||||||
.forEach(([key, value]) => {
|
// Ensure we're processing a new instance
|
||||||
validSettings[key] = value
|
const instanceKey = Helpers.hashString(JSON.stringify(instance))
|
||||||
})
|
if (instanceKey === lastInstanceKey) {
|
||||||
return validSettings
|
return
|
||||||
|
} else {
|
||||||
|
lastInstanceKey = instanceKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull definition and constructor
|
||||||
|
constructor = getComponentConstructor(instance._component)
|
||||||
|
definition = getComponentDefinition(instance._component)
|
||||||
|
if (!definition) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the settings definition for this component, and cache it
|
||||||
|
let settingsDefinition
|
||||||
|
if (SettingsDefinitionCache[definition.name]) {
|
||||||
|
settingsDefinition = SettingsDefinitionCache[definition.name]
|
||||||
|
} else {
|
||||||
|
settingsDefinition = getSettingsDefinition(definition)
|
||||||
|
SettingsDefinitionCache[definition.name] = settingsDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the instance settings, and cache them
|
||||||
|
let instanceSettings
|
||||||
|
if (InstanceSettingsCache[instanceKey]) {
|
||||||
|
instanceSettings = InstanceSettingsCache[instanceKey]
|
||||||
|
} else {
|
||||||
|
instanceSettings = getInstanceSettings(instance, settingsDefinition)
|
||||||
|
InstanceSettingsCache[instanceKey] = instanceSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the settings types
|
||||||
|
staticSettings = instanceSettings.staticSettings
|
||||||
|
dynamicSettings = instanceSettings.dynamicSettings
|
||||||
|
|
||||||
|
// Force an initial enrichment of the new settings
|
||||||
|
enrichComponentSettings(get(context), { force: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the component constructor for the specified component
|
// Gets the component constructor for the specified component
|
||||||
|
@ -169,9 +210,6 @@
|
||||||
if (!definition) {
|
if (!definition) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
if (SettingsDefinitionCache[definition.name]) {
|
|
||||||
return SettingsDefinitionCache[definition.name]
|
|
||||||
}
|
|
||||||
let settings = []
|
let settings = []
|
||||||
definition.settings?.forEach(setting => {
|
definition.settings?.forEach(setting => {
|
||||||
if (setting.section) {
|
if (setting.section) {
|
||||||
|
@ -180,63 +218,69 @@
|
||||||
settings.push(setting)
|
settings.push(setting)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
SettingsDefinitionCache[definition] = settings
|
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates and enriches component settings when raw settings change
|
const getInstanceSettings = (instance, settingsDefinition) => {
|
||||||
const updateSettings = (settings, key, settingsDefinition, context) => {
|
// Get raw settings
|
||||||
const instanceChanged = key !== lastInstanceKey
|
let settings = {}
|
||||||
|
Object.entries(instance)
|
||||||
|
.filter(([name]) => name === "_conditions" || !name.startsWith("_"))
|
||||||
|
.forEach(([key, value]) => {
|
||||||
|
settings[key] = value
|
||||||
|
})
|
||||||
|
|
||||||
// Derive component and nested settings if the instance changed
|
// Derive static, dynamic and nested settings if the instance changed
|
||||||
if (instanceChanged) {
|
let newStaticSettings = { ...settings }
|
||||||
splitRawSettings(settings, settingsDefinition)
|
let newDynamicSettings = { ...settings }
|
||||||
}
|
|
||||||
|
|
||||||
// Enrich component settings
|
|
||||||
enrichComponentSettings(componentSettings, context, instanceChanged)
|
|
||||||
|
|
||||||
// Update instance key
|
|
||||||
if (instanceChanged) {
|
|
||||||
lastInstanceKey = key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Splits the raw settings into those destined for the component itself
|
|
||||||
// and nexted settings for child components inside blocks
|
|
||||||
const splitRawSettings = (rawSettings, settingsDefinition) => {
|
|
||||||
let newComponentSettings = { ...rawSettings }
|
|
||||||
let newNestedSettings = { ...rawSettings }
|
|
||||||
settingsDefinition?.forEach(setting => {
|
settingsDefinition?.forEach(setting => {
|
||||||
if (setting.nested) {
|
if (setting.nested) {
|
||||||
delete newComponentSettings[setting.key]
|
delete newDynamicSettings[setting.key]
|
||||||
} else {
|
} else {
|
||||||
delete newNestedSettings[setting.key]
|
const value = settings[setting.key]
|
||||||
|
if (value == null) {
|
||||||
|
delete newDynamicSettings[setting.key]
|
||||||
|
} else if (typeof value === "string" && value.includes("{{")) {
|
||||||
|
// Strings can be trivially checked
|
||||||
|
delete newStaticSettings[setting.key]
|
||||||
|
} else if (value[0]?.["##eventHandlerType"] != null) {
|
||||||
|
// Always treat button actions as dynamic
|
||||||
|
delete newStaticSettings[setting.key]
|
||||||
|
} else if (typeof value === "object") {
|
||||||
|
// Stringify and check objects
|
||||||
|
const stringified = JSON.stringify(value)
|
||||||
|
if (stringified.includes("{{")) {
|
||||||
|
delete newStaticSettings[setting.key]
|
||||||
|
} else {
|
||||||
|
delete newDynamicSettings[setting.key]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For other types, we can safely assume they are static
|
||||||
|
delete newDynamicSettings[setting.key]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
componentSettings = newComponentSettings
|
|
||||||
nestedSettings = newNestedSettings
|
return {
|
||||||
|
staticSettings: newStaticSettings,
|
||||||
|
dynamicSettings: newDynamicSettings,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enriches any string component props using handlebars
|
// Enriches any string component props using handlebars
|
||||||
const enrichComponentSettings = (rawSettings, context, instanceChanged) => {
|
const enrichComponentSettings = (context, options = { force: false }) => {
|
||||||
const contextChanged = context.key !== lastContextKey
|
const contextChanged = context.key !== lastContextKey
|
||||||
|
if (!contextChanged && !options?.force) {
|
||||||
// Skip enrichment if the context and instance are unchanged
|
|
||||||
if (!contextChanged) {
|
|
||||||
if (!instanceChanged) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
lastContextKey = context.key
|
lastContextKey = context.key
|
||||||
}
|
|
||||||
|
|
||||||
// Record the timestamp so we can reference it after enrichment
|
// Record the timestamp so we can reference it after enrichment
|
||||||
latestUpdateTime = Date.now()
|
latestUpdateTime = Date.now()
|
||||||
const enrichmentTime = latestUpdateTime
|
const enrichmentTime = latestUpdateTime
|
||||||
|
|
||||||
// Enrich settings with context
|
// Enrich settings with context
|
||||||
const newEnrichedSettings = enrichProps(rawSettings, context)
|
const newEnrichedSettings = enrichProps(dynamicSettings, context)
|
||||||
|
|
||||||
// Abandon this update if a newer update has started
|
// Abandon this update if a newer update has started
|
||||||
if (enrichmentTime !== latestUpdateTime) {
|
if (enrichmentTime !== latestUpdateTime) {
|
||||||
|
@ -271,8 +315,16 @@
|
||||||
// Combines and caches all settings which will be passed to the component
|
// Combines and caches all settings which will be passed to the component
|
||||||
// instance. Settings are aggressively memoized to avoid triggering svelte
|
// instance. Settings are aggressively memoized to avoid triggering svelte
|
||||||
// reactive statements as much as possible.
|
// reactive statements as much as possible.
|
||||||
const cacheSettings = (enriched, nested, conditional) => {
|
const applySettings = (
|
||||||
const allSettings = { ...enriched, ...nested, ...conditional }
|
staticSettings,
|
||||||
|
enrichedSettings,
|
||||||
|
conditionalSettings
|
||||||
|
) => {
|
||||||
|
const allSettings = {
|
||||||
|
...staticSettings,
|
||||||
|
...enrichedSettings,
|
||||||
|
...conditionalSettings,
|
||||||
|
}
|
||||||
if (!cachedSettings) {
|
if (!cachedSettings) {
|
||||||
cachedSettings = { ...allSettings }
|
cachedSettings = { ...allSettings }
|
||||||
initialSettings = cachedSettings
|
initialSettings = cachedSettings
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { derived, get, writable } from "svelte/store"
|
import { derived, get, writable } from "svelte/store"
|
||||||
import { createValidatorFromConstraints } from "./validation"
|
import { createValidatorFromConstraints } from "./validation"
|
||||||
import { Helpers } from "@budibase/bbui"
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { cloneDeep } from "lodash/fp"
|
|
||||||
|
|
||||||
export let dataSource
|
export let dataSource
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
@ -96,7 +95,7 @@
|
||||||
// Derive the overall form value and deeply set all field paths so that we
|
// Derive the overall form value and deeply set all field paths so that we
|
||||||
// can support things like JSON fields.
|
// can support things like JSON fields.
|
||||||
const deriveFormValue = (initialValues, values, enrichments) => {
|
const deriveFormValue = (initialValues, values, enrichments) => {
|
||||||
let formValue = cloneDeep(initialValues || {})
|
let formValue = Helpers.cloneDeep(initialValues || {})
|
||||||
|
|
||||||
// We need to sort the keys to avoid a JSON field overwriting a nested field
|
// We need to sort the keys to avoid a JSON field overwriting a nested field
|
||||||
const sortedFields = Object.entries(values || {})
|
const sortedFields = Object.entries(values || {})
|
||||||
|
|
|
@ -40,7 +40,8 @@
|
||||||
// Check for an invalid column selection
|
// Check for an invalid column selection
|
||||||
let invalid = false
|
let invalid = false
|
||||||
customColumns?.forEach(column => {
|
customColumns?.forEach(column => {
|
||||||
if (schema[column] == null) {
|
const columnName = typeof column === "string" ? column : column.name
|
||||||
|
if (schema[columnName] == null) {
|
||||||
invalid = true
|
invalid = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -75,9 +76,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.forEach(field => {
|
fields.forEach(field => {
|
||||||
newSchema[field] = schema[field]
|
const columnName = typeof field === "string" ? field : field.name
|
||||||
if (schema[field] && UnsortableTypes.indexOf(schema[field].type) !== -1) {
|
if (!schema[columnName]) {
|
||||||
newSchema[field].sortable = false
|
return
|
||||||
|
}
|
||||||
|
newSchema[columnName] = schema[columnName]
|
||||||
|
if (UnsortableTypes.includes(schema[columnName].type)) {
|
||||||
|
newSchema[columnName].sortable = false
|
||||||
|
}
|
||||||
|
if (field?.displayName) {
|
||||||
|
newSchema[columnName].displayName = field?.displayName
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return newSchema
|
return newSchema
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { enrichDataBindings } from "./enrichDataBinding"
|
import { enrichDataBindings } from "./enrichDataBinding"
|
||||||
import { enrichButtonActions } from "./buttonActions"
|
import { enrichButtonActions } from "./buttonActions"
|
||||||
|
import { decodeJSBinding } from "@budibase/string-templates"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deeply compares 2 props using JSON.stringify.
|
* Deeply compares 2 props using JSON.stringify.
|
||||||
|
@ -43,9 +44,9 @@ export const enrichProps = (props, context) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handle conditional UI separately after normal settings
|
// Store the original conditions so that we can restore parts of them after
|
||||||
let conditions = normalProps._conditions
|
// enrichment
|
||||||
delete normalProps._conditions
|
let rawConditions = normalProps._conditions
|
||||||
|
|
||||||
// Enrich all props except button actions
|
// Enrich all props except button actions
|
||||||
let enrichedProps = enrichDataBindings(normalProps, totalContext)
|
let enrichedProps = enrichDataBindings(normalProps, totalContext)
|
||||||
|
@ -58,31 +59,51 @@ export const enrichProps = (props, context) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Conditions
|
// Conditions
|
||||||
if (conditions?.length) {
|
if (enrichedProps._conditions?.length) {
|
||||||
let enrichedConditions = []
|
enrichedProps._conditions.forEach((condition, idx) => {
|
||||||
conditions.forEach(condition => {
|
|
||||||
if (condition.setting?.toLowerCase().includes("onclick")) {
|
if (condition.setting?.toLowerCase().includes("onclick")) {
|
||||||
// Copy and remove the setting value from the condition as it needs
|
// Use the original condition action value to enrich it to a button
|
||||||
// enriched separately
|
// action
|
||||||
let toEnrich = { ...condition }
|
condition.settingValue = enrichButtonActions(
|
||||||
delete toEnrich.settingValue
|
rawConditions[idx].settingValue,
|
||||||
|
|
||||||
// Join the condition back together
|
|
||||||
enrichedConditions.push({
|
|
||||||
...enrichDataBindings(toEnrich, totalContext),
|
|
||||||
settingValue: enrichButtonActions(
|
|
||||||
condition.settingValue,
|
|
||||||
totalContext
|
totalContext
|
||||||
),
|
)
|
||||||
rand: Math.random(),
|
|
||||||
})
|
// Since we can't compare functions, we need to assume that conditions
|
||||||
} else {
|
// change after every enrichment
|
||||||
// Normal condition
|
condition.rand = Math.random()
|
||||||
enrichedConditions.push(enrichDataBindings(condition, totalContext))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
enrichedProps._conditions = enrichedConditions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return enrichedProps
|
return enrichedProps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a props object references a particular context binding.
|
||||||
|
* e.g. if props are { foo: "My name is {{ person.name }}" }, and we search for" +
|
||||||
|
* "person", then this function wil return true - the props do a context key
|
||||||
|
* called "person".
|
||||||
|
* @param props the props object to search
|
||||||
|
* @param bindingKey the key to search for
|
||||||
|
*/
|
||||||
|
export const propsUseBinding = (props, bindingKey) => {
|
||||||
|
if (!Object.keys(props || {}).length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const string = JSON.stringify(props)
|
||||||
|
const usedInHBS = string.includes(`[${bindingKey}]`)
|
||||||
|
if (usedInHBS) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const jsBindingRegex = new RegExp("{{ js [^}]+ }}", "g")
|
||||||
|
const jsBindings = [...string.matchAll(jsBindingRegex)]
|
||||||
|
for (let jsBinding of jsBindings) {
|
||||||
|
const encoded = jsBinding[0]
|
||||||
|
const js = decodeJSBinding(encoded)
|
||||||
|
if (js?.includes(`$("[${bindingKey}]`)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { processString, processObjectSync } from "@budibase/string-templates"
|
import { processString, processObjectSync } from "@budibase/string-templates"
|
||||||
|
|
||||||
// Regex to test inputs with to see if they are likely candidates for template strings
|
// Regex to test inputs with to see if they are likely candidates for template strings
|
||||||
|
@ -24,5 +24,5 @@ export const enrichDataBinding = async (input, context) => {
|
||||||
* Props are deeply cloned so that no mutation is done to the source object.
|
* Props are deeply cloned so that no mutation is done to the source object.
|
||||||
*/
|
*/
|
||||||
export const enrichDataBindings = (props, context) => {
|
export const enrichDataBindings = (props, context) => {
|
||||||
return processObjectSync(cloneDeep(props), context)
|
return processObjectSync(Helpers.cloneDeep(props), context, { cache: true })
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/frontend-core",
|
"name": "@budibase/frontend-core",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"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": "^1.0.58-alpha.4",
|
"@budibase/bbui": "^1.0.66-alpha.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import DataFetch from "./DataFetch.js"
|
import DataFetch from "./DataFetch.js"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
export default class QueryFetch extends DataFetch {
|
export default class QueryFetch extends DataFetch {
|
||||||
|
@ -36,7 +36,7 @@ export default class QueryFetch extends DataFetch {
|
||||||
const type = definition?.fields?.pagination?.type
|
const type = definition?.fields?.pagination?.type
|
||||||
|
|
||||||
// Set the default query params
|
// Set the default query params
|
||||||
let parameters = cloneDeep(datasource?.queryParams || {})
|
let parameters = Helpers.cloneDeep(datasource?.queryParams || {})
|
||||||
for (let param of datasource?.parameters || {}) {
|
for (let param of datasource?.parameters || {}) {
|
||||||
if (!parameters[param.name]) {
|
if (!parameters[param.name]) {
|
||||||
parameters[param.name] = param.default
|
parameters[param.name] = param.default
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -70,9 +70,9 @@
|
||||||
"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": "^1.0.58-alpha.4",
|
"@budibase/backend-core": "^1.0.66-alpha.0",
|
||||||
"@budibase/client": "^1.0.58-alpha.4",
|
"@budibase/client": "^1.0.66-alpha.0",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.4",
|
"@budibase/string-templates": "^1.0.66-alpha.0",
|
||||||
"@bull-board/api": "^3.7.0",
|
"@bull-board/api": "^3.7.0",
|
||||||
"@bull-board/koa": "^3.7.0",
|
"@bull-board/koa": "^3.7.0",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
const joiValidator = require("../../../middleware/joi-validator")
|
const joiValidator = require("../../../middleware/joi-validator")
|
||||||
const Joi = require("joi")
|
const Joi = require("joi")
|
||||||
|
|
||||||
|
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
||||||
|
|
||||||
exports.queryValidation = () => {
|
exports.queryValidation = () => {
|
||||||
return Joi.object({
|
return Joi.object({
|
||||||
_id: Joi.string(),
|
_id: Joi.string(),
|
||||||
|
@ -18,7 +20,7 @@ exports.queryValidation = () => {
|
||||||
queryVerb: Joi.string().allow().required(),
|
queryVerb: Joi.string().allow().required(),
|
||||||
extra: Joi.object().optional(),
|
extra: Joi.object().optional(),
|
||||||
schema: Joi.object({}).required().unknown(true),
|
schema: Joi.object({}).required().unknown(true),
|
||||||
transformer: Joi.string().optional(),
|
transformer: OPTIONAL_STRING,
|
||||||
flags: Joi.object().optional(),
|
flags: Joi.object().optional(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -31,18 +33,18 @@ exports.generateQueryValidation = () => {
|
||||||
exports.generateQueryPreviewValidation = () => {
|
exports.generateQueryPreviewValidation = () => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return joiValidator.body(Joi.object({
|
return joiValidator.body(Joi.object({
|
||||||
_id: Joi.string().optional(),
|
_id: OPTIONAL_STRING,
|
||||||
_rev: Joi.string().optional(),
|
_rev: OPTIONAL_STRING,
|
||||||
readable: Joi.boolean().optional(),
|
readable: Joi.boolean().optional(),
|
||||||
fields: Joi.object().required(),
|
fields: Joi.object().required(),
|
||||||
queryVerb: Joi.string().allow().required(),
|
queryVerb: Joi.string().required(),
|
||||||
name: Joi.string().required(),
|
name: OPTIONAL_STRING,
|
||||||
flags: Joi.object().optional(),
|
flags: Joi.object().optional(),
|
||||||
schema: Joi.object().optional(),
|
schema: Joi.object().optional(),
|
||||||
extra: Joi.object().optional(),
|
extra: Joi.object().optional(),
|
||||||
datasourceId: Joi.string().required(),
|
datasourceId: Joi.string().required(),
|
||||||
transformer: Joi.string().optional(),
|
transformer: OPTIONAL_STRING,
|
||||||
parameters: Joi.object({}).required().unknown(true),
|
parameters: Joi.object({}).required().unknown(true),
|
||||||
queryId: Joi.string().optional(),
|
queryId: OPTIONAL_STRING,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,7 +235,7 @@ class QueryBuilder {
|
||||||
if (this.sort) {
|
if (this.sort) {
|
||||||
const order = this.sortOrder === "descending" ? "-" : ""
|
const order = this.sortOrder === "descending" ? "-" : ""
|
||||||
const type = `<${this.sortType}>`
|
const type = `<${this.sortType}>`
|
||||||
body.sort = `${order}${this.sort.replace(/ /, "_")}${type}`
|
body.sort = `${order}${this.sort.replace(/ /g, "_")}${type}`
|
||||||
}
|
}
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ exports.createAllSearchIndex = async () => {
|
||||||
function idx(input, prev) {
|
function idx(input, prev) {
|
||||||
for (let key of Object.keys(input)) {
|
for (let key of Object.keys(input)) {
|
||||||
let idxKey = prev != null ? `${prev}.${key}` : key
|
let idxKey = prev != null ? `${prev}.${key}` : key
|
||||||
idxKey = idxKey.replace(/ /, "_")
|
idxKey = idxKey.replace(/ /g, "_")
|
||||||
if (Array.isArray(input[key])) {
|
if (Array.isArray(input[key])) {
|
||||||
for (let val of input[key]) {
|
for (let val of input[key]) {
|
||||||
if (typeof val !== "object") {
|
if (typeof val !== "object") {
|
||||||
|
|
|
@ -983,10 +983,10 @@
|
||||||
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@^1.0.58-alpha.1":
|
"@budibase/backend-core@^1.0.58-alpha.5":
|
||||||
version "1.0.62"
|
version "1.0.63"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.62.tgz#ca7f5b81fdf671157a237956fc903da48d7093a1"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.63.tgz#85808b55130e3812724ba362083c69c90c0a680c"
|
||||||
integrity sha512-cHPH44zSnhxXqdTn2FQjdZyhB/gOPDUJX1W6BHALsBf4aWdcCVA8EpDmYQD08z6wvQpYdXD85gJ8R2eXE8PBGA==
|
integrity sha512-9u7mOqHXKHD8Me6aeBzJTc2uT69g0LHnqnokMt/3hWYO9DBvyr/NeS41K9sGIXS91MbsYIvTM/oCQhefHN+PTg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@techpass/passport-openidconnect" "^0.3.0"
|
"@techpass/passport-openidconnect" "^0.3.0"
|
||||||
aws-sdk "^2.901.0"
|
aws-sdk "^2.901.0"
|
||||||
|
@ -1056,10 +1056,10 @@
|
||||||
svelte-flatpickr "^3.2.3"
|
svelte-flatpickr "^3.2.3"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/bbui@^1.0.62":
|
"@budibase/bbui@^1.0.63":
|
||||||
version "1.0.62"
|
version "1.0.63"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.0.62.tgz#985e64f428ff4fa66e936f803abb2368bc2813e0"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.0.63.tgz#4293c2e8baf6921ecf9fb119218bcbf315219641"
|
||||||
integrity sha512-nvzo5li6/UQaIl5MMkinu4gUAZkv7HqWHXrFKWbzTt4xBRO0D8C+QgH62X5ZRBTFd34jIEsNJP1isJl38LvESQ==
|
integrity sha512-h8IZHyNFyddHvp3J6toWNdYqnfnj3fyDPnczvQAHcX3xojC4Kk8uNruAA5UQLAuBQd8U44ZZ86lUZEPo720t6A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
||||||
"@spectrum-css/actionbutton" "^1.0.1"
|
"@spectrum-css/actionbutton" "^1.0.1"
|
||||||
|
@ -1107,25 +1107,25 @@
|
||||||
svelte-flatpickr "^3.2.3"
|
svelte-flatpickr "^3.2.3"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/client@^1.0.58-alpha.1":
|
"@budibase/client@^1.0.58-alpha.5":
|
||||||
version "1.0.62"
|
version "1.0.63"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-1.0.62.tgz#a737a94390149da57bb27b2b49bff94053e7660c"
|
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-1.0.63.tgz#dfc710350ccd18c74169af15700df1e3c7bfd55b"
|
||||||
integrity sha512-sX3VmOf9bpGmfkxqKfEvVrpD5X9BEsqoJ9jyjvy3IMHfIQ0Y+Yr0ZAJL25nrh33JL16Ir4CtDSCihdE7qtbA7Q==
|
integrity sha512-x6xqsgx++hMy5H/XOZtwZiKXQ4Y2SvxueHUWQe9mK0QCxHDDvBVtzSFWcoLgR7Sna8ATWI6MSWhJVvWq5P2dkw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/bbui" "^1.0.62"
|
"@budibase/bbui" "^1.0.63"
|
||||||
"@budibase/frontend-core" "^1.0.62"
|
"@budibase/frontend-core" "^1.0.63"
|
||||||
"@budibase/string-templates" "^1.0.62"
|
"@budibase/string-templates" "^1.0.63"
|
||||||
regexparam "^1.3.0"
|
regexparam "^1.3.0"
|
||||||
rollup-plugin-polyfill-node "^0.8.0"
|
rollup-plugin-polyfill-node "^0.8.0"
|
||||||
shortid "^2.2.15"
|
shortid "^2.2.15"
|
||||||
svelte-spa-router "^3.0.5"
|
svelte-spa-router "^3.0.5"
|
||||||
|
|
||||||
"@budibase/frontend-core@^1.0.62":
|
"@budibase/frontend-core@^1.0.63":
|
||||||
version "1.0.62"
|
version "1.0.63"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/frontend-core/-/frontend-core-1.0.62.tgz#8d7436bb22a327ba0f1343805323c33c87d44d84"
|
resolved "https://registry.yarnpkg.com/@budibase/frontend-core/-/frontend-core-1.0.63.tgz#9c3a8a4967c00863ab0fde1a754ebb170bd3103d"
|
||||||
integrity sha512-LF028SoFAXaHajNr3UYiKuBl+h3h4kDNdu2NfGQjkU96kZq4Aqq2Ey174jj2wT6k97kEV7ht2pBTR4jU7s4aWQ==
|
integrity sha512-4ArPq8aFnsfWf81n4sqZyaTNPVzld859HfLvyXD/hf568ut7ETGc5DskpjcfeyNnpLzgTek5g+D1bzCSm4RBbA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/bbui" "^1.0.62"
|
"@budibase/bbui" "^1.0.63"
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
svelte "^3.46.2"
|
svelte "^3.46.2"
|
||||||
|
|
||||||
|
@ -1172,10 +1172,10 @@
|
||||||
svelte-apexcharts "^1.0.2"
|
svelte-apexcharts "^1.0.2"
|
||||||
svelte-flatpickr "^3.1.0"
|
svelte-flatpickr "^3.1.0"
|
||||||
|
|
||||||
"@budibase/string-templates@^1.0.58-alpha.1", "@budibase/string-templates@^1.0.62":
|
"@budibase/string-templates@^1.0.58-alpha.5", "@budibase/string-templates@^1.0.63":
|
||||||
version "1.0.62"
|
version "1.0.63"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.62.tgz#f78ddde139a3f67e538c58b7e866244d1dbedfe8"
|
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.63.tgz#4a64ffd595fac412df5bacbba4555ad0a0dd4c96"
|
||||||
integrity sha512-0ujwPgJ6y54Wxyx6PH6qvg7ufioo+YtWtncrSDZ1bXdFX2uFHxrrHDYIQEG8c4e61ub/tvNESNAB/4Gb3IN4uA==
|
integrity sha512-oBY8wA7oylQobVwnT5wdTc2xdpPpuJ4kJ4pO75EfGo8lNGNnwqpic6e3icthx6fb+/DoNFGpEsUByOmAlVKtHg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/handlebars-helpers" "^0.11.7"
|
"@budibase/handlebars-helpers" "^0.11.7"
|
||||||
dayjs "^1.10.4"
|
dayjs "^1.10.4"
|
||||||
|
@ -2964,7 +2964,7 @@ acorn-walk@^7.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
||||||
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
||||||
|
|
||||||
acorn-walk@^8.1.1:
|
acorn-walk@^8.1.1, acorn-walk@^8.2.0:
|
||||||
version "8.2.0"
|
version "8.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||||
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||||
|
@ -2989,10 +2989,10 @@ acorn@^7.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
acorn@^8.2.4, acorn@^8.4.1:
|
acorn@^8.2.4, acorn@^8.4.1, acorn@^8.7.0:
|
||||||
version "8.5.0"
|
version "8.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
|
||||||
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
|
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
|
||||||
|
|
||||||
adal-node@^0.2.2:
|
adal-node@^0.2.2:
|
||||||
version "0.2.3"
|
version "0.2.3"
|
||||||
|
@ -12941,9 +12941,12 @@ verror@1.3.6:
|
||||||
extsprintf "1.0.2"
|
extsprintf "1.0.2"
|
||||||
|
|
||||||
vm2@^3.9.3, vm2@^3.9.4:
|
vm2@^3.9.3, vm2@^3.9.4:
|
||||||
version "3.9.5"
|
version "3.9.6"
|
||||||
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.5.tgz#5288044860b4bbace443101fcd3bddb2a0aa2496"
|
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.6.tgz#2f9b2fd0d82802dcd872e1011869ba8ae6b74778"
|
||||||
integrity sha512-LuCAHZN75H9tdrAiLFf030oW7nJV5xwNMuk1ymOZwopmuK3d2H4L1Kv4+GFHgarKiLfXXLFU+7LDABHnwOkWng==
|
integrity sha512-BF7euUjgO+ezsz2UKex9kO9M/PtDNOf+KEpiqNepZsgf1MT7JYfJEIvG8BoYhZMLAVjqevFJ0UmXNuETe8m5dQ==
|
||||||
|
dependencies:
|
||||||
|
acorn "^8.7.0"
|
||||||
|
acorn-walk "^8.2.0"
|
||||||
|
|
||||||
vuvuzela@1.0.3:
|
vuvuzela@1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
|
|
1
packages/string-templates/.gitignore
vendored
1
packages/string-templates/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
dist/
|
dist/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
stats.html
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.cjs",
|
"main": "src/index.cjs",
|
||||||
"module": "dist/bundle.mjs",
|
"module": "dist/bundle.mjs",
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
"manifest": "node ./scripts/gen-collection-info.js"
|
"manifest": "node ./scripts/gen-collection-info.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/handlebars-helpers": "^0.11.7",
|
"@budibase/handlebars-helpers": "^0.11.8",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.4",
|
||||||
"handlebars": "^4.7.6",
|
"handlebars": "^4.7.6",
|
||||||
"handlebars-utils": "^1.0.6",
|
"handlebars-utils": "^1.0.6",
|
||||||
|
|
|
@ -3,16 +3,22 @@ const { registerAll, registerMinimum } = require("./helpers/index")
|
||||||
const processors = require("./processors")
|
const processors = require("./processors")
|
||||||
const { atob, btoa } = require("./utilities")
|
const { atob, btoa } = require("./utilities")
|
||||||
const manifest = require("../manifest.json")
|
const manifest = require("../manifest.json")
|
||||||
const { FIND_HBS_REGEX, FIND_DOUBLE_HBS_REGEX } = require("./utilities")
|
const { FIND_HBS_REGEX, findDoubleHbsInstances } = require("./utilities")
|
||||||
|
|
||||||
const hbsInstance = handlebars.create()
|
const hbsInstance = handlebars.create()
|
||||||
registerAll(hbsInstance)
|
registerAll(hbsInstance)
|
||||||
const hbsInstanceNoHelpers = handlebars.create()
|
const hbsInstanceNoHelpers = handlebars.create()
|
||||||
registerMinimum(hbsInstanceNoHelpers)
|
registerMinimum(hbsInstanceNoHelpers)
|
||||||
const defaultOpts = { noHelpers: false, noEscaping: false }
|
const defaultOpts = {
|
||||||
|
noHelpers: false,
|
||||||
|
cacheTemplates: false,
|
||||||
|
noEscaping: false,
|
||||||
|
escapeNewlines: false,
|
||||||
|
noFinalise: false,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* utility function to check if the object is valid
|
* Utility function to check if the object is valid.
|
||||||
*/
|
*/
|
||||||
function testObject(object) {
|
function testObject(object) {
|
||||||
// JSON stringify will fail if there are any cycles, stops infinite recursion
|
// JSON stringify will fail if there are any cycles, stops infinite recursion
|
||||||
|
@ -23,6 +29,38 @@ function testObject(object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a HBS template function for a given string, and optionally caches it.
|
||||||
|
*/
|
||||||
|
let templateCache = {}
|
||||||
|
function createTemplate(string, opts) {
|
||||||
|
opts = { ...defaultOpts, ...opts }
|
||||||
|
|
||||||
|
// Finalising adds a helper, can't do this with no helpers
|
||||||
|
const key = `${string}-${JSON.stringify(opts)}`
|
||||||
|
|
||||||
|
// Reuse the cached template is possible
|
||||||
|
if (opts.cacheTemplates && templateCache[key]) {
|
||||||
|
return templateCache[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
string = processors.preprocess(string, opts)
|
||||||
|
|
||||||
|
// Optionally disable built in HBS escaping
|
||||||
|
if (opts.noEscaping) {
|
||||||
|
string = exports.disableEscaping(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This does not throw an error when template can't be fulfilled,
|
||||||
|
// have to try correct beforehand
|
||||||
|
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
||||||
|
const template = instance.compile(string, {
|
||||||
|
strict: false,
|
||||||
|
})
|
||||||
|
templateCache[key] = template
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an input object this will recurse through all props to try and update any handlebars statements within.
|
* Given an input object this will recurse through all props to try and update any handlebars statements within.
|
||||||
* @param {object|array} object The input structure which is to be recursed, it is important to note that
|
* @param {object|array} object The input structure which is to be recursed, it is important to note that
|
||||||
|
@ -98,30 +136,20 @@ module.exports.processObjectSync = (object, context, opts) => {
|
||||||
* @returns {string} The enriched string, all templates should have been replaced if they can be.
|
* @returns {string} The enriched string, all templates should have been replaced if they can be.
|
||||||
*/
|
*/
|
||||||
module.exports.processStringSync = (string, context, opts) => {
|
module.exports.processStringSync = (string, context, opts) => {
|
||||||
opts = { ...defaultOpts, ...opts }
|
// Take a copy of input in case of error
|
||||||
|
|
||||||
// take a copy of input in case of error
|
|
||||||
const input = string
|
const input = string
|
||||||
if (typeof string !== "string") {
|
if (typeof string !== "string") {
|
||||||
throw "Cannot process non-string types."
|
throw "Cannot process non-string types."
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
string = processors.preprocess(string, opts)
|
const template = createTemplate(string, opts)
|
||||||
// this does not throw an error when template can't be fulfilled, have to try correct beforehand
|
|
||||||
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
|
||||||
const templateString =
|
|
||||||
opts && opts.noEscaping ? exports.disableEscaping(string) : string
|
|
||||||
const template = instance.compile(templateString, {
|
|
||||||
strict: false,
|
|
||||||
})
|
|
||||||
const now = Math.floor(Date.now() / 1000) * 1000
|
const now = Math.floor(Date.now() / 1000) * 1000
|
||||||
return processors.postprocess(
|
return processors.postprocess(
|
||||||
template({
|
template({
|
||||||
now: new Date(now).toISOString(),
|
now: new Date(now).toISOString(),
|
||||||
__opts: opts,
|
__opts: opts,
|
||||||
...context,
|
...context,
|
||||||
}),
|
})
|
||||||
{ escapeNewlines: opts ? opts.escapeNewlines : false }
|
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return input
|
return input
|
||||||
|
@ -135,8 +163,7 @@ module.exports.processStringSync = (string, context, opts) => {
|
||||||
* @param string the string to have double HBS statements converted to triple.
|
* @param string the string to have double HBS statements converted to triple.
|
||||||
*/
|
*/
|
||||||
module.exports.disableEscaping = string => {
|
module.exports.disableEscaping = string => {
|
||||||
let regexp = new RegExp(FIND_DOUBLE_HBS_REGEX)
|
const matches = findDoubleHbsInstances(string)
|
||||||
const matches = string.match(regexp)
|
|
||||||
if (matches == null) {
|
if (matches == null) {
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
|
@ -162,7 +189,6 @@ module.exports.makePropSafe = property => {
|
||||||
* @returns {boolean} Whether or not the input string is valid.
|
* @returns {boolean} Whether or not the input string is valid.
|
||||||
*/
|
*/
|
||||||
module.exports.isValid = (string, opts) => {
|
module.exports.isValid = (string, opts) => {
|
||||||
opts = { ...defaultOpts, ...opts }
|
|
||||||
const validCases = [
|
const validCases = [
|
||||||
"string",
|
"string",
|
||||||
"number",
|
"number",
|
||||||
|
@ -176,10 +202,11 @@ module.exports.isValid = (string, opts) => {
|
||||||
// don't really need a real context to check if its valid
|
// don't really need a real context to check if its valid
|
||||||
const context = {}
|
const context = {}
|
||||||
try {
|
try {
|
||||||
const instance = opts.noHelpers ? hbsInstanceNoHelpers : hbsInstance
|
const template = createTemplate(string, {
|
||||||
instance.compile(processors.preprocess(string, { noFinalise: true }))(
|
...opts,
|
||||||
context
|
noFinalise: true,
|
||||||
)
|
})
|
||||||
|
template(context)
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err && err.message ? err.message : err
|
const msg = err && err.message ? err.message : err
|
||||||
|
|
|
@ -1,7 +1,25 @@
|
||||||
const ALPHA_NUMERIC_REGEX = /^[A-Za-z0-9]+$/g
|
const ALPHA_NUMERIC_REGEX = /^[A-Za-z0-9]+$/g
|
||||||
|
|
||||||
module.exports.FIND_HBS_REGEX = /{{([^{].*?)}}/g
|
module.exports.FIND_HBS_REGEX = /{{([^{].*?)}}/g
|
||||||
module.exports.FIND_DOUBLE_HBS_REGEX = /(?<!{){{[^{}]+}}(?!})/g
|
module.exports.FIND_TRIPLE_HBS_REGEX = /{{{([^{].*?)}}}/g
|
||||||
|
|
||||||
|
// originally this could be done with a single regex using look behinds
|
||||||
|
// but safari does not support this feature
|
||||||
|
// original regex: /(?<!{){{[^{}]+}}(?!})/g
|
||||||
|
module.exports.findDoubleHbsInstances = string => {
|
||||||
|
let copied = string
|
||||||
|
const doubleRegex = new RegExp(exports.FIND_HBS_REGEX)
|
||||||
|
const regex = new RegExp(exports.FIND_TRIPLE_HBS_REGEX)
|
||||||
|
const tripleMatches = copied.match(regex)
|
||||||
|
// remove triple braces
|
||||||
|
if (tripleMatches) {
|
||||||
|
tripleMatches.forEach(match => {
|
||||||
|
copied = copied.replace(match, "")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const doubleMatches = copied.match(doubleRegex)
|
||||||
|
return doubleMatches ? doubleMatches : []
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.isAlphaNumeric = char => {
|
module.exports.isAlphaNumeric = char => {
|
||||||
return char.match(ALPHA_NUMERIC_REGEX)
|
return char.match(ALPHA_NUMERIC_REGEX)
|
||||||
|
|
|
@ -295,10 +295,10 @@
|
||||||
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/handlebars-helpers@^0.11.7":
|
"@budibase/handlebars-helpers@^0.11.8":
|
||||||
version "0.11.7"
|
version "0.11.8"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.7.tgz#8e5f9843d7dd10503e9f608555a96ccf4d836c46"
|
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.8.tgz#6953d29673a8c5c407e096c0a84890465c7ce841"
|
||||||
integrity sha512-PvGHAv22cWSFExs1kc0WglwsmCEUEOqWvSp6JCFZwtc3qAAr5yMfLK8WGVQ63ALvyzWZiyxF+yrlzeeaohCMJw==
|
integrity sha512-ggWJUt0GqsHFAEup5tlWlcrmYML57nKhpNGGLzVsqXVYN8eVmf3xluYmmMe7fDYhQH0leSprrdEXmsdFQF3HAQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
array-sort "^1.0.0"
|
array-sort "^1.0.0"
|
||||||
define-property "^2.0.2"
|
define-property "^2.0.2"
|
||||||
|
@ -309,8 +309,6 @@
|
||||||
handlebars "^4.7.7"
|
handlebars "^4.7.7"
|
||||||
handlebars-utils "^1.0.6"
|
handlebars-utils "^1.0.6"
|
||||||
has-value "^2.0.2"
|
has-value "^2.0.2"
|
||||||
helper-date "^1.0.1"
|
|
||||||
helper-markdown "^1.0.0"
|
|
||||||
helper-md "^0.2.2"
|
helper-md "^0.2.2"
|
||||||
html-tag "^2.0.0"
|
html-tag "^2.0.0"
|
||||||
is-even "^1.0.0"
|
is-even "^1.0.0"
|
||||||
|
@ -698,6 +696,11 @@ acorn-walk@^7.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
||||||
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
||||||
|
|
||||||
|
acorn-walk@^8.2.0:
|
||||||
|
version "8.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||||
|
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||||
|
|
||||||
acorn@^5.7.3:
|
acorn@^5.7.3:
|
||||||
version "5.7.4"
|
version "5.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
|
||||||
|
@ -708,10 +711,10 @@ acorn@^7.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
acorn@^8.2.4:
|
acorn@^8.2.4, acorn@^8.7.0:
|
||||||
version "8.5.0"
|
version "8.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
|
||||||
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
|
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
|
||||||
|
|
||||||
agent-base@6:
|
agent-base@6:
|
||||||
version "6.0.2"
|
version "6.0.2"
|
||||||
|
@ -1371,13 +1374,6 @@ data-urls@^2.0.0:
|
||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
date.js@^0.3.1:
|
|
||||||
version "0.3.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/date.js/-/date.js-0.3.3.tgz#ef1e92332f507a638795dbb985e951882e50bbda"
|
|
||||||
integrity sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==
|
|
||||||
dependencies:
|
|
||||||
debug "~3.1.0"
|
|
||||||
|
|
||||||
dayjs@^1.10.4:
|
dayjs@^1.10.4:
|
||||||
version "1.10.7"
|
version "1.10.7"
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468"
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468"
|
||||||
|
@ -1397,13 +1393,6 @@ debug@^2.2.0, debug@^2.3.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@~3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
|
||||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
|
||||||
dependencies:
|
|
||||||
ms "2.0.0"
|
|
||||||
|
|
||||||
decamelize@^1.2.0:
|
decamelize@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
|
@ -1907,7 +1896,7 @@ gulp-header@^1.7.1:
|
||||||
lodash.template "^4.4.0"
|
lodash.template "^4.4.0"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
handlebars-utils@^1.0.2, handlebars-utils@^1.0.4, handlebars-utils@^1.0.6:
|
handlebars-utils@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/handlebars-utils/-/handlebars-utils-1.0.6.tgz#cb9db43362479054782d86ffe10f47abc76357f9"
|
resolved "https://registry.yarnpkg.com/handlebars-utils/-/handlebars-utils-1.0.6.tgz#cb9db43362479054782d86ffe10f47abc76357f9"
|
||||||
integrity sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==
|
integrity sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==
|
||||||
|
@ -2007,24 +1996,6 @@ hash.js@^1.0.0, hash.js@^1.0.3:
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
minimalistic-assert "^1.0.1"
|
minimalistic-assert "^1.0.1"
|
||||||
|
|
||||||
helper-date@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/helper-date/-/helper-date-1.0.1.tgz#12fedea3ad8e44a7ca4c4efb0ff4104a5120cffb"
|
|
||||||
integrity sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==
|
|
||||||
dependencies:
|
|
||||||
date.js "^0.3.1"
|
|
||||||
handlebars-utils "^1.0.4"
|
|
||||||
moment "^2.18.1"
|
|
||||||
|
|
||||||
helper-markdown@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/helper-markdown/-/helper-markdown-1.0.0.tgz#ee7e9fc554675007d37eb90f7853b13ce74f3e10"
|
|
||||||
integrity sha512-AnDqMS4ejkQK0MXze7pA9TM3pu01ZY+XXsES6gEE0RmCGk5/NIfvTn0NmItfyDOjRAzyo9z6X7YHbHX4PzIvOA==
|
|
||||||
dependencies:
|
|
||||||
handlebars-utils "^1.0.2"
|
|
||||||
highlight.js "^9.12.0"
|
|
||||||
remarkable "^1.7.1"
|
|
||||||
|
|
||||||
helper-md@^0.2.2:
|
helper-md@^0.2.2:
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/helper-md/-/helper-md-0.2.2.tgz#c1f59d7e55bbae23362fd8a0e971607aec69d41f"
|
resolved "https://registry.yarnpkg.com/helper-md/-/helper-md-0.2.2.tgz#c1f59d7e55bbae23362fd8a0e971607aec69d41f"
|
||||||
|
@ -2035,11 +2006,6 @@ helper-md@^0.2.2:
|
||||||
fs-exists-sync "^0.1.0"
|
fs-exists-sync "^0.1.0"
|
||||||
remarkable "^1.6.2"
|
remarkable "^1.6.2"
|
||||||
|
|
||||||
highlight.js@^9.12.0:
|
|
||||||
version "9.18.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825"
|
|
||||||
integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==
|
|
||||||
|
|
||||||
hmac-drbg@^1.0.1:
|
hmac-drbg@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||||
|
@ -3191,11 +3157,6 @@ mixin-deep@^1.2.0:
|
||||||
for-in "^1.0.2"
|
for-in "^1.0.2"
|
||||||
is-extendable "^1.0.1"
|
is-extendable "^1.0.1"
|
||||||
|
|
||||||
moment@^2.18.1:
|
|
||||||
version "2.29.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
|
||||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
@ -3668,7 +3629,7 @@ relative@^3.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isobject "^2.0.0"
|
isobject "^2.0.0"
|
||||||
|
|
||||||
remarkable@^1.6.2, remarkable@^1.7.1:
|
remarkable@^1.6.2:
|
||||||
version "1.7.4"
|
version "1.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00"
|
resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00"
|
||||||
integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==
|
integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==
|
||||||
|
@ -4404,9 +4365,12 @@ vlq@^0.2.2:
|
||||||
integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==
|
integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==
|
||||||
|
|
||||||
vm2@^3.9.4:
|
vm2@^3.9.4:
|
||||||
version "3.9.5"
|
version "3.9.6"
|
||||||
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.5.tgz#5288044860b4bbace443101fcd3bddb2a0aa2496"
|
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.6.tgz#2f9b2fd0d82802dcd872e1011869ba8ae6b74778"
|
||||||
integrity sha512-LuCAHZN75H9tdrAiLFf030oW7nJV5xwNMuk1ymOZwopmuK3d2H4L1Kv4+GFHgarKiLfXXLFU+7LDABHnwOkWng==
|
integrity sha512-BF7euUjgO+ezsz2UKex9kO9M/PtDNOf+KEpiqNepZsgf1MT7JYfJEIvG8BoYhZMLAVjqevFJ0UmXNuETe8m5dQ==
|
||||||
|
dependencies:
|
||||||
|
acorn "^8.7.0"
|
||||||
|
acorn-walk "^8.2.0"
|
||||||
|
|
||||||
w3c-hr-time@^1.0.2:
|
w3c-hr-time@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.58-alpha.4",
|
"version": "1.0.66-alpha.0",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -34,8 +34,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "^1.0.58-alpha.4",
|
"@budibase/backend-core": "^1.0.66-alpha.0",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.4",
|
"@budibase/string-templates": "^1.0.66-alpha.0",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@sentry/node": "^6.0.0",
|
"@sentry/node": "^6.0.0",
|
||||||
"@techpass/passport-openidconnect": "^0.3.0",
|
"@techpass/passport-openidconnect": "^0.3.0",
|
||||||
|
|
|
@ -27,8 +27,10 @@ describe("/api/global/email", () => {
|
||||||
userId: user._id,
|
userId: user._id,
|
||||||
})
|
})
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
// ethereal hiccup, can't test right now
|
||||||
.expect(200)
|
if (res.status >= 300) {
|
||||||
|
return
|
||||||
|
}
|
||||||
expect(res.body.message).toBeDefined()
|
expect(res.body.message).toBeDefined()
|
||||||
const testUrl = nodemailer.getTestMessageUrl(res.body)
|
const testUrl = nodemailer.getTestMessageUrl(res.body)
|
||||||
console.log(`${purpose} URL: ${testUrl}`)
|
console.log(`${purpose} URL: ${testUrl}`)
|
||||||
|
@ -37,7 +39,7 @@ describe("/api/global/email", () => {
|
||||||
text = await response.text()
|
text = await response.text()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ethereal hiccup, can't test right now
|
// ethereal hiccup, can't test right now
|
||||||
if (parseInt(err.status) >= 400) {
|
if (parseInt(err.status) >= 300) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
throw err
|
throw err
|
||||||
|
|
Loading…
Reference in a new issue