diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml
index 4b241c5165..14cc110214 100644
--- a/.github/workflows/budibase_ci.yml
+++ b/.github/workflows/budibase_ci.yml
@@ -204,7 +204,7 @@ jobs:
check-pro-submodule:
runs-on: ubuntu-latest
- if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase'
+ if: inputs.run_as_oss != true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase')
steps:
- name: Checkout repo and submodules
uses: actions/checkout@v3
@@ -254,7 +254,7 @@ jobs:
check-accountportal-submodule:
runs-on: ubuntu-latest
- if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase'
+ if: inputs.run_as_oss != true && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase')
steps:
- name: Checkout repo and submodules
uses: actions/checkout@v3
diff --git a/.github/workflows/close-featurebranch.yml b/.github/workflows/close-featurebranch.yml
index 46cb781730..5da3eb52cd 100644
--- a/.github/workflows/close-featurebranch.yml
+++ b/.github/workflows/close-featurebranch.yml
@@ -2,9 +2,7 @@ name: close-featurebranch
on:
pull_request:
- types: [closed]
- branches:
- - master
+ types: [closed, unlabeled]
workflow_dispatch:
inputs:
BRANCH:
@@ -14,6 +12,9 @@ on:
jobs:
release:
+ if: |
+ (github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'feature-branch')) ||
+ github.event.label.name == 'feature-branch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
diff --git a/.github/workflows/deploy-featurebranch.yml b/.github/workflows/deploy-featurebranch.yml
index c70f2fff20..a5636fe912 100644
--- a/.github/workflows/deploy-featurebranch.yml
+++ b/.github/workflows/deploy-featurebranch.yml
@@ -2,12 +2,19 @@ name: deploy-featurebranch
on:
pull_request:
- branches:
- - master
+ types: [
+ labeled,
+ # default types below (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request)
+ opened,
+ synchronize,
+ reopened,
+ ]
jobs:
release:
- if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase'
+ if: |
+ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase') &&
+ contains(github.event.pull_request.labels.*.name, 'feature-branch')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
diff --git a/.vscode/settings.json b/.vscode/settings.json
index ece537efac..e22d5a8866 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,7 +1,7 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
- "source.fixAll": true
+ "source.fixAll": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[json]": {
diff --git a/charts/budibase/README.md b/charts/budibase/README.md
index d8191026ce..342011bdb1 100644
--- a/charts/budibase/README.md
+++ b/charts/budibase/README.md
@@ -157,6 +157,17 @@ $ helm install --create-namespace --namespace budibase budibase . -f values.yaml
| services.apps.replicaCount | int | `1` | The number of apps replicas to run. |
| services.apps.resources | object | `{}` | The resources to use for apps pods. See for more information on how to set these. |
| services.apps.startupProbe | object | HTTP health checks. | Startup probe configuration for apps pods. You shouldn't need to change this, but if you want to you can find more information here: |
+| services.automationWorkers.autoscaling.enabled | bool | `false` | Whether to enable horizontal pod autoscaling for the apps service. |
+| services.automationWorkers.autoscaling.maxReplicas | int | `10` | |
+| services.automationWorkers.autoscaling.minReplicas | int | `1` | |
+| services.automationWorkers.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for the automation worker service. Note that for autoscaling to work, you will need to have metrics-server configured, and resources set for the automation worker pods. |
+| services.automationWorkers.enabled | bool | `true` | Whether or not to enable the automation worker service. If you disable this, automations will be processed by the apps service. |
+| services.automationWorkers.livenessProbe | object | HTTP health checks. | Liveness probe configuration for automation worker pods. You shouldn't need to change this, but if you want to you can find more information here: |
+| services.automationWorkers.logLevel | string | `"info"` | The log level for the automation worker service. |
+| services.automationWorkers.readinessProbe | object | HTTP health checks. | Readiness probe configuration for automation worker pods. You shouldn't need to change this, but if you want to you can find more information here: |
+| services.automationWorkers.replicaCount | int | `1` | The number of automation worker replicas to run. |
+| services.automationWorkers.resources | object | `{}` | The resources to use for automation worker pods. See for more information on how to set these. |
+| services.automationWorkers.startupProbe | object | HTTP health checks. | Startup probe configuration for automation worker pods. You shouldn't need to change this, but if you want to you can find more information here: |
| services.couchdb.backup.enabled | bool | `false` | Whether or not to enable periodic CouchDB backups. This works by replicating to another CouchDB instance. |
| services.couchdb.backup.interval | string | `""` | Backup interval in seconds |
| services.couchdb.backup.resources | object | `{}` | The resources to use for CouchDB backup pods. See for more information on how to set these. |
diff --git a/charts/budibase/templates/app-service-deployment.yaml b/charts/budibase/templates/app-service-deployment.yaml
index 7358e474ca..9fb435c2a3 100644
--- a/charts/budibase/templates/app-service-deployment.yaml
+++ b/charts/budibase/templates/app-service-deployment.yaml
@@ -192,7 +192,14 @@ spec:
- name: NODE_TLS_REJECT_UNAUTHORIZED
value: {{ .Values.services.tlsRejectUnauthorized }}
{{ end }}
-
+ {{- if .Values.services.automationWorkers.enabled }}
+ - name: APP_FEATURES
+ value: "api"
+ {{- end }}
+ {{- range .Values.services.apps.extraEnv }}
+ - name: {{ .name }}
+ value: {{ .value | quote }}
+ {{- end }}
image: budibase/apps:{{ .Values.globals.appVersion | default .Chart.AppVersion }}
imagePullPolicy: Always
{{- if .Values.services.apps.startupProbe }}
diff --git a/charts/budibase/templates/automation-worker-service-deployment.yaml b/charts/budibase/templates/automation-worker-service-deployment.yaml
new file mode 100644
index 0000000000..46be6a4435
--- /dev/null
+++ b/charts/budibase/templates/automation-worker-service-deployment.yaml
@@ -0,0 +1,248 @@
+{{- if .Values.services.automationWorkers.enabled }}
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ annotations:
+{{ if .Values.services.automationWorkers.deploymentAnnotations }}
+{{- toYaml .Values.services.automationWorkers.deploymentAnnotations | indent 4 -}}
+{{ end }}
+ labels:
+ io.kompose.service: automation-worker-service
+{{ if .Values.services.automationWorkers.deploymentLabels }}
+{{- toYaml .Values.services.automationWorkers.deploymentLabels | indent 4 -}}
+{{ end }}
+ name: automation-worker-service
+spec:
+ replicas: {{ .Values.services.automationWorkers.replicaCount }}
+ selector:
+ matchLabels:
+ io.kompose.service: automation-worker-service
+ strategy:
+ type: RollingUpdate
+ template:
+ metadata:
+ annotations:
+{{ if .Values.services.automationWorkers.templateAnnotations }}
+{{- toYaml .Values.services.automationWorkers.templateAnnotations | indent 8 -}}
+{{ end }}
+ labels:
+ io.kompose.service: automation-worker-service
+{{ if .Values.services.automationWorkers.templateLabels }}
+{{- toYaml .Values.services.automationWorkers.templateLabels | indent 8 -}}
+{{ end }}
+ spec:
+ containers:
+ - env:
+ - name: BUDIBASE_ENVIRONMENT
+ value: {{ .Values.globals.budibaseEnv }}
+ - name: DEPLOYMENT_ENVIRONMENT
+ value: "kubernetes"
+ - name: COUCH_DB_URL
+ {{ if .Values.services.couchdb.url }}
+ value: {{ .Values.services.couchdb.url }}
+ {{ else }}
+ value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }}
+ {{ end }}
+ {{ if .Values.services.couchdb.enabled }}
+ - name: COUCH_DB_USER
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "couchdb.fullname" . }}
+ key: adminUsername
+ - name: COUCH_DB_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "couchdb.fullname" . }}
+ key: adminPassword
+ {{ end }}
+ - name: ENABLE_ANALYTICS
+ value: {{ .Values.globals.enableAnalytics | quote }}
+ - name: API_ENCRYPTION_KEY
+ value: {{ .Values.globals.apiEncryptionKey | quote }}
+ - name: HTTP_LOGGING
+ value: {{ .Values.services.automationWorkers.httpLogging | quote }}
+ - name: INTERNAL_API_KEY
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "budibase.fullname" . }}
+ key: internalApiKey
+ - name: INTERNAL_API_KEY_FALLBACK
+ value: {{ .Values.globals.internalApiKeyFallback | quote }}
+ - name: JWT_SECRET
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "budibase.fullname" . }}
+ key: jwtSecret
+ - name: JWT_SECRET_FALLBACK
+ value: {{ .Values.globals.jwtSecretFallback | quote }}
+ {{ if .Values.services.objectStore.region }}
+ - name: AWS_REGION
+ value: {{ .Values.services.objectStore.region }}
+ {{ end }}
+ - name: MINIO_ENABLED
+ value: {{ .Values.services.objectStore.minio | quote }}
+ - name: MINIO_ACCESS_KEY
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "budibase.fullname" . }}
+ key: objectStoreAccess
+ - name: MINIO_SECRET_KEY
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "budibase.fullname" . }}
+ key: objectStoreSecret
+ - name: CLOUDFRONT_CDN
+ value: {{ .Values.services.objectStore.cloudfront.cdn | quote }}
+ - name: CLOUDFRONT_PUBLIC_KEY_ID
+ value: {{ .Values.services.objectStore.cloudfront.publicKeyId | quote }}
+ - name: CLOUDFRONT_PRIVATE_KEY_64
+ value: {{ .Values.services.objectStore.cloudfront.privateKey64 | quote }}
+ - name: MINIO_URL
+ value: {{ .Values.services.objectStore.url }}
+ - name: PLUGIN_BUCKET_NAME
+ value: {{ .Values.services.objectStore.pluginBucketName | quote }}
+ - name: APPS_BUCKET_NAME
+ value: {{ .Values.services.objectStore.appsBucketName | quote }}
+ - name: GLOBAL_BUCKET_NAME
+ value: {{ .Values.services.objectStore.globalBucketName | quote }}
+ - name: BACKUPS_BUCKET_NAME
+ value: {{ .Values.services.objectStore.backupsBucketName | quote }}
+ - name: PORT
+ value: {{ .Values.services.automationWorkers.port | quote }}
+ {{ if .Values.services.worker.publicApiRateLimitPerSecond }}
+ - name: API_REQ_LIMIT_PER_SEC
+ value: {{ .Values.globals.automationWorkers.publicApiRateLimitPerSecond | quote }}
+ {{ end }}
+ - name: MULTI_TENANCY
+ value: {{ .Values.globals.multiTenancy | quote }}
+ - name: OFFLINE_MODE
+ value: {{ .Values.globals.offlineMode | quote }}
+ - name: LOG_LEVEL
+ value: {{ .Values.services.automationWorkers.logLevel | quote }}
+ - name: REDIS_PASSWORD
+ value: {{ .Values.services.redis.password }}
+ - name: REDIS_URL
+ {{ if .Values.services.redis.url }}
+ value: {{ .Values.services.redis.url }}
+ {{ else }}
+ value: redis-service:{{ .Values.services.redis.port }}
+ {{ end }}
+ - name: SELF_HOSTED
+ value: {{ .Values.globals.selfHosted | quote }}
+ - name: POSTHOG_TOKEN
+ value: {{ .Values.globals.posthogToken | quote }}
+ - name: WORKER_URL
+ value: http://worker-service:{{ .Values.services.worker.port }}
+ - name: PLATFORM_URL
+ value: {{ .Values.globals.platformUrl | quote }}
+ - name: ACCOUNT_PORTAL_URL
+ value: {{ .Values.globals.accountPortalUrl | quote }}
+ - name: ACCOUNT_PORTAL_API_KEY
+ value: {{ .Values.globals.accountPortalApiKey | quote }}
+ - name: COOKIE_DOMAIN
+ value: {{ .Values.globals.cookieDomain | quote }}
+ - name: HTTP_MIGRATIONS
+ value: {{ .Values.globals.httpMigrations | quote }}
+ - name: GOOGLE_CLIENT_ID
+ value: {{ .Values.globals.google.clientId | quote }}
+ - name: GOOGLE_CLIENT_SECRET
+ value: {{ .Values.globals.google.secret | quote }}
+ - name: AUTOMATION_MAX_ITERATIONS
+ value: {{ .Values.globals.automationMaxIterations | quote }}
+ - name: TENANT_FEATURE_FLAGS
+ value: {{ .Values.globals.tenantFeatureFlags | quote }}
+ - name: ENCRYPTION_KEY
+ value: {{ .Values.globals.bbEncryptionKey | quote }}
+ {{ if .Values.globals.bbAdminUserEmail }}
+ - name: BB_ADMIN_USER_EMAIL
+ value: {{ .Values.globals.bbAdminUserEmail | quote }}
+ {{ end }}
+ {{ if .Values.globals.bbAdminUserPassword }}
+ - name: BB_ADMIN_USER_PASSWORD
+ value: {{ .Values.globals.bbAdminUserPassword | quote }}
+ {{ end }}
+ {{ if .Values.globals.pluginsDir }}
+ - name: PLUGINS_DIR
+ value: {{ .Values.globals.pluginsDir | quote }}
+ {{ end }}
+ {{ if .Values.services.automationWorkers.nodeDebug }}
+ - name: NODE_DEBUG
+ value: {{ .Values.services.automationWorkers.nodeDebug | quote }}
+ {{ end }}
+ {{ if .Values.globals.datadogApmEnabled }}
+ - name: DD_LOGS_INJECTION
+ value: {{ .Values.globals.datadogApmEnabled | quote }}
+ - name: DD_APM_ENABLED
+ value: {{ .Values.globals.datadogApmEnabled | quote }}
+ - name: DD_APM_DD_URL
+ value: https://trace.agent.datadoghq.eu
+ {{ end }}
+ {{ if .Values.globals.globalAgentHttpProxy }}
+ - name: GLOBAL_AGENT_HTTP_PROXY
+ value: {{ .Values.globals.globalAgentHttpProxy | quote }}
+ {{ end }}
+ {{ if .Values.globals.globalAgentHttpsProxy }}
+ - name: GLOBAL_AGENT_HTTPS_PROXY
+ value: {{ .Values.globals.globalAgentHttpsProxy | quote }}
+ {{ end }}
+ {{ if .Values.globals.globalAgentNoProxy }}
+ - name: GLOBAL_AGENT_NO_PROXY
+ value: {{ .Values.globals.globalAgentNoProxy | quote }}
+ {{ end }}
+ {{ if .Values.services.tlsRejectUnauthorized }}
+ - name: NODE_TLS_REJECT_UNAUTHORIZED
+ value: {{ .Values.services.tlsRejectUnauthorized }}
+ {{ end }}
+ - name: APP_FEATURES
+ value: "automations"
+ {{- range .Values.services.automationWorkers.extraEnv }}
+ - name: {{ .name }}
+ value: {{ .value | quote }}
+ {{- end }}
+
+ image: budibase/apps:{{ .Values.globals.appVersion | default .Chart.AppVersion }}
+ imagePullPolicy: Always
+ {{- if .Values.services.automationWorkers.startupProbe }}
+ {{- with .Values.services.automationWorkers.startupProbe }}
+ startupProbe:
+ {{- toYaml . | nindent 10 }}
+ {{- end }}
+ {{- end }}
+ {{- if .Values.services.automationWorkers.livenessProbe }}
+ {{- with .Values.services.automationWorkers.livenessProbe }}
+ livenessProbe:
+ {{- toYaml . | nindent 10 }}
+ {{- end }}
+ {{- end }}
+ {{- if .Values.services.automationWorkers.readinessProbe }}
+ {{- with .Values.services.automationWorkers.readinessProbe }}
+ readinessProbe:
+ {{- toYaml . | nindent 10 }}
+ {{- end }}
+ {{- end }}
+ name: bbautomationworker
+ ports:
+ - containerPort: {{ .Values.services.automationWorkers.port }}
+ {{ with .Values.services.automationWorkers.resources }}
+ resources:
+ {{- toYaml . | nindent 10 }}
+ {{ end }}
+ {{- with .Values.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{ if .Values.schedulerName }}
+ schedulerName: {{ .Values.schedulerName | quote }}
+ {{ end }}
+ {{ if .Values.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml .Values.imagePullSecrets | nindent 6 }}
+ {{ end }}
+ restartPolicy: Always
+ serviceAccountName: ""
+status: {}
+{{- end }}
\ No newline at end of file
diff --git a/charts/budibase/templates/automation-worker-service-hpa.yaml b/charts/budibase/templates/automation-worker-service-hpa.yaml
new file mode 100644
index 0000000000..f29223b61b
--- /dev/null
+++ b/charts/budibase/templates/automation-worker-service-hpa.yaml
@@ -0,0 +1,32 @@
+{{- if .Values.services.automationWorkers.autoscaling.enabled }}
+apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }}
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "budibase.fullname" . }}-apps
+ labels:
+ {{- include "budibase.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: automation-worker-service
+ minReplicas: {{ .Values.services.automationWorkers.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.services.automationWorkers.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.services.automationWorkers.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.services.automationWorkers.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.services.automationWorkers.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ target:
+ type: Utilization
+ averageUtilization: {{ .Values.services.automationWorkers.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/charts/budibase/templates/worker-service-deployment.yaml b/charts/budibase/templates/worker-service-deployment.yaml
index 6427aa70e8..1d90aaf954 100644
--- a/charts/budibase/templates/worker-service-deployment.yaml
+++ b/charts/budibase/templates/worker-service-deployment.yaml
@@ -182,6 +182,10 @@ spec:
- name: NODE_TLS_REJECT_UNAUTHORIZED
value: {{ .Values.services.tlsRejectUnauthorized }}
{{ end }}
+ {{- range .Values.services.worker.extraEnv }}
+ - name: {{ .name }}
+ value: {{ .value | quote }}
+ {{- end }}
image: budibase/worker:{{ .Values.globals.appVersion | default .Chart.AppVersion }}
imagePullPolicy: Always
{{- if .Values.services.worker.startupProbe }}
diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml
index 13054e75fc..09262df463 100644
--- a/charts/budibase/values.yaml
+++ b/charts/budibase/values.yaml
@@ -220,6 +220,9 @@ services:
#
# for more information on how to set these.
resources: {}
+ # -- Extra environment variables to set for apps pods. Takes a list of
+ # name=value pairs.
+ extraEnv: []
# -- Startup probe configuration for apps pods. You shouldn't need to
# change this, but if you want to you can find more information here:
#
@@ -272,6 +275,78 @@ services:
# and resources set for the apps pods.
targetCPUUtilizationPercentage: 80
+ automationWorkers:
+ # -- Whether or not to enable the automation worker service. If you disable this,
+ # automations will be processed by the apps service.
+ enabled: true
+ # @ignore (you shouldn't need to change this)
+ port: 4002
+ # -- The number of automation worker replicas to run.
+ replicaCount: 1
+ # -- The log level for the automation worker service.
+ logLevel: info
+ # -- The resources to use for automation worker pods. See
+ #
+ # for more information on how to set these.
+ resources: {}
+ # -- Extra environment variables to set for automation worker pods. Takes a list of
+ # name=value pairs.
+ extraEnv: []
+ # -- Startup probe configuration for automation worker pods. You shouldn't
+ # need to change this, but if you want to you can find more information
+ # here:
+ #
+ # @default -- HTTP health checks.
+ startupProbe:
+ # @ignore
+ httpGet:
+ path: /health
+ port: 4002
+ scheme: HTTP
+ # @ignore
+ failureThreshold: 30
+ # @ignore
+ periodSeconds: 3
+ # -- Readiness probe configuration for automation worker pods. You shouldn't
+ # need to change this, but if you want to you can find more information
+ # here:
+ #
+ # @default -- HTTP health checks.
+ readinessProbe:
+ # @ignore
+ httpGet:
+ path: /health
+ port: 4002
+ scheme: HTTP
+ # @ignore
+ periodSeconds: 3
+ # @ignore
+ failureThreshold: 1
+ # -- Liveness probe configuration for automation worker pods. You shouldn't
+ # need to change this, but if you want to you can find more information
+ # here:
+ #
+ # @default -- HTTP health checks.
+ livenessProbe:
+ # @ignore
+ httpGet:
+ path: /health
+ port: 4002
+ scheme: HTTP
+ # @ignore
+ failureThreshold: 3
+ # @ignore
+ periodSeconds: 30
+ autoscaling:
+ # -- Whether to enable horizontal pod autoscaling for the apps service.
+ enabled: false
+ minReplicas: 1
+ maxReplicas: 10
+ # -- Target CPU utilization percentage for the automation worker service.
+ # Note that for autoscaling to work, you will need to have metrics-server
+ # configured, and resources set for the automation worker pods.
+ targetCPUUtilizationPercentage: 80
+
worker:
# @ignore (you shouldn't need to change this)
port: 4003
@@ -285,6 +360,9 @@ services:
#
# for more information on how to set these.
resources: {}
+ # -- Extra environment variables to set for worker pods. Takes a list of
+ # name=value pairs.
+ extraEnv: []
# -- Startup probe configuration for worker pods. You shouldn't need to
# change this, but if you want to you can find more information here:
#
diff --git a/hosting/proxy/nginx.prod.conf b/hosting/proxy/nginx.prod.conf
index 88f9645f80..65cc3ff390 100644
--- a/hosting/proxy/nginx.prod.conf
+++ b/hosting/proxy/nginx.prod.conf
@@ -257,6 +257,7 @@ http {
access_log off;
allow 127.0.0.1;
+ allow 10.0.0.0/8;
deny all;
location /nginx_status {
diff --git a/lerna.json b/lerna.json
index 3d309f0881..6a7fb47923 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "2.13.38",
+ "version": "2.13.45",
"npmClient": "yarn",
"packages": [
"packages/*",
diff --git a/packages/account-portal b/packages/account-portal
index a0b13270c3..a1832145f0 160000
--- a/packages/account-portal
+++ b/packages/account-portal
@@ -1 +1 @@
-Subproject commit a0b13270c36dd188e2a953d026b4560a1208008e
+Subproject commit a1832145f08fca9b923cc145930d5d3a4c2f1d1f
diff --git a/packages/backend-core/src/cache/writethrough.ts b/packages/backend-core/src/cache/writethrough.ts
index 24e519dc7f..5cafe418d7 100644
--- a/packages/backend-core/src/cache/writethrough.ts
+++ b/packages/backend-core/src/cache/writethrough.ts
@@ -56,11 +56,8 @@ async function put(
const writeDb = async (toWrite: any) => {
// doc should contain the _id and _rev
const response = await db.put(toWrite, { force: true })
- output = {
- ...doc,
- _id: response.id,
- _rev: response.rev,
- }
+ output._id = response.id
+ output._rev = response.rev
}
try {
await writeDb(doc)
diff --git a/packages/backend-core/src/constants/misc.ts b/packages/backend-core/src/constants/misc.ts
index 8ef34196ed..aee099e10a 100644
--- a/packages/backend-core/src/constants/misc.ts
+++ b/packages/backend-core/src/constants/misc.ts
@@ -11,24 +11,7 @@ export enum Cookie {
OIDC_CONFIG = "budibase:oidc:config",
}
-export enum Header {
- API_KEY = "x-budibase-api-key",
- LICENSE_KEY = "x-budibase-license-key",
- API_VER = "x-budibase-api-version",
- APP_ID = "x-budibase-app-id",
- SESSION_ID = "x-budibase-session-id",
- TYPE = "x-budibase-type",
- PREVIEW_ROLE = "x-budibase-role",
- TENANT_ID = "x-budibase-tenant-id",
- VERIFICATION_CODE = "x-budibase-verification-code",
- RETURN_VERIFICATION_CODE = "x-budibase-return-verification-code",
- RESET_PASSWORD_CODE = "x-budibase-reset-password-code",
- RETURN_RESET_PASSWORD_CODE = "x-budibase-return-reset-password-code",
- TOKEN = "x-budibase-token",
- CSRF_TOKEN = "x-csrf-token",
- CORRELATION_ID = "x-budibase-correlation-id",
- AUTHORIZATION = "authorization",
-}
+export { Header } from "@budibase/shared-core"
export enum GlobalRole {
OWNER = "owner",
diff --git a/packages/backend-core/src/queue/inMemoryQueue.ts b/packages/backend-core/src/queue/inMemoryQueue.ts
index a8add7ecb6..ac7cdf550b 100644
--- a/packages/backend-core/src/queue/inMemoryQueue.ts
+++ b/packages/backend-core/src/queue/inMemoryQueue.ts
@@ -68,6 +68,10 @@ class InMemoryQueue {
})
}
+ async isReady() {
+ return true
+ }
+
// simply puts a message to the queue and emits to the queue for processing
/**
* Simple function to replicate the add message functionality of Bull, putting
diff --git a/packages/backend-core/src/utils/utils.ts b/packages/backend-core/src/utils/utils.ts
index ee1ef6da0c..0554737518 100644
--- a/packages/backend-core/src/utils/utils.ts
+++ b/packages/backend-core/src/utils/utils.ts
@@ -96,7 +96,7 @@ export async function getAppIdFromCtx(ctx: Ctx) {
}
// look in the path
- const pathId = parseAppIdFromUrl(ctx.path)
+ const pathId = parseAppIdFromUrlPath(ctx.path)
if (!appId && pathId) {
appId = confirmAppId(pathId)
}
@@ -116,18 +116,21 @@ export async function getAppIdFromCtx(ctx: Ctx) {
// referer header is present from a builder redirect
const referer = ctx.request.headers.referer
if (!appId && referer?.includes(BUILDER_APP_PREFIX)) {
- const refererId = parseAppIdFromUrl(ctx.request.headers.referer)
+ const refererId = parseAppIdFromUrlPath(ctx.request.headers.referer)
appId = confirmAppId(refererId)
}
return appId
}
-function parseAppIdFromUrl(url?: string) {
+function parseAppIdFromUrlPath(url?: string) {
if (!url) {
return
}
- return url.split("/").find(subPath => subPath.startsWith(APP_PREFIX))
+ return url
+ .split("?")[0] // Remove any possible query string
+ .split("/")
+ .find(subPath => subPath.startsWith(APP_PREFIX))
}
/**
diff --git a/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte b/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte
index a5b0b19c9b..4b68c285f8 100644
--- a/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte
+++ b/packages/builder/src/pages/builder/portal/account/auditLogs/index.svelte
@@ -257,7 +257,7 @@
{
diff --git a/packages/builder/src/pages/builder/portal/account/usage.svelte b/packages/builder/src/pages/builder/portal/account/usage.svelte
index cbe84973ef..9e7c402e91 100644
--- a/packages/builder/src/pages/builder/portal/account/usage.svelte
+++ b/packages/builder/src/pages/builder/portal/account/usage.svelte
@@ -15,6 +15,7 @@
import { DashCard, Usage } from "components/usage"
import { PlanModel } from "constants"
import { sdk } from "@budibase/shared-core"
+ import { PlanType } from "@budibase/types"
let staticUsage = []
let monthlyUsage = []
@@ -106,7 +107,14 @@
}
const planTitle = () => {
- return `${capitalise(license?.plan.type)} Plan`
+ const planType = license?.plan.type
+ let planName = license?.plan.type
+ if (planType === PlanType.PREMIUM_PLUS) {
+ planName = "Premium"
+ } else if (planType === PlanType.ENTERPRISE_BASIC) {
+ planName = "Enterprise"
+ }
+ return `${capitalise(planName)} Plan`
}
const getDaysRemaining = timestamp => {
diff --git a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte
index 36cf5c13a8..8c7cad7eeb 100644
--- a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte
@@ -283,7 +283,7 @@
{#if !$licensing.enforceableSSO}
- Enterprise
+ Enterprise plan
{/if}
diff --git a/packages/builder/src/pages/builder/portal/settings/environment/index.svelte b/packages/builder/src/pages/builder/portal/settings/environment/index.svelte
index cff578febd..0d4c7e1479 100644
--- a/packages/builder/src/pages/builder/portal/settings/environment/index.svelte
+++ b/packages/builder/src/pages/builder/portal/settings/environment/index.svelte
@@ -59,7 +59,7 @@
{
diff --git a/packages/frontend-core/src/api/index.js b/packages/frontend-core/src/api/index.js
index aefc3522a7..d4b4f3636e 100644
--- a/packages/frontend-core/src/api/index.js
+++ b/packages/frontend-core/src/api/index.js
@@ -1,4 +1,5 @@
import { Helpers } from "@budibase/bbui"
+import { Header } from "@budibase/shared-core"
import { ApiVersion } from "../constants"
import { buildAnalyticsEndpoints } from "./analytics"
import { buildAppEndpoints } from "./app"
@@ -62,6 +63,11 @@ const defaultAPIClientConfig = {
* invoked before the actual JS error is thrown up the stack.
*/
onError: null,
+
+ /**
+ * A function can be passed to be called when an API call returns info about a migration running for a specific app
+ */
+ onMigrationDetected: null,
}
/**
@@ -133,9 +139,9 @@ export const createAPIClient = config => {
// Build headers
let headers = { Accept: "application/json" }
- headers["x-budibase-session-id"] = APISessionID
+ headers[Header.SESSION_ID] = APISessionID
if (!external) {
- headers["x-budibase-api-version"] = ApiVersion
+ headers[Header.API_VER] = ApiVersion
}
if (json) {
headers["Content-Type"] = "application/json"
@@ -170,6 +176,7 @@ export const createAPIClient = config => {
// Handle response
if (response.status >= 200 && response.status < 400) {
+ handleMigrations(response)
try {
if (parseResponse) {
return await parseResponse(response)
@@ -186,7 +193,18 @@ export const createAPIClient = config => {
}
}
- // Performs an API call to the server and caches the response.
+ const handleMigrations = response => {
+ if (!config.onMigrationDetected) {
+ return
+ }
+ const migration = response.headers.get(Header.MIGRATING_APP)
+
+ if (migration) {
+ config.onMigrationDetected(migration)
+ }
+ }
+
+ // Performs an API call to the server and caches the response.
// Future invocation for this URL will return the cached result instead of
// hitting the server again.
const makeCachedApiCall = async params => {
@@ -242,7 +260,7 @@ export const createAPIClient = config => {
getAppID: () => {
let headers = {}
config?.attachHeaders(headers)
- return headers?.["x-budibase-app-id"]
+ return headers?.[Header.APP_ID]
},
}
diff --git a/packages/server/Dockerfile b/packages/server/Dockerfile
index f737570fcd..2af37ae288 100644
--- a/packages/server/Dockerfile
+++ b/packages/server/Dockerfile
@@ -68,9 +68,13 @@ COPY packages/server/builder/ builder/
COPY packages/server/client/ client/
ARG BUDIBASE_VERSION
+ARG GIT_COMMIT_SHA
# Ensuring the version argument is sent
RUN test -n "$BUDIBASE_VERSION"
ENV BUDIBASE_VERSION=$BUDIBASE_VERSION
+ENV DD_GIT_REPOSITORY_URL=https://github.com/budibase/budibase
+ENV DD_GIT_COMMIT_SHA=$GIT_COMMIT_SHA
+ENV DD_VERSION=$BUDIBASE_VERSION
EXPOSE 4001
diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts
index bb4f447f79..70298c7172 100644
--- a/packages/server/src/api/controllers/application.ts
+++ b/packages/server/src/api/controllers/application.ts
@@ -340,7 +340,7 @@ async function performAppCreate(ctx: UserCtx) {
// Initialise the app migration version as the latest one
await appMigrations.updateAppMigrationMetadata({
appId,
- version: appMigrations.latestMigration,
+ version: appMigrations.getLatestMigrationId(),
})
await cache.app.invalidateAppMetadata(appId, newApplication)
diff --git a/packages/server/src/api/controllers/migrations.ts b/packages/server/src/api/controllers/migrations.ts
index 8f1bfa22db..c8f786578d 100644
--- a/packages/server/src/api/controllers/migrations.ts
+++ b/packages/server/src/api/controllers/migrations.ts
@@ -1,14 +1,34 @@
+import { context } from "@budibase/backend-core"
import { migrate as migrationImpl, MIGRATIONS } from "../../migrations"
-import { BBContext } from "@budibase/types"
+import { Ctx } from "@budibase/types"
+import {
+ getAppMigrationVersion,
+ getLatestMigrationId,
+} from "../../appMigrations"
-export async function migrate(ctx: BBContext) {
+export async function migrate(ctx: Ctx) {
const options = ctx.request.body
// don't await as can take a while, just return
migrationImpl(options)
ctx.status = 200
}
-export async function fetchDefinitions(ctx: BBContext) {
+export async function fetchDefinitions(ctx: Ctx) {
ctx.body = MIGRATIONS
ctx.status = 200
}
+
+export async function getMigrationStatus(ctx: Ctx) {
+ const appId = context.getAppId()
+
+ if (!appId) {
+ ctx.throw("AppId could not be found")
+ }
+
+ const latestAppliedMigration = await getAppMigrationVersion(appId)
+
+ const migrated = latestAppliedMigration === getLatestMigrationId()
+
+ ctx.body = { migrated }
+ ctx.status = 200
+}
diff --git a/packages/server/src/api/index.ts b/packages/server/src/api/index.ts
index 56c61ce85a..ad3d8307da 100644
--- a/packages/server/src/api/index.ts
+++ b/packages/server/src/api/index.ts
@@ -4,15 +4,24 @@ import currentApp from "../middleware/currentapp"
import zlib from "zlib"
import { mainRoutes, staticRoutes, publicRoutes } from "./routes"
import { middleware as pro } from "@budibase/pro"
-import { apiEnabled } from "../features"
+import { apiEnabled, automationsEnabled } from "../features"
import migrations from "../middleware/appMigrations"
+import { automationQueue } from "../automations"
export { shutdown } from "./routes/public"
const compress = require("koa-compress")
export const router: Router = new Router()
-router.get("/health", ctx => (ctx.status = 200))
+router.get("/health", async ctx => {
+ if (automationsEnabled()) {
+ if (!(await automationQueue.isReady())) {
+ ctx.status = 503
+ return
+ }
+ }
+ ctx.status = 200
+})
router.get("/version", ctx => (ctx.body = envCore.VERSION))
router.use(middleware.errorHandling)
diff --git a/packages/server/src/api/routes/migrations.ts b/packages/server/src/api/routes/migrations.ts
index f530647c78..918b197de2 100644
--- a/packages/server/src/api/routes/migrations.ts
+++ b/packages/server/src/api/routes/migrations.ts
@@ -11,4 +11,6 @@ router
auth.internalApi,
migrationsController.fetchDefinitions
)
+ .get("/api/migrations/status", migrationsController.getMigrationStatus)
+
export default router
diff --git a/packages/server/src/appMigrations/index.ts b/packages/server/src/appMigrations/index.ts
index a4ffe64604..b382d8b533 100644
--- a/packages/server/src/appMigrations/index.ts
+++ b/packages/server/src/appMigrations/index.ts
@@ -1,6 +1,9 @@
import queue from "./queue"
+import { Next } from "koa"
import { getAppMigrationVersion } from "./appMigrationMetadata"
import { MIGRATIONS } from "./migrations"
+import { UserCtx } from "@budibase/types"
+import { Header } from "@budibase/backend-core"
export * from "./appMigrationMetadata"
@@ -9,14 +12,20 @@ export type AppMigration = {
func: () => Promise
}
-export const latestMigration = MIGRATIONS.map(m => m.id)
- .sort()
- .reverse()[0]
+export const getLatestMigrationId = () =>
+ MIGRATIONS.map(m => m.id)
+ .sort()
+ .reverse()[0]
const getTimestamp = (versionId: string) => versionId?.split("_")[0]
-export async function checkMissingMigrations(appId: string) {
+export async function checkMissingMigrations(
+ ctx: UserCtx,
+ next: Next,
+ appId: string
+) {
const currentVersion = await getAppMigrationVersion(appId)
+ const latestMigration = getLatestMigrationId()
if (getTimestamp(currentVersion) < getTimestamp(latestMigration)) {
await queue.add(
@@ -29,5 +38,9 @@ export async function checkMissingMigrations(appId: string) {
removeOnFail: true,
}
)
+
+ ctx.response.set(Header.MIGRATING_APP, appId)
}
+
+ return next()
}
diff --git a/packages/server/src/appMigrations/tests/migrations.integrity.spec.ts b/packages/server/src/appMigrations/tests/migrations.integrity.spec.ts
new file mode 100644
index 0000000000..145a06d7f5
--- /dev/null
+++ b/packages/server/src/appMigrations/tests/migrations.integrity.spec.ts
@@ -0,0 +1,25 @@
+import { context } from "@budibase/backend-core"
+import * as setup from "../../api/routes/tests/utilities"
+import * as migrations from "../migrations"
+
+describe("migration integrity", () => {
+ // These test is checking that each migration is "idempotent".
+ // We should be able to rerun any migration, with any rerun not modifiying anything. The code should be aware that the migration already ran
+ it("each migration can rerun safely", async () => {
+ const config = setup.getConfig()
+ await config.init()
+
+ await config.doInContext(config.getAppId(), async () => {
+ const db = context.getAppDB()
+ for (const migration of migrations.MIGRATIONS) {
+ await migration.func()
+ const docs = await db.allDocs({ include_docs: true })
+
+ await migration.func()
+ const latestDocs = await db.allDocs({ include_docs: true })
+
+ expect(docs).toEqual(latestDocs)
+ }
+ })
+ })
+})
diff --git a/packages/server/src/appMigrations/tests/migrations.spec.ts b/packages/server/src/appMigrations/tests/migrations.spec.ts
index 9d80cc5f99..5eb8535695 100644
--- a/packages/server/src/appMigrations/tests/migrations.spec.ts
+++ b/packages/server/src/appMigrations/tests/migrations.spec.ts
@@ -1,25 +1,53 @@
-import { context } from "@budibase/backend-core"
+import { Header } from "@budibase/backend-core"
import * as setup from "../../api/routes/tests/utilities"
-import { MIGRATIONS } from "../migrations"
+import * as migrations from "../migrations"
+import { getAppMigrationVersion } from "../appMigrationMetadata"
-describe("migration", () => {
- // These test is checking that each migration is "idempotent".
- // We should be able to rerun any migration, with any rerun not modifiying anything. The code should be aware that the migration already ran
- it("each migration can rerun safely", async () => {
+jest.mock("../migrations", () => ({
+ MIGRATIONS: [
+ {
+ id: "20231211101320_test",
+ func: async () => {},
+ },
+ ],
+}))
+
+describe("migrations", () => {
+ it("new apps are created with the latest app migration version set", async () => {
const config = setup.getConfig()
await config.init()
await config.doInContext(config.getAppId(), async () => {
- const db = context.getAppDB()
- for (const migration of MIGRATIONS) {
- await migration.func()
- const docs = await db.allDocs({ include_docs: true })
+ const migrationVersion = await getAppMigrationVersion(config.getAppId())
- await migration.func()
- const latestDocs = await db.allDocs({ include_docs: true })
-
- expect(docs).toEqual(latestDocs)
- }
+ expect(migrationVersion).toEqual("20231211101320_test")
})
})
+
+ it("accessing an app that has no pending migrations will not attach the migrating header", async () => {
+ const config = setup.getConfig()
+ await config.init()
+
+ const appId = config.getAppId()
+
+ const response = await config.api.application.getRaw(appId)
+
+ expect(response.headers[Header.MIGRATING_APP]).toBeUndefined()
+ })
+
+ it("accessing an app that has pending migrations will attach the migrating header", async () => {
+ const config = setup.getConfig()
+ await config.init()
+
+ const appId = config.getAppId()
+
+ migrations.MIGRATIONS.push({
+ id: "20231211105812_new-test",
+ func: async () => {},
+ })
+
+ const response = await config.api.application.getRaw(appId)
+
+ expect(response.headers[Header.MIGRATING_APP]).toEqual(appId)
+ })
})
diff --git a/packages/server/src/appMigrations/tests/migrationsProcessor.spec.ts b/packages/server/src/appMigrations/tests/migrationsProcessor.spec.ts
index 189f6c068b..3b8e90b526 100644
--- a/packages/server/src/appMigrations/tests/migrationsProcessor.spec.ts
+++ b/packages/server/src/appMigrations/tests/migrationsProcessor.spec.ts
@@ -4,12 +4,14 @@ import { getAppMigrationVersion } from "../appMigrationMetadata"
import { context } from "@budibase/backend-core"
import { AppMigration } from ".."
+const futureTimestamp = `20500101174029`
+
describe("migrationsProcessor", () => {
it("running migrations will update the latest applied migration", async () => {
const testMigrations: AppMigration[] = [
- { id: "123", func: async () => {} },
- { id: "124", func: async () => {} },
- { id: "125", func: async () => {} },
+ { id: `${futureTimestamp}_123`, func: async () => {} },
+ { id: `${futureTimestamp}_124`, func: async () => {} },
+ { id: `${futureTimestamp}_125`, func: async () => {} },
]
const config = setup.getConfig()
@@ -23,13 +25,13 @@ describe("migrationsProcessor", () => {
expect(
await config.doInContext(appId, () => getAppMigrationVersion(appId))
- ).toBe("125")
+ ).toBe(`${futureTimestamp}_125`)
})
it("no context can be initialised within a migration", async () => {
const testMigrations: AppMigration[] = [
{
- id: "123",
+ id: `${futureTimestamp}_123`,
func: async () => {
await context.doInAppMigrationContext("any", () => {})
},
diff --git a/packages/server/src/db/linkedRows/linkUtils.ts b/packages/server/src/db/linkedRows/linkUtils.ts
index 5942e7e5a1..20f752407f 100644
--- a/packages/server/src/db/linkedRows/linkUtils.ts
+++ b/packages/server/src/db/linkedRows/linkUtils.ts
@@ -56,7 +56,7 @@ export async function getLinkDocuments(args: {
try {
let linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows
// filter to get unique entries
- const foundIds: string[] = []
+ const foundIds = new Set()
linkRows = linkRows.filter(link => {
// make sure anything unique is the correct key
if (
@@ -65,9 +65,9 @@ export async function getLinkDocuments(args: {
) {
return false
}
- const unique = foundIds.indexOf(link.id) === -1
+ const unique = !foundIds.has(link.id)
if (unique) {
- foundIds.push(link.id)
+ foundIds.add(link.id)
}
return unique
})
@@ -99,9 +99,15 @@ export async function getLinkDocuments(args: {
}
export function getUniqueByProp(array: any[], prop: string) {
- return array.filter((obj, pos, arr) => {
- return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos
- })
+ const seen = new Set()
+ const filteredArray = []
+ for (const item of array) {
+ if (!seen.has(item[prop])) {
+ seen.add(item[prop])
+ filteredArray.push(item)
+ }
+ }
+ return filteredArray
}
export function getLinkedTableIDs(table: Table): string[] {
diff --git a/packages/server/src/middleware/appMigrations.ts b/packages/server/src/middleware/appMigrations.ts
index a94b8823e8..36e021c7ed 100644
--- a/packages/server/src/middleware/appMigrations.ts
+++ b/packages/server/src/middleware/appMigrations.ts
@@ -8,7 +8,5 @@ export default async (ctx: UserCtx, next: any) => {
return next()
}
- await checkMissingMigrations(appId)
-
- return next()
+ return checkMissingMigrations(ctx, next, appId)
}
diff --git a/packages/server/src/middleware/currentapp.ts b/packages/server/src/middleware/currentapp.ts
index 984dd8e5e9..4b11b933af 100644
--- a/packages/server/src/middleware/currentapp.ts
+++ b/packages/server/src/middleware/currentapp.ts
@@ -12,6 +12,7 @@ import { getCachedSelf } from "../utilities/global"
import env from "../environment"
import { isWebhookEndpoint } from "./utils"
import { UserCtx, ContextUser } from "@budibase/types"
+import tracer from "dd-trace"
export default async (ctx: UserCtx, next: any) => {
// try to get the appID from the request
@@ -20,6 +21,11 @@ export default async (ctx: UserCtx, next: any) => {
return next()
}
+ if (requestAppId) {
+ const span = tracer.scope().active()
+ span?.addTags({ app_id: requestAppId })
+ }
+
// deny access to application preview
if (!env.isTest()) {
if (
diff --git a/packages/server/src/tests/utilities/api/application.ts b/packages/server/src/tests/utilities/api/application.ts
index 85bc4e4173..9c784bade1 100644
--- a/packages/server/src/tests/utilities/api/application.ts
+++ b/packages/server/src/tests/utilities/api/application.ts
@@ -1,3 +1,4 @@
+import { Response } from "supertest"
import { App } from "@budibase/types"
import TestConfiguration from "../TestConfiguration"
import { TestAPI } from "./base"
@@ -7,12 +8,17 @@ export class ApplicationAPI extends TestAPI {
super(config)
}
- get = async (appId: string): Promise => {
+ getRaw = async (appId: string): Promise => {
const result = await this.request
.get(`/api/applications/${appId}/appPackage`)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
+ return result
+ }
+
+ get = async (appId: string): Promise => {
+ const result = await this.getRaw(appId)
return result.body.application as App
}
}
diff --git a/packages/server/src/utilities/rowProcessor/utils.ts b/packages/server/src/utilities/rowProcessor/utils.ts
index 9eb725dd7c..cafd366cae 100644
--- a/packages/server/src/utilities/rowProcessor/utils.ts
+++ b/packages/server/src/utilities/rowProcessor/utils.ts
@@ -11,6 +11,7 @@ import {
Row,
Table,
} from "@budibase/types"
+import tracer from "dd-trace"
interface FormulaOpts {
dynamic?: boolean
@@ -50,33 +51,42 @@ export function processFormulas(
inputRows: T,
{ dynamic, contextRows }: FormulaOpts = { dynamic: true }
): T {
- const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
- if (rows)
- for (let [column, schema] of Object.entries(table.schema)) {
- if (schema.type !== FieldTypes.FORMULA) {
- continue
- }
+ return tracer.trace("processFormulas", {}, span => {
+ const numRows = Array.isArray(inputRows) ? inputRows.length : 1
+ span?.addTags({ table_id: table._id, dynamic, numRows })
+ const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
+ if (rows) {
+ for (let [column, schema] of Object.entries(table.schema)) {
+ if (schema.type !== FieldTypes.FORMULA) {
+ continue
+ }
- const isStatic = schema.formulaType === FormulaTypes.STATIC
+ const isStatic = schema.formulaType === FormulaTypes.STATIC
- if (
- schema.formula == null ||
- (dynamic && isStatic) ||
- (!dynamic && !isStatic)
- ) {
- continue
- }
- // iterate through rows and process formula
- for (let i = 0; i < rows.length; i++) {
- let row = rows[i]
- let context = contextRows ? contextRows[i] : row
- rows[i] = {
- ...row,
- [column]: processStringSync(schema.formula, context),
+ if (
+ schema.formula == null ||
+ (dynamic && isStatic) ||
+ (!dynamic && !isStatic)
+ ) {
+ continue
+ }
+ // iterate through rows and process formula
+ for (let i = 0; i < rows.length; i++) {
+ let row = rows[i]
+ let context = contextRows ? contextRows[i] : row
+ let formula = schema.formula
+ rows[i] = {
+ ...row,
+ [column]: tracer.trace("processStringSync", {}, span => {
+ span?.addTags({ table_id: table._id, column, static: isStatic })
+ return processStringSync(formula, context)
+ }),
+ }
}
}
}
- return Array.isArray(inputRows) ? rows : rows[0]
+ return Array.isArray(inputRows) ? rows : rows[0]
+ })
}
/**
diff --git a/packages/shared-core/src/constants/api.ts b/packages/shared-core/src/constants/api.ts
new file mode 100644
index 0000000000..d6633649e6
--- /dev/null
+++ b/packages/shared-core/src/constants/api.ts
@@ -0,0 +1,19 @@
+export enum Header {
+ API_KEY = "x-budibase-api-key",
+ LICENSE_KEY = "x-budibase-license-key",
+ API_VER = "x-budibase-api-version",
+ APP_ID = "x-budibase-app-id",
+ SESSION_ID = "x-budibase-session-id",
+ TYPE = "x-budibase-type",
+ PREVIEW_ROLE = "x-budibase-role",
+ TENANT_ID = "x-budibase-tenant-id",
+ VERIFICATION_CODE = "x-budibase-verification-code",
+ RETURN_VERIFICATION_CODE = "x-budibase-return-verification-code",
+ RESET_PASSWORD_CODE = "x-budibase-reset-password-code",
+ RETURN_RESET_PASSWORD_CODE = "x-budibase-return-reset-password-code",
+ TOKEN = "x-budibase-token",
+ CSRF_TOKEN = "x-csrf-token",
+ CORRELATION_ID = "x-budibase-correlation-id",
+ AUTHORIZATION = "authorization",
+ MIGRATING_APP = "x-budibase-migrating-app",
+}
diff --git a/packages/shared-core/src/constants.ts b/packages/shared-core/src/constants/index.ts
similarity index 99%
rename from packages/shared-core/src/constants.ts
rename to packages/shared-core/src/constants/index.ts
index 0787b8bed1..a23913dd11 100644
--- a/packages/shared-core/src/constants.ts
+++ b/packages/shared-core/src/constants/index.ts
@@ -1,3 +1,5 @@
+export * from "./api"
+
export const OperatorOptions = {
Equals: {
value: "equal",
diff --git a/packages/string-templates/src/helpers/javascript.js b/packages/string-templates/src/helpers/javascript.js
index c5996c25f0..53baec8613 100644
--- a/packages/string-templates/src/helpers/javascript.js
+++ b/packages/string-templates/src/helpers/javascript.js
@@ -56,6 +56,10 @@ module.exports.processJS = (handlebars, context) => {
const res = { data: runJS(js, sandboxContext) }
return `{{${LITERAL_MARKER} js_result-${JSON.stringify(res)}}}`
} catch (error) {
+ console.log(`JS error: ${typeof error} ${JSON.stringify(error)}`)
+ if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
+ return "Timed out while executing JS"
+ }
return "Error while executing JS"
}
}
diff --git a/packages/string-templates/test/javascript.spec.js b/packages/string-templates/test/javascript.spec.js
index b35bf8b040..d8b42678a7 100644
--- a/packages/string-templates/test/javascript.spec.js
+++ b/packages/string-templates/test/javascript.spec.js
@@ -115,7 +115,7 @@ describe("Test the JavaScript helper", () => {
it("should timeout after one second", () => {
const output = processJS(`while (true) {}`)
- expect(output).toBe("Error while executing JS")
+ expect(output).toBe("Timed out while executing JS")
})
it("should prevent access to the process global", () => {
diff --git a/packages/worker/Dockerfile b/packages/worker/Dockerfile
index 4706ca155a..b03a817c8b 100644
--- a/packages/worker/Dockerfile
+++ b/packages/worker/Dockerfile
@@ -51,8 +51,12 @@ ENV TENANT_FEATURE_FLAGS=*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR
ENV ACCOUNT_PORTAL_URL=https://account.budibase.app
ARG BUDIBASE_VERSION
+ARG GIT_COMMIT_SHA
# Ensuring the version argument is sent
RUN test -n "$BUDIBASE_VERSION"
ENV BUDIBASE_VERSION=$BUDIBASE_VERSION
+ENV DD_GIT_REPOSITORY_URL=https://github.com/budibase/budibase
+ENV DD_GIT_COMMIT_SHA=$GIT_COMMIT_SHA
+ENV DD_VERSION=$BUDIBASE_VERSION
CMD ["./docker_run.sh"]