Merge branch 'master' of github.com:Budibase/budibase into develop
This commit is contained in:
commit
db45086105
|
@ -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 }}
|
||||||
|
|
|
@ -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.7",
|
"version": "1.0.65",
|
||||||
"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.7",
|
"version": "1.0.65",
|
||||||
"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.7",
|
"version": "1.0.65",
|
||||||
"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,62 +17,55 @@
|
||||||
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}
|
class:spectrum-Button--secondary={secondary}
|
||||||
class:spectrum-Button--secondary={secondary}
|
class:spectrum-Button--warning={warning}
|
||||||
class:spectrum-Button--warning={warning}
|
class:spectrum-Button--overBackground={overBackground}
|
||||||
class:spectrum-Button--overBackground={overBackground}
|
class:spectrum-Button--quiet={quiet}
|
||||||
class:spectrum-Button--quiet={quiet}
|
class:active
|
||||||
class:active
|
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
|
||||||
class="spectrum-Button spectrum-Button--size{size.toUpperCase()}"
|
{disabled}
|
||||||
{disabled}
|
on:click|preventDefault
|
||||||
on:click|preventDefault
|
on:mouseover={() => (showTooltip = true)}
|
||||||
on:mouseover={() => (showTooltip = true)}
|
on:mouseleave={() => (showTooltip = false)}
|
||||||
on:focus={() => (showTooltip = true)}
|
>
|
||||||
on:mouseleave={() => (showTooltip = false)}
|
{#if icon}
|
||||||
>
|
<svg
|
||||||
{#if icon}
|
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-label={icon}
|
||||||
|
>
|
||||||
|
<use xlink:href="#spectrum-icon-18-{icon}" />
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
{#if $$slots}
|
||||||
|
<span class="spectrum-Button-label"><slot /></span>
|
||||||
|
{/if}
|
||||||
|
{#if !disabled && tooltip}
|
||||||
|
<div class="tooltip-icon">
|
||||||
<svg
|
<svg
|
||||||
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
||||||
focusable="false"
|
focusable="false"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
aria-label={icon}
|
aria-label="Info"
|
||||||
>
|
>
|
||||||
<use xlink:href="#spectrum-icon-18-{icon}" />
|
<use xlink:href="#spectrum-icon-18-InfoOutline" />
|
||||||
</svg>
|
</svg>
|
||||||
{/if}
|
|
||||||
{#if $$slots}
|
|
||||||
<span class="spectrum-Button-label"><slot /></span>
|
|
||||||
{/if}
|
|
||||||
{#if !disabled && tooltip}
|
|
||||||
<div class="tooltip-icon">
|
|
||||||
<svg
|
|
||||||
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
|
||||||
focusable="false"
|
|
||||||
aria-hidden="true"
|
|
||||||
aria-label="Info"
|
|
||||||
>
|
|
||||||
<use xlink:href="#spectrum-icon-18-InfoOutline" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
{#if showTooltip && tooltip}
|
|
||||||
<div class="position">
|
|
||||||
<div class="tooltip">
|
|
||||||
<Tooltip textWrapping={true} direction={"bottom"} text={tooltip} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
{#if showTooltip && tooltip}
|
||||||
|
<div class="tooltip">
|
||||||
|
<Tooltip textWrapping={true} direction={"bottom"} text={tooltip} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</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;
|
||||||
|
@ -90,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;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "1.0.58-alpha.7",
|
"version": "1.0.65",
|
||||||
"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.7",
|
"@budibase/bbui": "^1.0.65",
|
||||||
"@budibase/client": "^1.0.58-alpha.7",
|
"@budibase/client": "^1.0.65",
|
||||||
"@budibase/frontend-core": "^1.0.58-alpha.7",
|
"@budibase/frontend-core": "^1.0.65",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.7",
|
"@budibase/string-templates": "^1.0.65",
|
||||||
"@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 {
|
||||||
|
|
|
@ -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,13 +126,15 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
{#if selectedActionComponent}
|
{#if selectedActionComponent}
|
||||||
<div class="selected-action-container">
|
{#key selectedAction.id}
|
||||||
<svelte:component
|
<div class="selected-action-container">
|
||||||
this={selectedActionComponent}
|
<svelte:component
|
||||||
parameters={selectedAction.parameters}
|
this={selectedActionComponent}
|
||||||
bindings={allBindings}
|
parameters={selectedAction.parameters}
|
||||||
/>
|
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 || {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,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]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "1.0.58-alpha.7",
|
"version": "1.0.65",
|
||||||
"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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "1.0.58-alpha.7",
|
"version": "1.0.65",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -19,9 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.58-alpha.7",
|
"@budibase/bbui": "^1.0.65",
|
||||||
"@budibase/frontend-core": "^1.0.58-alpha.7",
|
"@budibase/frontend-core": "^1.0.65",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.7",
|
"@budibase/string-templates": "^1.0.65",
|
||||||
"@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",
|
||||||
|
|
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.7",
|
"version": "1.0.65",
|
||||||
"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.7",
|
"@budibase/bbui": "^1.0.65",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.58-alpha.7",
|
"version": "1.0.65",
|
||||||
"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.7",
|
"@budibase/backend-core": "^1.0.65",
|
||||||
"@budibase/client": "^1.0.58-alpha.7",
|
"@budibase/client": "^1.0.65",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.7",
|
"@budibase/string-templates": "^1.0.65",
|
||||||
"@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") {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "1.0.58-alpha.7",
|
"version": "1.0.65",
|
||||||
"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",
|
||||||
|
|
|
@ -3,7 +3,7 @@ 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)
|
||||||
|
@ -163,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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.58-alpha.7",
|
"version": "1.0.65",
|
||||||
"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.7",
|
"@budibase/backend-core": "^1.0.65",
|
||||||
"@budibase/string-templates": "^1.0.58-alpha.7",
|
"@budibase/string-templates": "^1.0.65",
|
||||||
"@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 New Issue