Merge branch 'master' into feature/uuid-helper
This commit is contained in:
commit
dceb623732
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
"source.fixAll": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[json]": {
|
||||
|
|
|
@ -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 <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> 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: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
|
||||
| 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: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
|
||||
| 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: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
|
||||
| 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 <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> 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: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
|
||||
| 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 <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> for more information on how to set these. |
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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 }}
|
|
@ -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 }}
|
|
@ -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 }}
|
||||
|
|
|
@ -220,6 +220,9 @@ services:
|
|||
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
|
||||
# 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:
|
||||
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
|
||||
|
@ -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
|
||||
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
|
||||
# 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:
|
||||
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
|
||||
# @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:
|
||||
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
|
||||
# @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:
|
||||
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
|
||||
# @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:
|
|||
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
|
||||
# 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:
|
||||
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
|
||||
|
|
|
@ -257,6 +257,7 @@ http {
|
|||
|
||||
access_log off;
|
||||
allow 127.0.0.1;
|
||||
allow 10.0.0.0/8;
|
||||
deny all;
|
||||
|
||||
location /nginx_status {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "2.13.38",
|
||||
"version": "2.13.45",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*",
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit a0b13270c36dd188e2a953d026b4560a1208008e
|
||||
Subproject commit a1832145f08fca9b923cc145930d5d3a4c2f1d1f
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -257,7 +257,7 @@
|
|||
|
||||
<LockedFeature
|
||||
title={"Audit Logs"}
|
||||
planType={"Business plan"}
|
||||
planType={"Enterprise plan"}
|
||||
description={"View all events that have occurred in your Budibase installation"}
|
||||
enabled={$licensing.auditLogsEnabled}
|
||||
upgradeButtonClick={async () => {
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -283,7 +283,7 @@
|
|||
</div>
|
||||
{#if !$licensing.enforceableSSO}
|
||||
<Tags>
|
||||
<Tag icon="LockClosed">Enterprise</Tag>
|
||||
<Tag icon="LockClosed">Enterprise plan</Tag>
|
||||
</Tags>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
<LockedFeature
|
||||
title={"Environment Variables"}
|
||||
planType={"Business plan"}
|
||||
planType={"Enterprise plan"}
|
||||
description={"Add and manage environment variables for development and production"}
|
||||
enabled={$licensing.environmentVariablesEnabled}
|
||||
upgradeButtonClick={async () => {
|
||||
|
|
|
@ -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]
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -11,4 +11,6 @@ router
|
|||
auth.internalApi,
|
||||
migrationsController.fetchDefinitions
|
||||
)
|
||||
.get("/api/migrations/status", migrationsController.getMigrationStatus)
|
||||
|
||||
export default router
|
||||
|
|
|
@ -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<void>
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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<typeof migrations>("../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)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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", () => {})
|
||||
},
|
||||
|
|
|
@ -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[] {
|
||||
|
|
|
@ -8,7 +8,5 @@ export default async (ctx: UserCtx, next: any) => {
|
|||
return next()
|
||||
}
|
||||
|
||||
await checkMissingMigrations(appId)
|
||||
|
||||
return next()
|
||||
return checkMissingMigrations(ctx, next, appId)
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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<App> => {
|
||||
getRaw = async (appId: string): Promise<Response> => {
|
||||
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<App> => {
|
||||
const result = await this.getRaw(appId)
|
||||
return result.body.application as App
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T extends Row | Row[]>(
|
|||
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]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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",
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
export * from "./api"
|
||||
|
||||
export const OperatorOptions = {
|
||||
Equals: {
|
||||
value: "equal",
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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", () => {
|
||||
|
|
|
@ -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"]
|
||||
|
|
Loading…
Reference in New Issue