Merge remote-tracking branch 'origin/master' into feature/multistep-form-block
This commit is contained in:
commit
42171bd41d
|
@ -76,6 +76,18 @@ jobs:
|
||||||
yarn check:types
|
yarn check:types
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
helm-lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Use Node.js 18.x
|
||||||
|
uses: azure/setup-helm@v3
|
||||||
|
- run: cd charts/budibase && helm lint .
|
||||||
|
|
||||||
test-libraries:
|
test-libraries:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -204,7 +216,7 @@ jobs:
|
||||||
|
|
||||||
check-pro-submodule:
|
check-pro-submodule:
|
||||||
runs-on: ubuntu-latest
|
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:
|
steps:
|
||||||
- name: Checkout repo and submodules
|
- name: Checkout repo and submodules
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -254,7 +266,7 @@ jobs:
|
||||||
|
|
||||||
check-accountportal-submodule:
|
check-accountportal-submodule:
|
||||||
runs-on: ubuntu-latest
|
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:
|
steps:
|
||||||
- name: Checkout repo and submodules
|
- name: Checkout repo and submodules
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
|
@ -227,6 +227,14 @@ spec:
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml . | nindent 10 }}
|
{{- toYaml . | nindent 10 }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if .Values.services.apps.command }}
|
||||||
|
command:
|
||||||
|
{{- toYaml .Values.services.apps.command | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.services.apps.args }}
|
||||||
|
args:
|
||||||
|
{{- toYaml .Values.services.apps.args | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|
|
@ -227,6 +227,13 @@ spec:
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml . | nindent 10 }}
|
{{- toYaml . | nindent 10 }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
command:
|
||||||
|
{{- toYaml .Values.services.automationWorkers.command | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.services.automationWorkers.args }}
|
||||||
|
args:
|
||||||
|
{{- toYaml .Values.services.automationWorkers.args | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
@ -244,5 +251,6 @@ spec:
|
||||||
{{ end }}
|
{{ end }}
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
serviceAccountName: ""
|
serviceAccountName: ""
|
||||||
|
{{ if .Values.services.automationWorkers.command }}}
|
||||||
status: {}
|
status: {}
|
||||||
{{- end }}
|
{{- end }}
|
|
@ -100,5 +100,13 @@ spec:
|
||||||
{{ end }}
|
{{ end }}
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
serviceAccountName: ""
|
serviceAccountName: ""
|
||||||
|
{{ if .Values.services.proxy.command }}
|
||||||
|
command:
|
||||||
|
{{- toYaml .Values.services.proxy.command | nindent 8 }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.services.proxy.args }}
|
||||||
|
args:
|
||||||
|
{{- toYaml .Values.services.proxy.args | nindent 8 }}
|
||||||
|
{{ end }}
|
||||||
volumes:
|
volumes:
|
||||||
status: {}
|
status: {}
|
||||||
|
|
|
@ -213,6 +213,14 @@ spec:
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml . | nindent 10 }}
|
{{- toYaml . | nindent 10 }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ if .Values.services.worker.command }}
|
||||||
|
command:
|
||||||
|
{{- toYaml .Values.services.worker.command | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Values.services.worker.args }}
|
||||||
|
args:
|
||||||
|
{{- toYaml .Values.services.worker.args | nindent 10 }}
|
||||||
|
{{ end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|
|
@ -7,7 +7,7 @@ declare -a DOCKER_VARS=("APP_PORT" "APPS_URL" "ARCHITECTURE" "BUDIBASE_ENVIRONME
|
||||||
[[ -z "${BUDIBASE_ENVIRONMENT}" ]] && export BUDIBASE_ENVIRONMENT=PRODUCTION
|
[[ -z "${BUDIBASE_ENVIRONMENT}" ]] && export BUDIBASE_ENVIRONMENT=PRODUCTION
|
||||||
[[ -z "${CLUSTER_PORT}" ]] && export CLUSTER_PORT=80
|
[[ -z "${CLUSTER_PORT}" ]] && export CLUSTER_PORT=80
|
||||||
[[ -z "${DEPLOYMENT_ENVIRONMENT}" ]] && export DEPLOYMENT_ENVIRONMENT=docker
|
[[ -z "${DEPLOYMENT_ENVIRONMENT}" ]] && export DEPLOYMENT_ENVIRONMENT=docker
|
||||||
[[ -z "${MINIO_URL}" ]] && export MINIO_URL=http://127.0.0.1:9000
|
[[ -z "${MINIO_URL}" ]] && [[ -z "${USE_S3}" ]] && export MINIO_URL=http://127.0.0.1:9000
|
||||||
[[ -z "${NODE_ENV}" ]] && export NODE_ENV=production
|
[[ -z "${NODE_ENV}" ]] && export NODE_ENV=production
|
||||||
[[ -z "${POSTHOG_TOKEN}" ]] && export POSTHOG_TOKEN=phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU
|
[[ -z "${POSTHOG_TOKEN}" ]] && export POSTHOG_TOKEN=phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU
|
||||||
[[ -z "${TENANT_FEATURE_FLAGS}" ]] && export TENANT_FEATURE_FLAGS="*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR"
|
[[ -z "${TENANT_FEATURE_FLAGS}" ]] && export TENANT_FEATURE_FLAGS="*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR"
|
||||||
|
@ -77,7 +77,12 @@ mkdir -p ${DATA_DIR}/minio
|
||||||
chown -R couchdb:couchdb ${DATA_DIR}/couch
|
chown -R couchdb:couchdb ${DATA_DIR}/couch
|
||||||
redis-server --requirepass $REDIS_PASSWORD > /dev/stdout 2>&1 &
|
redis-server --requirepass $REDIS_PASSWORD > /dev/stdout 2>&1 &
|
||||||
/bbcouch-runner.sh &
|
/bbcouch-runner.sh &
|
||||||
/minio/minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 &
|
|
||||||
|
# only start minio if use s3 isn't passed
|
||||||
|
if [[ -z "${USE_S3}" ]]; then
|
||||||
|
/minio/minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 &
|
||||||
|
fi
|
||||||
|
|
||||||
/etc/init.d/nginx restart
|
/etc/init.d/nginx restart
|
||||||
if [[ ! -z "${CUSTOM_DOMAIN}" ]]; then
|
if [[ ! -z "${CUSTOM_DOMAIN}" ]]; then
|
||||||
# Add monthly cron job to renew certbot certificate
|
# Add monthly cron job to renew certbot certificate
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.13.41",
|
"version": "2.13.52",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a0b13270c36dd188e2a953d026b4560a1208008e
|
Subproject commit c1a53bb2f4cafcb4c55ad7181146617b449907f2
|
|
@ -32,6 +32,7 @@
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"bull": "4.10.1",
|
"bull": "4.10.1",
|
||||||
"correlation-id": "4.0.0",
|
"correlation-id": "4.0.0",
|
||||||
|
"dd-trace": "3.13.2",
|
||||||
"dotenv": "16.0.1",
|
"dotenv": "16.0.1",
|
||||||
"ioredis": "5.3.2",
|
"ioredis": "5.3.2",
|
||||||
"joi": "17.6.0",
|
"joi": "17.6.0",
|
||||||
|
|
|
@ -18,14 +18,15 @@ export enum TTL {
|
||||||
ONE_DAY = 86400,
|
ONE_DAY = 86400,
|
||||||
}
|
}
|
||||||
|
|
||||||
function performExport(funcName: string) {
|
export const keys = (...args: Parameters<typeof GENERIC.keys>) =>
|
||||||
// @ts-ignore
|
GENERIC.keys(...args)
|
||||||
return (...args: any) => GENERIC[funcName](...args)
|
export const get = (...args: Parameters<typeof GENERIC.get>) =>
|
||||||
}
|
GENERIC.get(...args)
|
||||||
|
export const store = (...args: Parameters<typeof GENERIC.store>) =>
|
||||||
export const keys = performExport("keys")
|
GENERIC.store(...args)
|
||||||
export const get = performExport("get")
|
export const destroy = (...args: Parameters<typeof GENERIC.delete>) =>
|
||||||
export const store = performExport("store")
|
GENERIC.delete(...args)
|
||||||
export const destroy = performExport("delete")
|
export const withCache = (...args: Parameters<typeof GENERIC.withCache>) =>
|
||||||
export const withCache = performExport("withCache")
|
GENERIC.withCache(...args)
|
||||||
export const bustCache = performExport("bustCache")
|
export const bustCache = (...args: Parameters<typeof GENERIC.bustCache>) =>
|
||||||
|
GENERIC.bustCache(...args)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as redis from "../redis/init"
|
import * as redis from "../redis/init"
|
||||||
import * as utils from "../utils"
|
import * as utils from "../utils"
|
||||||
import { Duration, DurationType } from "../utils"
|
import { Duration } from "../utils"
|
||||||
|
|
||||||
const TTL_SECONDS = Duration.fromHours(1).toSeconds()
|
const TTL_SECONDS = Duration.fromHours(1).toSeconds()
|
||||||
|
|
||||||
|
@ -32,7 +32,18 @@ export async function getCode(code: string): Promise<PasswordReset> {
|
||||||
const client = await redis.getPasswordResetClient()
|
const client = await redis.getPasswordResetClient()
|
||||||
const value = (await client.get(code)) as PasswordReset | undefined
|
const value = (await client.get(code)) as PasswordReset | undefined
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw "Provided information is not valid, cannot reset password - please try again."
|
throw new Error(
|
||||||
|
"Provided information is not valid, cannot reset password - please try again."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a reset code this will invalidate it.
|
||||||
|
* @param code The code provided via the email link.
|
||||||
|
*/
|
||||||
|
export async function invalidateCode(code: string): Promise<void> {
|
||||||
|
const client = await redis.getPasswordResetClient()
|
||||||
|
await client.delete(code)
|
||||||
|
}
|
||||||
|
|
|
@ -56,11 +56,8 @@ async function put(
|
||||||
const writeDb = async (toWrite: any) => {
|
const writeDb = async (toWrite: any) => {
|
||||||
// doc should contain the _id and _rev
|
// doc should contain the _id and _rev
|
||||||
const response = await db.put(toWrite, { force: true })
|
const response = await db.put(toWrite, { force: true })
|
||||||
output = {
|
output._id = response.id
|
||||||
...doc,
|
output._rev = response.rev
|
||||||
_id: response.id,
|
|
||||||
_rev: response.rev,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await writeDb(doc)
|
await writeDb(doc)
|
||||||
|
|
|
@ -335,3 +335,11 @@ export function isScim(): boolean {
|
||||||
const scimCall = context?.isScim
|
const scimCall = context?.isScim
|
||||||
return !!scimCall
|
return !!scimCall
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCurrentContext(): ContextMap | undefined {
|
||||||
|
try {
|
||||||
|
return Context.get()
|
||||||
|
} catch (e) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { IdentityContext } from "@budibase/types"
|
import { IdentityContext } from "@budibase/types"
|
||||||
|
import { ExecutionTimeTracker } from "../timers"
|
||||||
|
|
||||||
// keep this out of Budibase types, don't want to expose context info
|
// keep this out of Budibase types, don't want to expose context info
|
||||||
export type ContextMap = {
|
export type ContextMap = {
|
||||||
|
@ -9,4 +10,5 @@ export type ContextMap = {
|
||||||
isScim?: boolean
|
isScim?: boolean
|
||||||
automationId?: string
|
automationId?: string
|
||||||
isMigrating?: boolean
|
isMigrating?: boolean
|
||||||
|
jsExecutionTracker?: ExecutionTimeTracker
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { directCouchUrlCall } from "./utils"
|
||||||
import { getPouchDB } from "./pouchDB"
|
import { getPouchDB } from "./pouchDB"
|
||||||
import { WriteStream, ReadStream } from "fs"
|
import { WriteStream, ReadStream } from "fs"
|
||||||
import { newid } from "../../docIds/newid"
|
import { newid } from "../../docIds/newid"
|
||||||
|
import { DDInstrumentedDatabase } from "../instrumentation"
|
||||||
|
|
||||||
function buildNano(couchInfo: { url: string; cookie: string }) {
|
function buildNano(couchInfo: { url: string; cookie: string }) {
|
||||||
return Nano({
|
return Nano({
|
||||||
|
@ -35,10 +36,8 @@ export function DatabaseWithConnection(
|
||||||
connection: string,
|
connection: string,
|
||||||
opts?: DatabaseOpts
|
opts?: DatabaseOpts
|
||||||
) {
|
) {
|
||||||
if (!connection) {
|
const db = new DatabaseImpl(dbName, opts, connection)
|
||||||
throw new Error("Must provide connection details")
|
return new DDInstrumentedDatabase(db)
|
||||||
}
|
|
||||||
return new DatabaseImpl(dbName, opts, connection)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DatabaseImpl implements Database {
|
export class DatabaseImpl implements Database {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { directCouchQuery, DatabaseImpl } from "./couch"
|
import { directCouchQuery, DatabaseImpl } from "./couch"
|
||||||
import { CouchFindOptions, Database, DatabaseOpts } from "@budibase/types"
|
import { CouchFindOptions, Database, DatabaseOpts } from "@budibase/types"
|
||||||
|
import { DDInstrumentedDatabase } from "./instrumentation"
|
||||||
|
|
||||||
export function getDB(dbName: string, opts?: DatabaseOpts): Database {
|
export function getDB(dbName: string, opts?: DatabaseOpts): Database {
|
||||||
return new DatabaseImpl(dbName, opts)
|
return new DDInstrumentedDatabase(new DatabaseImpl(dbName, opts))
|
||||||
}
|
}
|
||||||
|
|
||||||
// we have to use a callback for this so that we can close
|
// we have to use a callback for this so that we can close
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
import {
|
||||||
|
DocumentScope,
|
||||||
|
DocumentDestroyResponse,
|
||||||
|
DocumentInsertResponse,
|
||||||
|
DocumentBulkResponse,
|
||||||
|
OkResponse,
|
||||||
|
} from "@budibase/nano"
|
||||||
|
import {
|
||||||
|
AllDocsResponse,
|
||||||
|
AnyDocument,
|
||||||
|
Database,
|
||||||
|
DatabaseDumpOpts,
|
||||||
|
DatabasePutOpts,
|
||||||
|
DatabaseQueryOpts,
|
||||||
|
Document,
|
||||||
|
} from "@budibase/types"
|
||||||
|
import tracer from "dd-trace"
|
||||||
|
import { Writable } from "stream"
|
||||||
|
|
||||||
|
export class DDInstrumentedDatabase implements Database {
|
||||||
|
constructor(private readonly db: Database) {}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return this.db.name
|
||||||
|
}
|
||||||
|
|
||||||
|
exists(): Promise<boolean> {
|
||||||
|
return tracer.trace("db.exists", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.exists()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSetup(): Promise<DocumentScope<any>> {
|
||||||
|
return tracer.trace("db.checkSetup", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.checkSetup()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get<T extends Document>(id?: string | undefined): Promise<T> {
|
||||||
|
return tracer.trace("db.get", span => {
|
||||||
|
span?.addTags({ db_name: this.name, doc_id: id })
|
||||||
|
return this.db.get(id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getMultiple<T extends Document>(
|
||||||
|
ids: string[],
|
||||||
|
opts?: { allowMissing?: boolean | undefined } | undefined
|
||||||
|
): Promise<T[]> {
|
||||||
|
return tracer.trace("db.getMultiple", span => {
|
||||||
|
span?.addTags({
|
||||||
|
db_name: this.name,
|
||||||
|
num_docs: ids.length,
|
||||||
|
allow_missing: opts?.allowMissing,
|
||||||
|
})
|
||||||
|
return this.db.getMultiple(ids, opts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(
|
||||||
|
id: string | Document,
|
||||||
|
rev?: string | undefined
|
||||||
|
): Promise<DocumentDestroyResponse> {
|
||||||
|
return tracer.trace("db.remove", span => {
|
||||||
|
span?.addTags({ db_name: this.name, doc_id: id })
|
||||||
|
return this.db.remove(id, rev)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
put(
|
||||||
|
document: AnyDocument,
|
||||||
|
opts?: DatabasePutOpts | undefined
|
||||||
|
): Promise<DocumentInsertResponse> {
|
||||||
|
return tracer.trace("db.put", span => {
|
||||||
|
span?.addTags({ db_name: this.name, doc_id: document._id })
|
||||||
|
return this.db.put(document, opts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkDocs(documents: AnyDocument[]): Promise<DocumentBulkResponse[]> {
|
||||||
|
return tracer.trace("db.bulkDocs", span => {
|
||||||
|
span?.addTags({ db_name: this.name, num_docs: documents.length })
|
||||||
|
return this.db.bulkDocs(documents)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
allDocs<T extends Document>(
|
||||||
|
params: DatabaseQueryOpts
|
||||||
|
): Promise<AllDocsResponse<T>> {
|
||||||
|
return tracer.trace("db.allDocs", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.allDocs(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
query<T extends Document>(
|
||||||
|
viewName: string,
|
||||||
|
params: DatabaseQueryOpts
|
||||||
|
): Promise<AllDocsResponse<T>> {
|
||||||
|
return tracer.trace("db.query", span => {
|
||||||
|
span?.addTags({ db_name: this.name, view_name: viewName })
|
||||||
|
return this.db.query(viewName, params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): Promise<void | OkResponse> {
|
||||||
|
return tracer.trace("db.destroy", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.destroy()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
compact(): Promise<void | OkResponse> {
|
||||||
|
return tracer.trace("db.compact", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.compact()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dump(stream: Writable, opts?: DatabaseDumpOpts | undefined): Promise<any> {
|
||||||
|
return tracer.trace("db.dump", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.dump(stream, opts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
load(...args: any[]): Promise<any> {
|
||||||
|
return tracer.trace("db.load", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.load(...args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
createIndex(...args: any[]): Promise<any> {
|
||||||
|
return tracer.trace("db.createIndex", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.createIndex(...args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteIndex(...args: any[]): Promise<any> {
|
||||||
|
return tracer.trace("db.deleteIndex", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.deleteIndex(...args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexes(...args: any[]): Promise<any> {
|
||||||
|
return tracer.trace("db.getIndexes", span => {
|
||||||
|
span?.addTags({ db_name: this.name })
|
||||||
|
return this.db.getIndexes(...args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import { IdentityType } from "@budibase/types"
|
||||||
import env from "../../environment"
|
import env from "../../environment"
|
||||||
import * as context from "../../context"
|
import * as context from "../../context"
|
||||||
import * as correlation from "../correlation"
|
import * as correlation from "../correlation"
|
||||||
|
import tracer from "dd-trace"
|
||||||
|
import { formats } from "dd-trace/ext"
|
||||||
|
|
||||||
import { localFileDestination } from "../system"
|
import { localFileDestination } from "../system"
|
||||||
|
|
||||||
|
@ -115,6 +117,11 @@ if (!env.DISABLE_PINO_LOGGER) {
|
||||||
correlationId: correlation.getId(),
|
correlationId: correlation.getId(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const span = tracer.scope().active()
|
||||||
|
if (span) {
|
||||||
|
tracer.inject(span.context(), formats.LOG, contextObject)
|
||||||
|
}
|
||||||
|
|
||||||
const mergingObject: any = {
|
const mergingObject: any = {
|
||||||
err: error,
|
err: error,
|
||||||
pid: process.pid,
|
pid: process.pid,
|
||||||
|
|
|
@ -15,6 +15,7 @@ function newJob(queue: string, message: any) {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
queue: queue,
|
queue: queue,
|
||||||
data: message,
|
data: message,
|
||||||
|
opts: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ export function createQueue<T>(
|
||||||
cleanupInterval = timers.set(cleanup, CLEANUP_PERIOD_MS)
|
cleanupInterval = timers.set(cleanup, CLEANUP_PERIOD_MS)
|
||||||
// fire off an initial cleanup
|
// fire off an initial cleanup
|
||||||
cleanup().catch(err => {
|
cleanup().catch(err => {
|
||||||
console.error(`Unable to cleanup automation queue initially - ${err}`)
|
console.error(`Unable to cleanup ${jobQueue} initially - ${err}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return queue
|
return queue
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
SelectableDatabase,
|
SelectableDatabase,
|
||||||
getRedisConnectionDetails,
|
getRedisConnectionDetails,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
import { logAlert } from "../logging"
|
||||||
import * as timers from "../timers"
|
import * as timers from "../timers"
|
||||||
|
|
||||||
const RETRY_PERIOD_MS = 2000
|
const RETRY_PERIOD_MS = 2000
|
||||||
|
@ -39,21 +40,16 @@ function pickClient(selectDb: number): any {
|
||||||
return CLIENTS[selectDb]
|
return CLIENTS[selectDb]
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectionError(
|
function connectionError(timeout: NodeJS.Timeout, err: Error | string) {
|
||||||
selectDb: number,
|
|
||||||
timeout: NodeJS.Timeout,
|
|
||||||
err: Error | string
|
|
||||||
) {
|
|
||||||
// manually shut down, ignore errors
|
// manually shut down, ignore errors
|
||||||
if (CLOSED) {
|
if (CLOSED) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pickClient(selectDb).disconnect()
|
|
||||||
CLOSED = true
|
CLOSED = true
|
||||||
// always clear this on error
|
// always clear this on error
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
CONNECTED = false
|
CONNECTED = false
|
||||||
console.error("Redis connection failed - " + err)
|
logAlert("Redis connection failed", err)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
init()
|
init()
|
||||||
}, RETRY_PERIOD_MS)
|
}, RETRY_PERIOD_MS)
|
||||||
|
@ -79,11 +75,7 @@ function init(selectDb = DEFAULT_SELECT_DB) {
|
||||||
// start the timer - only allowed 5 seconds to connect
|
// start the timer - only allowed 5 seconds to connect
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
if (!CONNECTED) {
|
if (!CONNECTED) {
|
||||||
connectionError(
|
connectionError(timeout, "Did not successfully connect in timeout")
|
||||||
selectDb,
|
|
||||||
timeout,
|
|
||||||
"Did not successfully connect in timeout"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}, STARTUP_TIMEOUT_MS)
|
}, STARTUP_TIMEOUT_MS)
|
||||||
|
|
||||||
|
@ -106,12 +98,13 @@ function init(selectDb = DEFAULT_SELECT_DB) {
|
||||||
// allow the process to exit
|
// allow the process to exit
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
connectionError(selectDb, timeout, err)
|
connectionError(timeout, err)
|
||||||
})
|
})
|
||||||
client.on("error", (err: Error) => {
|
client.on("error", (err: Error) => {
|
||||||
connectionError(selectDb, timeout, err)
|
connectionError(timeout, err)
|
||||||
})
|
})
|
||||||
client.on("connect", () => {
|
client.on("connect", () => {
|
||||||
|
console.log(`Connected to Redis DB: ${selectDb}`)
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
CONNECTED = true
|
CONNECTED = true
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,3 +20,41 @@ export function cleanup() {
|
||||||
}
|
}
|
||||||
intervals = []
|
intervals = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ExecutionTimeoutError extends Error {
|
||||||
|
public readonly name = "ExecutionTimeoutError"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExecutionTimeTracker {
|
||||||
|
static withLimit(limitMs: number) {
|
||||||
|
return new ExecutionTimeTracker(limitMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(readonly limitMs: number) {}
|
||||||
|
|
||||||
|
private totalTimeMs = 0
|
||||||
|
|
||||||
|
track<T>(f: () => T): T {
|
||||||
|
this.checkLimit()
|
||||||
|
const start = process.hrtime.bigint()
|
||||||
|
try {
|
||||||
|
return f()
|
||||||
|
} finally {
|
||||||
|
const end = process.hrtime.bigint()
|
||||||
|
this.totalTimeMs += Number(end - start) / 1e6
|
||||||
|
this.checkLimit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get elapsedMS() {
|
||||||
|
return this.totalTimeMs
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkLimit() {
|
||||||
|
if (this.totalTimeMs > this.limitMs) {
|
||||||
|
throw new ExecutionTimeoutError(
|
||||||
|
`Execution time limit of ${this.limitMs}ms exceeded: ${this.totalTimeMs}ms`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import env from "../environment"
|
||||||
import * as eventHelpers from "./events"
|
import * as eventHelpers from "./events"
|
||||||
import * as accountSdk from "../accounts"
|
import * as accountSdk from "../accounts"
|
||||||
import * as cache from "../cache"
|
import * as cache from "../cache"
|
||||||
import { doInTenant, getGlobalDB, getIdentity, getTenantId } from "../context"
|
import { getGlobalDB, getIdentity, getTenantId } from "../context"
|
||||||
import * as dbUtils from "../db"
|
import * as dbUtils from "../db"
|
||||||
import { EmailUnavailableError, HTTPError } from "../errors"
|
import { EmailUnavailableError, HTTPError } from "../errors"
|
||||||
import * as platform from "../platform"
|
import * as platform from "../platform"
|
||||||
|
|
|
@ -49,4 +49,8 @@ export class Duration {
|
||||||
static fromDays(duration: number) {
|
static fromDays(duration: number) {
|
||||||
return Duration.from(DurationType.DAYS, duration)
|
return Duration.from(DurationType.DAYS, duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromMilliseconds(duration: number) {
|
||||||
|
return Duration.from(DurationType.MILLISECONDS, duration)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
// Ensure the value is updated if the value prop changes outside the editor's
|
// Ensure the value is updated if the value prop changes outside the editor's
|
||||||
// control
|
// control
|
||||||
$: checkValue(value)
|
$: checkValue(value)
|
||||||
$: mde?.codemirror.on("change", debouncedUpdate)
|
$: mde?.codemirror.on("blur", update)
|
||||||
$: if (readonly || disabled) {
|
$: if (readonly || disabled) {
|
||||||
mde?.togglePreview()
|
mde?.togglePreview()
|
||||||
}
|
}
|
||||||
|
@ -30,21 +30,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const debounce = (fn, interval) => {
|
|
||||||
let timeout
|
|
||||||
return () => {
|
|
||||||
clearTimeout(timeout)
|
|
||||||
timeout = setTimeout(fn, interval)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const update = () => {
|
const update = () => {
|
||||||
latestValue = mde.value()
|
latestValue = mde.value()
|
||||||
dispatch("change", latestValue)
|
dispatch("change", latestValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debounce the update function to avoid spamming it constantly
|
|
||||||
const debouncedUpdate = debounce(update, 250)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key height}
|
{#key height}
|
||||||
|
|
|
@ -69,7 +69,15 @@
|
||||||
on:change={e => onChange(e, field)}
|
on:change={e => onChange(e, field)}
|
||||||
useLabel={false}
|
useLabel={false}
|
||||||
/>
|
/>
|
||||||
{:else if schema.type === "string" || schema.type === "number"}
|
{:else if schema.type === "bb_reference"}
|
||||||
|
<LinkedRowSelector
|
||||||
|
linkedRows={value[field]}
|
||||||
|
{schema}
|
||||||
|
linkedTableId={"ta_users"}
|
||||||
|
on:change={e => onChange(e, field)}
|
||||||
|
useLabel={false}
|
||||||
|
/>
|
||||||
|
{:else if ["string", "number", "bigint", "barcodeqr"].includes(schema.type)}
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
|
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
|
||||||
panel={AutomationBindingPanel}
|
panel={AutomationBindingPanel}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
export let schema
|
export let schema
|
||||||
export let linkedRows = []
|
export let linkedRows = []
|
||||||
export let useLabel = true
|
export let useLabel = true
|
||||||
|
export let linkedTableId
|
||||||
|
export let label
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
let rows = []
|
let rows = []
|
||||||
|
@ -16,8 +18,8 @@
|
||||||
$: linkedIds = (Array.isArray(linkedRows) ? linkedRows : [])?.map(
|
$: linkedIds = (Array.isArray(linkedRows) ? linkedRows : [])?.map(
|
||||||
row => row?._id || row
|
row => row?._id || row
|
||||||
)
|
)
|
||||||
$: label = capitalise(schema.name)
|
$: label = label || capitalise(schema.name)
|
||||||
$: linkedTableId = schema.tableId
|
$: linkedTableId = linkedTableId || schema.tableId
|
||||||
$: linkedTable = $tables.list.find(table => table._id === linkedTableId)
|
$: linkedTable = $tables.list.find(table => table._id === linkedTableId)
|
||||||
$: fetchRows(linkedTableId)
|
$: fetchRows(linkedTableId)
|
||||||
|
|
||||||
|
@ -57,7 +59,7 @@
|
||||||
{:else}
|
{:else}
|
||||||
<Multiselect
|
<Multiselect
|
||||||
value={linkedIds}
|
value={linkedIds}
|
||||||
{label}
|
label={useLabel ? label : null}
|
||||||
options={rows}
|
options={rows}
|
||||||
getOptionLabel={getPrettyName}
|
getOptionLabel={getPrettyName}
|
||||||
getOptionValue={row => row._id}
|
getOptionValue={row => row._id}
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
bind:this={popover}
|
bind:this={popover}
|
||||||
anchor={popoverAnchor}
|
anchor={popoverAnchor}
|
||||||
maxWidth={300}
|
maxWidth={300}
|
||||||
|
maxHeight={300}
|
||||||
dismissible={false}
|
dismissible={false}
|
||||||
>
|
>
|
||||||
<Layout gap="S">
|
<Layout gap="S">
|
||||||
|
|
|
@ -113,7 +113,7 @@
|
||||||
if (type === "json" && !isJSBinding(value)) {
|
if (type === "json" && !isJSBinding(value)) {
|
||||||
return "json-slot-icon"
|
return "json-slot-icon"
|
||||||
}
|
}
|
||||||
if (type !== "string" && type !== "number") {
|
if (!["string", "number", "bigint", "barcodeqr"].includes(type)) {
|
||||||
return "slot-icon"
|
return "slot-icon"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -164,7 +164,8 @@
|
||||||
// Required constraint
|
// Required constraint
|
||||||
if (
|
if (
|
||||||
field === dataSourceSchema?.table?.primaryDisplay ||
|
field === dataSourceSchema?.table?.primaryDisplay ||
|
||||||
constraints.presence?.allowEmpty === false
|
constraints.presence?.allowEmpty === false ||
|
||||||
|
constraints.presence === true
|
||||||
) {
|
) {
|
||||||
rules.push({
|
rules.push({
|
||||||
constraint: "required",
|
constraint: "required",
|
||||||
|
|
|
@ -257,7 +257,7 @@
|
||||||
|
|
||||||
<LockedFeature
|
<LockedFeature
|
||||||
title={"Audit Logs"}
|
title={"Audit Logs"}
|
||||||
planType={"Business plan"}
|
planType={"Enterprise plan"}
|
||||||
description={"View all events that have occurred in your Budibase installation"}
|
description={"View all events that have occurred in your Budibase installation"}
|
||||||
enabled={$licensing.auditLogsEnabled}
|
enabled={$licensing.auditLogsEnabled}
|
||||||
upgradeButtonClick={async () => {
|
upgradeButtonClick={async () => {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
import { DashCard, Usage } from "components/usage"
|
import { DashCard, Usage } from "components/usage"
|
||||||
import { PlanModel } from "constants"
|
import { PlanModel } from "constants"
|
||||||
import { sdk } from "@budibase/shared-core"
|
import { sdk } from "@budibase/shared-core"
|
||||||
|
import { PlanType } from "@budibase/types"
|
||||||
|
|
||||||
let staticUsage = []
|
let staticUsage = []
|
||||||
let monthlyUsage = []
|
let monthlyUsage = []
|
||||||
|
@ -106,7 +107,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const planTitle = () => {
|
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 => {
|
const getDaysRemaining = timestamp => {
|
||||||
|
|
|
@ -283,7 +283,7 @@
|
||||||
</div>
|
</div>
|
||||||
{#if !$licensing.enforceableSSO}
|
{#if !$licensing.enforceableSSO}
|
||||||
<Tags>
|
<Tags>
|
||||||
<Tag icon="LockClosed">Enterprise</Tag>
|
<Tag icon="LockClosed">Enterprise plan</Tag>
|
||||||
</Tags>
|
</Tags>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<LockedFeature
|
<LockedFeature
|
||||||
title={"Environment Variables"}
|
title={"Environment Variables"}
|
||||||
planType={"Business plan"}
|
planType={"Enterprise plan"}
|
||||||
description={"Add and manage environment variables for development and production"}
|
description={"Add and manage environment variables for development and production"}
|
||||||
enabled={$licensing.environmentVariablesEnabled}
|
enabled={$licensing.environmentVariablesEnabled}
|
||||||
upgradeButtonClick={async () => {
|
upgradeButtonClick={async () => {
|
||||||
|
|
|
@ -23,7 +23,8 @@ export const createValidatorFromConstraints = (
|
||||||
// Required constraint
|
// Required constraint
|
||||||
if (
|
if (
|
||||||
field === table?.primaryDisplay ||
|
field === table?.primaryDisplay ||
|
||||||
schemaConstraints.presence?.allowEmpty === false
|
schemaConstraints.presence?.allowEmpty === false ||
|
||||||
|
schemaConstraints.presence === true
|
||||||
) {
|
) {
|
||||||
rules.push({
|
rules.push({
|
||||||
type: schemaConstraints.type == "array" ? "array" : "string",
|
type: schemaConstraints.type == "array" ? "array" : "string",
|
||||||
|
|
|
@ -68,9 +68,13 @@ COPY packages/server/builder/ builder/
|
||||||
COPY packages/server/client/ client/
|
COPY packages/server/client/ client/
|
||||||
|
|
||||||
ARG BUDIBASE_VERSION
|
ARG BUDIBASE_VERSION
|
||||||
|
ARG GIT_COMMIT_SHA
|
||||||
# Ensuring the version argument is sent
|
# Ensuring the version argument is sent
|
||||||
RUN test -n "$BUDIBASE_VERSION"
|
RUN test -n "$BUDIBASE_VERSION"
|
||||||
ENV BUDIBASE_VERSION=$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
|
EXPOSE 4001
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
#!/bin/node
|
||||||
|
const {
|
||||||
|
createApp,
|
||||||
|
getTable,
|
||||||
|
createRow,
|
||||||
|
createTable,
|
||||||
|
getApp,
|
||||||
|
getRows,
|
||||||
|
} = require("./utils")
|
||||||
|
|
||||||
|
const Chance = require("chance")
|
||||||
|
|
||||||
|
const generator = new Chance()
|
||||||
|
|
||||||
|
const STUDENT_COUNT = 500
|
||||||
|
const SUBJECT_COUNT = 10
|
||||||
|
|
||||||
|
let { apiKey, appId } = require("yargs")
|
||||||
|
.demandOption(["apiKey"])
|
||||||
|
.option("appId").argv
|
||||||
|
|
||||||
|
const start = Date.now()
|
||||||
|
async function batchCreate(apiKey, appId, table, items, batchSize = 100) {
|
||||||
|
let i = 0
|
||||||
|
let errors = 0
|
||||||
|
|
||||||
|
async function createSingleRow(item) {
|
||||||
|
try {
|
||||||
|
const row = await createRow(apiKey, appId, table, item)
|
||||||
|
console.log(
|
||||||
|
`${table.name} - ${++i} of ${items.length} created (${
|
||||||
|
(Date.now() - start) / 1000
|
||||||
|
}s)`
|
||||||
|
)
|
||||||
|
return row
|
||||||
|
} catch {
|
||||||
|
errors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = []
|
||||||
|
const maxConcurrency = Math.min(batchSize, items.length)
|
||||||
|
const inFlight = {}
|
||||||
|
|
||||||
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
||||||
|
const item = items[itemIndex]
|
||||||
|
const promise = createSingleRow(item)
|
||||||
|
.then(result => {
|
||||||
|
rows.push(result)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
delete inFlight[itemIndex]
|
||||||
|
})
|
||||||
|
|
||||||
|
inFlight[itemIndex] = promise
|
||||||
|
|
||||||
|
if (Object.keys(inFlight).length >= maxConcurrency) {
|
||||||
|
await Promise.race(Object.values(inFlight))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(Object.values(inFlight))
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
console.error(
|
||||||
|
`${table.name} - ${errors} creation errored (${
|
||||||
|
(Date.now() - start) / 1000
|
||||||
|
}s)`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
const useExistingApp = !!appId
|
||||||
|
|
||||||
|
async function upsertTable(appId, tableName, tableData) {
|
||||||
|
if (useExistingApp) {
|
||||||
|
return await getTable(apiKey, appId, tableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = await createTable(apiKey, appId, {
|
||||||
|
...tableData,
|
||||||
|
name: tableName,
|
||||||
|
})
|
||||||
|
return table
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
if (!appId) {
|
||||||
|
const app = appId ? await getApp(apiKey, appId) : await createApp(apiKey)
|
||||||
|
appId = app._id
|
||||||
|
|
||||||
|
console.log(`App created. Url: http://localhost:10000/builder/app/${appId}`)
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`App retrieved. Url: http://localhost:10000/builder/app/${appId}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const studentsTable = await getTable(apiKey, appId, "Students")
|
||||||
|
|
||||||
|
let studentNumber = studentsTable.schema["Auto ID"].lastID
|
||||||
|
const students = await batchCreate(
|
||||||
|
apiKey,
|
||||||
|
appId,
|
||||||
|
studentsTable,
|
||||||
|
Array.from({ length: STUDENT_COUNT }).map(() => ({
|
||||||
|
"Student Number": (++studentNumber).toString(),
|
||||||
|
"First Name": generator.first(),
|
||||||
|
"Last Name": generator.last(),
|
||||||
|
Gender: generator.pickone(["M", "F"]),
|
||||||
|
Grade: generator.pickone(["8", "9", "10", "11"]),
|
||||||
|
"Tardiness (Days)": generator.integer({ min: 1, max: 100 }),
|
||||||
|
"Home Number": generator.phone(),
|
||||||
|
"Attendance_(%)": generator.integer({ min: 0, max: 100 }),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const subjectTable = await upsertTable(appId, "Subjects", {
|
||||||
|
schema: {
|
||||||
|
Name: {
|
||||||
|
name: "Name",
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
primaryDisplay: "Name",
|
||||||
|
})
|
||||||
|
|
||||||
|
const subjects = useExistingApp
|
||||||
|
? await getRows(apiKey, appId, subjectTable._id)
|
||||||
|
: await batchCreate(
|
||||||
|
apiKey,
|
||||||
|
appId,
|
||||||
|
subjectTable,
|
||||||
|
Array.from({ length: SUBJECT_COUNT }).map(() => ({
|
||||||
|
Name: generator.profession(),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const gradesTable = await upsertTable(appId, "Grades", {
|
||||||
|
schema: {
|
||||||
|
Score: {
|
||||||
|
name: "Score",
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
Student: {
|
||||||
|
name: "Student",
|
||||||
|
tableId: studentsTable._id,
|
||||||
|
constraints: {
|
||||||
|
presence: true,
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
fieldName: "Grades",
|
||||||
|
relationshipType: "one-to-many",
|
||||||
|
type: "link",
|
||||||
|
},
|
||||||
|
Subject: {
|
||||||
|
name: "Subject",
|
||||||
|
tableId: subjectTable._id,
|
||||||
|
constraints: {
|
||||||
|
presence: true,
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
fieldName: "Grades",
|
||||||
|
relationshipType: "one-to-many",
|
||||||
|
type: "link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await batchCreate(
|
||||||
|
apiKey,
|
||||||
|
appId,
|
||||||
|
gradesTable,
|
||||||
|
students.flatMap(student =>
|
||||||
|
subjects.map(subject => ({
|
||||||
|
Score: generator.integer({ min: 0, max: 100 }),
|
||||||
|
Student: [student],
|
||||||
|
Subject: [subject],
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Access the app here: http://localhost:10000/builder/app/${appId}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
||||||
|
.then(() => {
|
||||||
|
console.log(`Done in ${(Date.now() - start) / 1000} seconds`)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/bin/node
|
||||||
|
const { searchApps, deleteApp } = require("./utils")
|
||||||
|
|
||||||
|
if (!process.argv[2]) {
|
||||||
|
console.error("Please specify an API key as script argument.")
|
||||||
|
process.exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
const apiKey = process.argv[2]
|
||||||
|
const apps = await searchApps(apiKey)
|
||||||
|
console.log(`Deleting ${apps.length} apps`)
|
||||||
|
|
||||||
|
let deletedApps = 0
|
||||||
|
await Promise.all(
|
||||||
|
apps.map(async app => {
|
||||||
|
await deleteApp(apiKey, app._id)
|
||||||
|
console.log(`App ${++deletedApps} of ${apps.length} deleted`)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
||||||
|
.then(() => {
|
||||||
|
console.log("Done!")
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
|
@ -2,7 +2,8 @@ const fetch = require("node-fetch")
|
||||||
const uuid = require("uuid/v4")
|
const uuid = require("uuid/v4")
|
||||||
|
|
||||||
const URL_APP = "http://localhost:10000/api/public/v1/applications"
|
const URL_APP = "http://localhost:10000/api/public/v1/applications"
|
||||||
const URL_TABLE = "http://localhost:10000/api/public/v1/tables/search"
|
const URL_TABLE = "http://localhost:10000/api/public/v1/tables"
|
||||||
|
const URL_SEARCH_TABLE = "http://localhost:10000/api/public/v1/tables/search"
|
||||||
|
|
||||||
async function request(apiKey, url, method, body, appId = undefined) {
|
async function request(apiKey, url, method, body, appId = undefined) {
|
||||||
const headers = {
|
const headers = {
|
||||||
|
@ -37,30 +38,64 @@ exports.createApp = async apiKey => {
|
||||||
return json.data
|
return json.data
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getTable = async (apiKey, appId) => {
|
exports.getApp = async (apiKey, appId) => {
|
||||||
const res = await request(apiKey, URL_TABLE, "POST", {}, appId)
|
const res = await request(apiKey, `${URL_APP}/${appId}`, "GET")
|
||||||
const json = await res.json()
|
const json = await res.json()
|
||||||
return json.data[0]
|
return json.data
|
||||||
|
}
|
||||||
|
exports.searchApps = async apiKey => {
|
||||||
|
const res = await request(apiKey, `${URL_APP}/search`, "POST", {})
|
||||||
|
const json = await res.json()
|
||||||
|
return json.data
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createRow = async (apiKey, appId, table) => {
|
exports.deleteApp = async (apiKey, appId) => {
|
||||||
const body = {}
|
const res = await request(apiKey, `${URL_APP}/${appId}`, "DELETE")
|
||||||
for (let [key, schema] of Object.entries(table.schema)) {
|
return res
|
||||||
let fake
|
}
|
||||||
switch (schema.type) {
|
|
||||||
default:
|
exports.getTable = async (apiKey, appId, tableName) => {
|
||||||
case "string":
|
const res = await request(apiKey, URL_SEARCH_TABLE, "POST", {}, appId)
|
||||||
fake = schema.constraints.inclusion
|
const json = await res.json()
|
||||||
? schema.constraints.inclusion[0]
|
const table = json.data.find(t => t.name === tableName)
|
||||||
: "a"
|
if (!table) {
|
||||||
break
|
throw `Table '${tableName} not found`
|
||||||
case "number":
|
}
|
||||||
fake = 1
|
return table
|
||||||
break
|
}
|
||||||
|
|
||||||
|
exports.createRow = async (apiKey, appId, table, body) => {
|
||||||
|
if (!body) {
|
||||||
|
body = {}
|
||||||
|
for (let [key, schema] of Object.entries(table.schema)) {
|
||||||
|
let fake
|
||||||
|
switch (schema.type) {
|
||||||
|
default:
|
||||||
|
case "string":
|
||||||
|
fake = schema.constraints?.inclusion
|
||||||
|
? schema.constraints.inclusion[0]
|
||||||
|
: "a"
|
||||||
|
break
|
||||||
|
case "number":
|
||||||
|
fake = 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
body[key] = fake
|
||||||
}
|
}
|
||||||
body[key] = fake
|
|
||||||
}
|
}
|
||||||
const url = `http://localhost:10000/api/public/v1/tables/${table._id}/rows`
|
const url = `http://localhost:10000/api/public/v1/tables/${table._id}/rows`
|
||||||
const res = await request(apiKey, url, "POST", body, appId)
|
const res = await request(apiKey, url, "POST", body, appId)
|
||||||
return (await res.json()).data
|
return (await res.json()).data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getRows = async (apiKey, appId, tableId) => {
|
||||||
|
const url = `${URL_TABLE}/${tableId}/rows/search`
|
||||||
|
const res = await request(apiKey, url, "POST", {}, appId)
|
||||||
|
return (await res.json()).data
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createTable = async (apiKey, appId, config) => {
|
||||||
|
const res = await request(apiKey, URL_TABLE, "POST", config, appId)
|
||||||
|
const json = await res.json()
|
||||||
|
return json.data
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import {
|
||||||
inputProcessing,
|
inputProcessing,
|
||||||
outputProcessing,
|
outputProcessing,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import { cloneDeep, isEqual } from "lodash"
|
import { cloneDeep } from "lodash"
|
||||||
|
|
||||||
export async function handleRequest<T extends Operation>(
|
export async function handleRequest<T extends Operation>(
|
||||||
operation: T,
|
operation: T,
|
||||||
|
@ -86,50 +86,6 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(ctx: UserCtx) {
|
|
||||||
const inputs = ctx.request.body
|
|
||||||
const tableId = utils.getTableId(ctx)
|
|
||||||
|
|
||||||
const table = await sdk.tables.getTable(tableId)
|
|
||||||
const { table: updatedTable, row } = await inputProcessing(
|
|
||||||
ctx.user?._id,
|
|
||||||
cloneDeep(table),
|
|
||||||
inputs
|
|
||||||
)
|
|
||||||
|
|
||||||
const validateResult = await sdk.rows.utils.validate({
|
|
||||||
row,
|
|
||||||
tableId,
|
|
||||||
})
|
|
||||||
if (!validateResult.valid) {
|
|
||||||
throw { validation: validateResult.errors }
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await handleRequest(Operation.CREATE, tableId, {
|
|
||||||
row,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!isEqual(table, updatedTable)) {
|
|
||||||
await sdk.tables.saveTable(updatedTable)
|
|
||||||
}
|
|
||||||
|
|
||||||
const rowId = response.row._id
|
|
||||||
if (rowId) {
|
|
||||||
const row = await sdk.rows.external.getRow(tableId, rowId, {
|
|
||||||
relationships: true,
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
...response,
|
|
||||||
row: await outputProcessing(table, row, {
|
|
||||||
preserveLinks: true,
|
|
||||||
squash: true,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function find(ctx: UserCtx): Promise<Row> {
|
export async function find(ctx: UserCtx): Promise<Row> {
|
||||||
const id = ctx.params.rowId
|
const id = ctx.params.rowId
|
||||||
const tableId = utils.getTableId(ctx)
|
const tableId = utils.getTableId(ctx)
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { Format } from "../view/exporters"
|
||||||
|
|
||||||
export * as views from "./views"
|
export * as views from "./views"
|
||||||
|
|
||||||
function pickApi(tableId: any) {
|
function pickApi(tableId: string) {
|
||||||
if (isExternalTableID(tableId)) {
|
if (isExternalTableID(tableId)) {
|
||||||
return external
|
return external
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,12 @@ export const save = async (ctx: UserCtx<Row, Row>) => {
|
||||||
return patch(ctx as UserCtx<PatchRowRequest, PatchRowResponse>)
|
return patch(ctx as UserCtx<PatchRowRequest, PatchRowResponse>)
|
||||||
}
|
}
|
||||||
const { row, table, squashed } = await quotas.addRow(() =>
|
const { row, table, squashed } = await quotas.addRow(() =>
|
||||||
quotas.addQuery(() => pickApi(tableId).save(ctx), {
|
quotas.addQuery(
|
||||||
datasourceId: tableId,
|
() => sdk.rows.save(tableId, ctx.request.body, ctx.user?._id),
|
||||||
})
|
{
|
||||||
|
datasourceId: tableId,
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as linkRows from "../../../db/linkedRows"
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
import { generateRowID, InternalTables } from "../../../db/utils"
|
import { InternalTables } from "../../../db/utils"
|
||||||
import * as userController from "../user"
|
import * as userController from "../user"
|
||||||
import {
|
import {
|
||||||
AttachmentCleanup,
|
AttachmentCleanup,
|
||||||
|
@ -94,45 +94,6 @@ export async function patch(ctx: UserCtx<PatchRowRequest, PatchRowResponse>) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(ctx: UserCtx) {
|
|
||||||
let inputs = ctx.request.body
|
|
||||||
inputs.tableId = utils.getTableId(ctx)
|
|
||||||
|
|
||||||
if (!inputs._rev && !inputs._id) {
|
|
||||||
inputs._id = generateRowID(inputs.tableId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// this returns the table and row incase they have been updated
|
|
||||||
const dbTable = await sdk.tables.getTable(inputs.tableId)
|
|
||||||
|
|
||||||
// need to copy the table so it can be differenced on way out
|
|
||||||
const tableClone = cloneDeep(dbTable)
|
|
||||||
|
|
||||||
let { table, row } = await inputProcessing(ctx.user?._id, tableClone, inputs)
|
|
||||||
|
|
||||||
const validateResult = await sdk.rows.utils.validate({
|
|
||||||
row,
|
|
||||||
table,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!validateResult.valid) {
|
|
||||||
throw { validation: validateResult.errors }
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure link rows are up-to-date
|
|
||||||
row = (await linkRows.updateLinks({
|
|
||||||
eventType: linkRows.EventType.ROW_SAVE,
|
|
||||||
row,
|
|
||||||
tableId: row.tableId,
|
|
||||||
table,
|
|
||||||
})) as Row
|
|
||||||
|
|
||||||
return finaliseRow(table, row, {
|
|
||||||
oldTable: dbTable,
|
|
||||||
updateFormula: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function find(ctx: UserCtx): Promise<Row> {
|
export async function find(ctx: UserCtx): Promise<Row> {
|
||||||
const tableId = utils.getTableId(ctx),
|
const tableId = utils.getTableId(ctx),
|
||||||
rowId = ctx.params.rowId
|
rowId = ctx.params.rowId
|
||||||
|
|
|
@ -5,8 +5,8 @@ import {
|
||||||
processFormulas,
|
processFormulas,
|
||||||
} from "../../../utilities/rowProcessor"
|
} from "../../../utilities/rowProcessor"
|
||||||
import { FieldTypes, FormulaTypes } from "../../../constants"
|
import { FieldTypes, FormulaTypes } from "../../../constants"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context, locks } from "@budibase/backend-core"
|
||||||
import { Table, Row } from "@budibase/types"
|
import { Table, Row, LockType, LockName } from "@budibase/types"
|
||||||
import * as linkRows from "../../../db/linkedRows"
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
|
@ -149,12 +149,22 @@ export async function finaliseRow(
|
||||||
await db.put(table)
|
await db.put(table)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.status === 409) {
|
if (err.status === 409) {
|
||||||
const updatedTable = await sdk.tables.getTable(table._id!)
|
// Some conflicts with the autocolumns occurred, we need to refetch the table and recalculate
|
||||||
let response = processAutoColumn(null, updatedTable, row, {
|
await locks.doWithLock(
|
||||||
reprocessing: true,
|
{
|
||||||
})
|
type: LockType.AUTO_EXTEND,
|
||||||
await db.put(response.table)
|
name: LockName.PROCESS_AUTO_COLUMNS,
|
||||||
row = response.row
|
resource: table._id,
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const latestTable = await sdk.tables.getTable(table._id!)
|
||||||
|
let response = processAutoColumn(null, latestTable, row, {
|
||||||
|
reprocessing: true,
|
||||||
|
})
|
||||||
|
await db.put(response.table)
|
||||||
|
row = response.row
|
||||||
|
}
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ const publicRouter = new Router({
|
||||||
prefix: PREFIX,
|
prefix: PREFIX,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (limiter) {
|
if (limiter && !env.isDev()) {
|
||||||
publicRouter.use(limiter)
|
publicRouter.use(limiter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2086,4 +2086,112 @@ describe.each([
|
||||||
expect(row.formula).toBe(relatedRow.name)
|
expect(row.formula).toBe(relatedRow.name)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("Formula JS protection", () => {
|
||||||
|
it("should time out JS execution if a single cell takes too long", async () => {
|
||||||
|
await config.withEnv({ JS_PER_EXECUTION_TIME_LIMIT_MS: 20 }, async () => {
|
||||||
|
const js = Buffer.from(
|
||||||
|
`
|
||||||
|
let i = 0;
|
||||||
|
while (true) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
`
|
||||||
|
).toString("base64")
|
||||||
|
|
||||||
|
const table = await config.createTable({
|
||||||
|
name: "table",
|
||||||
|
type: "table",
|
||||||
|
schema: {
|
||||||
|
text: {
|
||||||
|
name: "text",
|
||||||
|
type: FieldType.STRING,
|
||||||
|
},
|
||||||
|
formula: {
|
||||||
|
name: "formula",
|
||||||
|
type: FieldType.FORMULA,
|
||||||
|
formula: `{{ js "${js}"}}`,
|
||||||
|
formulaType: FormulaTypes.DYNAMIC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await config.api.row.save(table._id!, { text: "foo" })
|
||||||
|
const { rows } = await config.api.row.search(table._id!)
|
||||||
|
expect(rows).toHaveLength(1)
|
||||||
|
const row = rows[0]
|
||||||
|
expect(row.text).toBe("foo")
|
||||||
|
expect(row.formula).toBe("Timed out while executing JS")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should time out JS execution if a multiple cells take too long", async () => {
|
||||||
|
await config.withEnv(
|
||||||
|
{
|
||||||
|
JS_PER_EXECUTION_TIME_LIMIT_MS: 20,
|
||||||
|
JS_PER_REQUEST_TIME_LIMIT_MS: 40,
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const js = Buffer.from(
|
||||||
|
`
|
||||||
|
let i = 0;
|
||||||
|
while (true) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
`
|
||||||
|
).toString("base64")
|
||||||
|
|
||||||
|
const table = await config.createTable({
|
||||||
|
name: "table",
|
||||||
|
type: "table",
|
||||||
|
schema: {
|
||||||
|
text: {
|
||||||
|
name: "text",
|
||||||
|
type: FieldType.STRING,
|
||||||
|
},
|
||||||
|
formula: {
|
||||||
|
name: "formula",
|
||||||
|
type: FieldType.FORMULA,
|
||||||
|
formula: `{{ js "${js}"}}`,
|
||||||
|
formulaType: FormulaTypes.DYNAMIC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
await config.api.row.save(table._id!, { text: "foo" })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run this test 3 times to make sure that there's no cross-request
|
||||||
|
// pollution of the execution time tracking.
|
||||||
|
for (let reqs = 0; reqs < 3; reqs++) {
|
||||||
|
const { rows } = await config.api.row.search(table._id!)
|
||||||
|
expect(rows).toHaveLength(10)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (; i < 10; i++) {
|
||||||
|
const row = rows[i]
|
||||||
|
if (row.formula !== "Timed out while executing JS") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given the execution times are not deterministic, we can't be sure
|
||||||
|
// of the exact number of rows that were executed before the timeout
|
||||||
|
// but it should absolutely be at least 1.
|
||||||
|
expect(i).toBeGreaterThan(0)
|
||||||
|
expect(i).toBeLessThan(5)
|
||||||
|
|
||||||
|
for (; i < 10; i++) {
|
||||||
|
const row = rows[i]
|
||||||
|
expect(row.text).toBe("foo")
|
||||||
|
expect(row.formula).toBe("Request JS execution limit hit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -84,9 +84,11 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) {
|
||||||
|
|
||||||
// clear any undefined, null or empty string properties so that they aren't updated
|
// clear any undefined, null or empty string properties so that they aren't updated
|
||||||
for (let propKey of Object.keys(inputs.row)) {
|
for (let propKey of Object.keys(inputs.row)) {
|
||||||
|
const clearRelationships =
|
||||||
|
inputs.meta?.fields?.[propKey]?.clearRelationships
|
||||||
if (
|
if (
|
||||||
(inputs.row[propKey] == null || inputs.row[propKey] === "") &&
|
(inputs.row[propKey] == null || inputs.row[propKey]?.length === 0) &&
|
||||||
!inputs.meta?.fields?.[propKey]?.clearRelationships
|
!clearRelationships
|
||||||
) {
|
) {
|
||||||
delete inputs.row[propKey]
|
delete inputs.row[propKey]
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../sdk"
|
import sdk from "../sdk"
|
||||||
import { automationsEnabled } from "../features"
|
import { automationsEnabled } from "../features"
|
||||||
|
import tracer from "dd-trace"
|
||||||
|
|
||||||
const REBOOT_CRON = "@reboot"
|
const REBOOT_CRON = "@reboot"
|
||||||
const WH_STEP_ID = definitions.WEBHOOK.stepId
|
const WH_STEP_ID = definitions.WEBHOOK.stepId
|
||||||
|
@ -39,26 +40,62 @@ function loggingArgs(job: AutomationJob) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function processEvent(job: AutomationJob) {
|
export async function processEvent(job: AutomationJob) {
|
||||||
const appId = job.data.event.appId!
|
return tracer.trace(
|
||||||
const automationId = job.data.automation._id!
|
"processEvent",
|
||||||
const task = async () => {
|
{ resource: "automation" },
|
||||||
try {
|
async span => {
|
||||||
// need to actually await these so that an error can be captured properly
|
const appId = job.data.event.appId!
|
||||||
console.log("automation running", ...loggingArgs(job))
|
const automationId = job.data.automation._id!
|
||||||
|
|
||||||
const runFn = () => Runner.run(job)
|
span?.addTags({
|
||||||
const result = await quotas.addAutomation(runFn, {
|
appId,
|
||||||
automationId,
|
automationId,
|
||||||
|
job: {
|
||||||
|
id: job.id,
|
||||||
|
name: job.name,
|
||||||
|
attemptsMade: job.attemptsMade,
|
||||||
|
opts: {
|
||||||
|
attempts: job.opts.attempts,
|
||||||
|
priority: job.opts.priority,
|
||||||
|
delay: job.opts.delay,
|
||||||
|
repeat: job.opts.repeat,
|
||||||
|
backoff: job.opts.backoff,
|
||||||
|
lifo: job.opts.lifo,
|
||||||
|
timeout: job.opts.timeout,
|
||||||
|
jobId: job.opts.jobId,
|
||||||
|
removeOnComplete: job.opts.removeOnComplete,
|
||||||
|
removeOnFail: job.opts.removeOnFail,
|
||||||
|
stackTraceLimit: job.opts.stackTraceLimit,
|
||||||
|
preventParsingData: job.opts.preventParsingData,
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
console.log("automation completed", ...loggingArgs(job))
|
|
||||||
return result
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`automation was unable to run`, err, ...loggingArgs(job))
|
|
||||||
return { err }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await context.doInAutomationContext({ appId, automationId, task })
|
const task = async () => {
|
||||||
|
try {
|
||||||
|
// need to actually await these so that an error can be captured properly
|
||||||
|
console.log("automation running", ...loggingArgs(job))
|
||||||
|
|
||||||
|
const runFn = () => Runner.run(job)
|
||||||
|
const result = await quotas.addAutomation(runFn, {
|
||||||
|
automationId,
|
||||||
|
})
|
||||||
|
console.log("automation completed", ...loggingArgs(job))
|
||||||
|
return result
|
||||||
|
} catch (err) {
|
||||||
|
span?.addTags({ error: true })
|
||||||
|
console.error(
|
||||||
|
`automation was unable to run`,
|
||||||
|
err,
|
||||||
|
...loggingArgs(job)
|
||||||
|
)
|
||||||
|
return { err }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await context.doInAutomationContext({ appId, automationId, task })
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateTestHistory(
|
export async function updateTestHistory(
|
||||||
|
|
|
@ -56,7 +56,7 @@ export async function getLinkDocuments(args: {
|
||||||
try {
|
try {
|
||||||
let linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows
|
let linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows
|
||||||
// filter to get unique entries
|
// filter to get unique entries
|
||||||
const foundIds: string[] = []
|
const foundIds = new Set()
|
||||||
linkRows = linkRows.filter(link => {
|
linkRows = linkRows.filter(link => {
|
||||||
// make sure anything unique is the correct key
|
// make sure anything unique is the correct key
|
||||||
if (
|
if (
|
||||||
|
@ -65,9 +65,9 @@ export async function getLinkDocuments(args: {
|
||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const unique = foundIds.indexOf(link.id) === -1
|
const unique = !foundIds.has(link.id)
|
||||||
if (unique) {
|
if (unique) {
|
||||||
foundIds.push(link.id)
|
foundIds.add(link.id)
|
||||||
}
|
}
|
||||||
return unique
|
return unique
|
||||||
})
|
})
|
||||||
|
|
|
@ -70,6 +70,11 @@ const environment = {
|
||||||
SELF_HOSTED: process.env.SELF_HOSTED,
|
SELF_HOSTED: process.env.SELF_HOSTED,
|
||||||
HTTP_MB_LIMIT: process.env.HTTP_MB_LIMIT,
|
HTTP_MB_LIMIT: process.env.HTTP_MB_LIMIT,
|
||||||
FORKED_PROCESS_NAME: process.env.FORKED_PROCESS_NAME || "main",
|
FORKED_PROCESS_NAME: process.env.FORKED_PROCESS_NAME || "main",
|
||||||
|
JS_PER_EXECUTION_TIME_LIMIT_MS:
|
||||||
|
parseIntSafe(process.env.JS_PER_EXECUTION_TIME_LIMIT_MS) || 1000,
|
||||||
|
JS_PER_REQUEST_TIME_LIMIT_MS: parseIntSafe(
|
||||||
|
process.env.JS_PER_REQUEST_TIME_LIMIT_MS
|
||||||
|
),
|
||||||
// old
|
// old
|
||||||
CLIENT_ID: process.env.CLIENT_ID,
|
CLIENT_ID: process.env.CLIENT_ID,
|
||||||
_set(key: string, value: any) {
|
_set(key: string, value: any) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
ConnectionInfo,
|
ConnectionInfo,
|
||||||
|
Database,
|
||||||
DatasourceFeature,
|
DatasourceFeature,
|
||||||
DatasourceFieldType,
|
DatasourceFieldType,
|
||||||
Document,
|
Document,
|
||||||
|
@ -66,7 +67,7 @@ const SCHEMA: Integration = {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CouchDBIntegration implements IntegrationBase {
|
class CouchDBIntegration implements IntegrationBase {
|
||||||
private readonly client: dbCore.DatabaseImpl
|
private readonly client: Database
|
||||||
|
|
||||||
constructor(config: CouchDBConfig) {
|
constructor(config: CouchDBConfig) {
|
||||||
this.client = dbCore.DatabaseWithConnection(config.database, config.url)
|
this.client = dbCore.DatabaseWithConnection(config.database, config.url)
|
||||||
|
|
|
@ -131,7 +131,10 @@ class RestIntegration implements IntegrationBase {
|
||||||
let data, raw, headers
|
let data, raw, headers
|
||||||
const contentType = response.headers.get("content-type") || ""
|
const contentType = response.headers.get("content-type") || ""
|
||||||
try {
|
try {
|
||||||
if (contentType.includes("application/json")) {
|
if (response.status === 204) {
|
||||||
|
data = []
|
||||||
|
raw = []
|
||||||
|
} else if (contentType.includes("application/json")) {
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
raw = JSON.stringify(data)
|
raw = JSON.stringify(data)
|
||||||
} else if (
|
} else if (
|
||||||
|
|
|
@ -186,9 +186,15 @@ describe("REST Integration", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("response", () => {
|
describe("response", () => {
|
||||||
function buildInput(json: any, text: any, header: any) {
|
const contentTypes = ["application/json", "text/plain", "application/xml"]
|
||||||
|
function buildInput(
|
||||||
|
json: any,
|
||||||
|
text: any,
|
||||||
|
header: any,
|
||||||
|
status: number = 200
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
status: 200,
|
status,
|
||||||
json: json ? async () => json : undefined,
|
json: json ? async () => json : undefined,
|
||||||
text: text ? async () => text : undefined,
|
text: text ? async () => text : undefined,
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -225,6 +231,18 @@ describe("REST Integration", () => {
|
||||||
expect(output.extra.raw).toEqual(text)
|
expect(output.extra.raw).toEqual(text)
|
||||||
expect(output.extra.headers["content-type"]).toEqual("application/xml")
|
expect(output.extra.headers["content-type"]).toEqual("application/xml")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.each(contentTypes)(
|
||||||
|
"should not throw an error on 204 no content",
|
||||||
|
async contentType => {
|
||||||
|
const input = buildInput(undefined, null, contentType, 204)
|
||||||
|
const output = await config.integration.parseResponse(input)
|
||||||
|
expect(output.data).toEqual([])
|
||||||
|
expect(output.extra.raw).toEqual([])
|
||||||
|
expect(output.info.code).toEqual(204)
|
||||||
|
expect(output.extra.headers["content-type"]).toEqual(contentType)
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("authentication", () => {
|
describe("authentication", () => {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import vm from "vm"
|
||||||
|
import env from "./environment"
|
||||||
|
import { setJSRunner } from "@budibase/string-templates"
|
||||||
|
import { context, timers } from "@budibase/backend-core"
|
||||||
|
import tracer from "dd-trace"
|
||||||
|
|
||||||
|
type TrackerFn = <T>(f: () => T) => T
|
||||||
|
|
||||||
|
export function init() {
|
||||||
|
setJSRunner((js: string, ctx: vm.Context) => {
|
||||||
|
return tracer.trace("runJS", {}, span => {
|
||||||
|
const perRequestLimit = env.JS_PER_REQUEST_TIME_LIMIT_MS
|
||||||
|
let track: TrackerFn = f => f()
|
||||||
|
if (perRequestLimit) {
|
||||||
|
const bbCtx = context.getCurrentContext()
|
||||||
|
if (bbCtx) {
|
||||||
|
if (!bbCtx.jsExecutionTracker) {
|
||||||
|
bbCtx.jsExecutionTracker =
|
||||||
|
timers.ExecutionTimeTracker.withLimit(perRequestLimit)
|
||||||
|
}
|
||||||
|
track = bbCtx.jsExecutionTracker.track.bind(bbCtx.jsExecutionTracker)
|
||||||
|
span?.addTags({
|
||||||
|
js: {
|
||||||
|
limitMS: bbCtx.jsExecutionTracker.limitMs,
|
||||||
|
elapsedMS: bbCtx.jsExecutionTracker.elapsedMS,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
...ctx,
|
||||||
|
alert: undefined,
|
||||||
|
setInterval: undefined,
|
||||||
|
setTimeout: undefined,
|
||||||
|
}
|
||||||
|
vm.createContext(ctx)
|
||||||
|
return track(() =>
|
||||||
|
vm.runInNewContext(js, ctx, {
|
||||||
|
timeout: env.JS_PER_EXECUTION_TIME_LIMIT_MS,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import { getCachedSelf } from "../utilities/global"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { isWebhookEndpoint } from "./utils"
|
import { isWebhookEndpoint } from "./utils"
|
||||||
import { UserCtx, ContextUser } from "@budibase/types"
|
import { UserCtx, ContextUser } from "@budibase/types"
|
||||||
|
import tracer from "dd-trace"
|
||||||
|
|
||||||
export default async (ctx: UserCtx, next: any) => {
|
export default async (ctx: UserCtx, next: any) => {
|
||||||
// try to get the appID from the request
|
// try to get the appID from the request
|
||||||
|
@ -20,6 +21,11 @@ export default async (ctx: UserCtx, next: any) => {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requestAppId) {
|
||||||
|
const span = tracer.scope().active()
|
||||||
|
span?.setTag("appId", requestAppId)
|
||||||
|
}
|
||||||
|
|
||||||
// deny access to application preview
|
// deny access to application preview
|
||||||
if (!env.isTest()) {
|
if (!env.isTest()) {
|
||||||
if (
|
if (
|
||||||
|
@ -70,6 +76,14 @@ export default async (ctx: UserCtx, next: any) => {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.user) {
|
||||||
|
const span = tracer.scope().active()
|
||||||
|
if (ctx.user._id) {
|
||||||
|
span?.setTag("userId", ctx.user._id)
|
||||||
|
}
|
||||||
|
span?.setTag("tenantId", ctx.user.tenantId)
|
||||||
|
}
|
||||||
|
|
||||||
const userId = ctx.user ? generateUserMetadataID(ctx.user._id!) : undefined
|
const userId = ctx.user ? generateUserMetadataID(ctx.user._id!) : undefined
|
||||||
|
|
||||||
// if the user is not in the right tenant then make sure to wipe their cookie
|
// if the user is not in the right tenant then make sure to wipe their cookie
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
import { IncludeRelationship, Operation } from "@budibase/types"
|
import { IncludeRelationship, Operation, Row } from "@budibase/types"
|
||||||
import { handleRequest } from "../../../api/controllers/row/external"
|
import { handleRequest } from "../../../api/controllers/row/external"
|
||||||
import { breakRowIdField } from "../../../integrations/utils"
|
import { breakRowIdField } from "../../../integrations/utils"
|
||||||
|
import sdk from "../../../sdk"
|
||||||
|
import {
|
||||||
|
inputProcessing,
|
||||||
|
outputProcessing,
|
||||||
|
} from "../../../utilities/rowProcessor"
|
||||||
|
import cloneDeep from "lodash/fp/cloneDeep"
|
||||||
|
import isEqual from "lodash/fp/isEqual"
|
||||||
|
|
||||||
export async function getRow(
|
export async function getRow(
|
||||||
tableId: string,
|
tableId: string,
|
||||||
|
@ -15,3 +22,48 @@ export async function getRow(
|
||||||
})
|
})
|
||||||
return response ? response[0] : response
|
return response ? response[0] : response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function save(
|
||||||
|
tableId: string,
|
||||||
|
inputs: Row,
|
||||||
|
userId: string | undefined
|
||||||
|
) {
|
||||||
|
const table = await sdk.tables.getTable(tableId)
|
||||||
|
const { table: updatedTable, row } = await inputProcessing(
|
||||||
|
userId,
|
||||||
|
cloneDeep(table),
|
||||||
|
inputs
|
||||||
|
)
|
||||||
|
|
||||||
|
const validateResult = await sdk.rows.utils.validate({
|
||||||
|
row,
|
||||||
|
tableId,
|
||||||
|
})
|
||||||
|
if (!validateResult.valid) {
|
||||||
|
throw { validation: validateResult.errors }
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await handleRequest(Operation.CREATE, tableId, {
|
||||||
|
row,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!isEqual(table, updatedTable)) {
|
||||||
|
await sdk.tables.saveTable(updatedTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowId = response.row._id
|
||||||
|
if (rowId) {
|
||||||
|
const row = await sdk.rows.external.getRow(tableId, rowId, {
|
||||||
|
relationships: true,
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
row: await outputProcessing(table, row, {
|
||||||
|
preserveLinks: true,
|
||||||
|
squash: true,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { db } from "@budibase/backend-core"
|
||||||
|
import { Row } from "@budibase/types"
|
||||||
|
import sdk from "../../../sdk"
|
||||||
|
import cloneDeep from "lodash/fp/cloneDeep"
|
||||||
|
import { finaliseRow } from "../../../api/controllers/row/staticFormula"
|
||||||
|
import { inputProcessing } from "../../../utilities/rowProcessor"
|
||||||
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
|
|
||||||
|
export async function save(
|
||||||
|
tableId: string,
|
||||||
|
inputs: Row,
|
||||||
|
userId: string | undefined
|
||||||
|
) {
|
||||||
|
inputs.tableId = tableId
|
||||||
|
|
||||||
|
if (!inputs._rev && !inputs._id) {
|
||||||
|
inputs._id = db.generateRowID(inputs.tableId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this returns the table and row incase they have been updated
|
||||||
|
const dbTable = await sdk.tables.getTable(inputs.tableId)
|
||||||
|
|
||||||
|
// need to copy the table so it can be differenced on way out
|
||||||
|
const tableClone = cloneDeep(dbTable)
|
||||||
|
|
||||||
|
let { table, row } = await inputProcessing(userId, tableClone, inputs)
|
||||||
|
|
||||||
|
const validateResult = await sdk.rows.utils.validate({
|
||||||
|
row,
|
||||||
|
table,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!validateResult.valid) {
|
||||||
|
throw { validation: validateResult.errors }
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure link rows are up-to-date
|
||||||
|
row = (await linkRows.updateLinks({
|
||||||
|
eventType: linkRows.EventType.ROW_SAVE,
|
||||||
|
row,
|
||||||
|
tableId: row.tableId,
|
||||||
|
table,
|
||||||
|
})) as Row
|
||||||
|
|
||||||
|
return finaliseRow(table, row, {
|
||||||
|
oldTable: dbTable,
|
||||||
|
updateFormula: true,
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
import { db as dbCore, context } from "@budibase/backend-core"
|
import { db as dbCore, context } from "@budibase/backend-core"
|
||||||
import { Database, Row } from "@budibase/types"
|
import { Database, Row } from "@budibase/types"
|
||||||
import { getRowParams } from "../../../db/utils"
|
import { getRowParams } from "../../../db/utils"
|
||||||
|
import { isExternalTableID } from "../../../integrations/utils"
|
||||||
|
import * as internal from "./internal"
|
||||||
|
import * as external from "./external"
|
||||||
|
|
||||||
export async function getAllInternalRows(appId?: string) {
|
export async function getAllInternalRows(appId?: string) {
|
||||||
let db: Database
|
let db: Database
|
||||||
|
@ -16,3 +19,18 @@ export async function getAllInternalRows(appId?: string) {
|
||||||
)
|
)
|
||||||
return response.rows.map(row => row.doc) as Row[]
|
return response.rows.map(row => row.doc) as Row[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pickApi(tableId: any) {
|
||||||
|
if (isExternalTableID(tableId)) {
|
||||||
|
return external
|
||||||
|
}
|
||||||
|
return internal
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function save(
|
||||||
|
tableId: string,
|
||||||
|
row: Row,
|
||||||
|
userId: string | undefined
|
||||||
|
) {
|
||||||
|
return pickApi(tableId).save(tableId, row, userId)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
import tk from "timekeeper"
|
||||||
|
import * as internalSdk from "../internal"
|
||||||
|
|
||||||
|
import { generator } from "@budibase/backend-core/tests"
|
||||||
|
import {
|
||||||
|
INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
TableSourceType,
|
||||||
|
FieldType,
|
||||||
|
Table,
|
||||||
|
AutoFieldSubTypes,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||||
|
import { cache } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
tk.freeze(Date.now())
|
||||||
|
|
||||||
|
describe("sdk >> rows >> internal", () => {
|
||||||
|
const config = new TestConfiguration()
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
function makeRow() {
|
||||||
|
return {
|
||||||
|
name: generator.first(),
|
||||||
|
surname: generator.last(),
|
||||||
|
age: generator.age(),
|
||||||
|
address: generator.address(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("save", () => {
|
||||||
|
const tableData: Table = {
|
||||||
|
name: generator.word(),
|
||||||
|
type: "table",
|
||||||
|
sourceId: INTERNAL_TABLE_SOURCE_ID,
|
||||||
|
sourceType: TableSourceType.INTERNAL,
|
||||||
|
schema: {
|
||||||
|
name: {
|
||||||
|
name: "name",
|
||||||
|
type: FieldType.STRING,
|
||||||
|
constraints: {
|
||||||
|
type: FieldType.STRING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
surname: {
|
||||||
|
name: "surname",
|
||||||
|
type: FieldType.STRING,
|
||||||
|
constraints: {
|
||||||
|
type: FieldType.STRING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
age: {
|
||||||
|
name: "age",
|
||||||
|
type: FieldType.NUMBER,
|
||||||
|
constraints: {
|
||||||
|
type: FieldType.NUMBER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
name: "address",
|
||||||
|
type: FieldType.STRING,
|
||||||
|
constraints: {
|
||||||
|
type: FieldType.STRING,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("save will persist the row properly", async () => {
|
||||||
|
const table = await config.createTable(tableData)
|
||||||
|
const row = makeRow()
|
||||||
|
|
||||||
|
await config.doInContext(config.appId, async () => {
|
||||||
|
const response = await internalSdk.save(
|
||||||
|
table._id!,
|
||||||
|
row,
|
||||||
|
config.user._id
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).toEqual({
|
||||||
|
table,
|
||||||
|
row: {
|
||||||
|
...row,
|
||||||
|
type: "row",
|
||||||
|
_rev: expect.stringMatching("1-.*"),
|
||||||
|
},
|
||||||
|
squashed: {
|
||||||
|
...row,
|
||||||
|
type: "row",
|
||||||
|
_rev: expect.stringMatching("1-.*"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const persistedRow = await config.getRow(table._id!, response.row._id!)
|
||||||
|
expect(persistedRow).toEqual({
|
||||||
|
...row,
|
||||||
|
type: "row",
|
||||||
|
_rev: expect.stringMatching("1-.*"),
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
updatedAt: expect.any(String),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("auto ids will update when creating new rows", async () => {
|
||||||
|
const table = await config.createTable({
|
||||||
|
...tableData,
|
||||||
|
schema: {
|
||||||
|
...tableData.schema,
|
||||||
|
id: {
|
||||||
|
name: "id",
|
||||||
|
type: FieldType.AUTO,
|
||||||
|
subtype: AutoFieldSubTypes.AUTO_ID,
|
||||||
|
autocolumn: true,
|
||||||
|
lastID: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const row = makeRow()
|
||||||
|
|
||||||
|
await config.doInContext(config.appId, async () => {
|
||||||
|
const response = await internalSdk.save(
|
||||||
|
table._id!,
|
||||||
|
row,
|
||||||
|
config.user._id
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).toEqual({
|
||||||
|
table: {
|
||||||
|
...table,
|
||||||
|
schema: {
|
||||||
|
...table.schema,
|
||||||
|
id: {
|
||||||
|
...table.schema.id,
|
||||||
|
lastID: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
...row,
|
||||||
|
id: 1,
|
||||||
|
type: "row",
|
||||||
|
_rev: expect.stringMatching("1-.*"),
|
||||||
|
},
|
||||||
|
squashed: {
|
||||||
|
...row,
|
||||||
|
id: 1,
|
||||||
|
type: "row",
|
||||||
|
_rev: expect.stringMatching("1-.*"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const persistedRow = await config.getRow(table._id!, response.row._id!)
|
||||||
|
expect(persistedRow).toEqual({
|
||||||
|
...row,
|
||||||
|
type: "row",
|
||||||
|
id: 1,
|
||||||
|
_rev: expect.stringMatching("1-.*"),
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
updatedAt: expect.any(String),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("auto ids will update when creating new rows in parallel", async () => {
|
||||||
|
function makeRows(count: number) {
|
||||||
|
return Array.from({ length: count }, () => makeRow())
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = await config.createTable({
|
||||||
|
...tableData,
|
||||||
|
schema: {
|
||||||
|
...tableData.schema,
|
||||||
|
id: {
|
||||||
|
name: "id",
|
||||||
|
type: FieldType.AUTO,
|
||||||
|
subtype: AutoFieldSubTypes.AUTO_ID,
|
||||||
|
autocolumn: true,
|
||||||
|
lastID: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await config.doInContext(config.appId, async () => {
|
||||||
|
for (const row of makeRows(5)) {
|
||||||
|
await internalSdk.save(table._id!, row, config.user._id)
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
makeRows(10).map(row =>
|
||||||
|
internalSdk.save(table._id!, row, config.user._id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for (const row of makeRows(5)) {
|
||||||
|
await internalSdk.save(table._id!, row, config.user._id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const persistedRows = await config.getRows(table._id!)
|
||||||
|
expect(persistedRows).toHaveLength(20)
|
||||||
|
expect(persistedRows).toEqual(
|
||||||
|
expect.arrayContaining(
|
||||||
|
Array.from({ length: 20 }).map((_, i) =>
|
||||||
|
expect.objectContaining({ id: i + 1 })
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const persistedTable = await config.getTable(table._id)
|
||||||
|
expect((table as any).schema.id.lastID).toBe(0)
|
||||||
|
expect(persistedTable.schema.id.lastID).toBe(20)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -23,6 +23,7 @@ import { automationsEnabled, printFeatures } from "./features"
|
||||||
import Koa from "koa"
|
import Koa from "koa"
|
||||||
import { Server } from "http"
|
import { Server } from "http"
|
||||||
import { AddressInfo } from "net"
|
import { AddressInfo } from "net"
|
||||||
|
import * as jsRunner from "./jsRunner"
|
||||||
|
|
||||||
let STARTUP_RAN = false
|
let STARTUP_RAN = false
|
||||||
|
|
||||||
|
@ -152,4 +153,6 @@ export async function startup(app?: Koa, server?: Server) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jsRunner.init()
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { cloneDeep } from "lodash/fp"
|
||||||
import { performance } from "perf_hooks"
|
import { performance } from "perf_hooks"
|
||||||
import * as sdkUtils from "../sdk/utils"
|
import * as sdkUtils from "../sdk/utils"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
|
import tracer from "dd-trace"
|
||||||
|
|
||||||
threadUtils.threadSetup()
|
threadUtils.threadSetup()
|
||||||
const FILTER_STEP_ID = actions.BUILTIN_ACTION_DEFINITIONS.FILTER.stepId
|
const FILTER_STEP_ID = actions.BUILTIN_ACTION_DEFINITIONS.FILTER.stepId
|
||||||
|
@ -242,278 +243,347 @@ class Orchestrator {
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(): Promise<any> {
|
async execute(): Promise<any> {
|
||||||
// this will retrieve from context created at start of thread
|
return tracer.trace(
|
||||||
this._context.env = await sdkUtils.getEnvironmentVariables()
|
"Orchestrator.execute",
|
||||||
let automation = this._automation
|
{ resource: "automation" },
|
||||||
let stopped = false
|
async span => {
|
||||||
let loopStep: AutomationStep | undefined = undefined
|
span?.addTags({
|
||||||
|
appId: this._appId,
|
||||||
|
automationId: this._automation._id,
|
||||||
|
})
|
||||||
|
|
||||||
let stepCount = 0
|
// this will retrieve from context created at start of thread
|
||||||
let loopStepNumber: any = undefined
|
this._context.env = await sdkUtils.getEnvironmentVariables()
|
||||||
let loopSteps: LoopStep[] | undefined = []
|
let automation = this._automation
|
||||||
let metadata
|
let stopped = false
|
||||||
let timeoutFlag = false
|
let loopStep: AutomationStep | undefined = undefined
|
||||||
let wasLoopStep = false
|
|
||||||
let timeout = this._job.data.event.timeout
|
|
||||||
// check if this is a recurring automation,
|
|
||||||
if (isProdAppID(this._appId) && isRecurring(automation)) {
|
|
||||||
metadata = await this.getMetadata()
|
|
||||||
const shouldStop = await this.checkIfShouldStop(metadata)
|
|
||||||
if (shouldStop) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const start = performance.now()
|
|
||||||
for (let step of automation.definition.steps) {
|
|
||||||
if (timeoutFlag) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout) {
|
let stepCount = 0
|
||||||
setTimeout(() => {
|
let loopStepNumber: any = undefined
|
||||||
timeoutFlag = true
|
let loopSteps: LoopStep[] | undefined = []
|
||||||
}, timeout || 12000)
|
let metadata
|
||||||
}
|
let timeoutFlag = false
|
||||||
|
let wasLoopStep = false
|
||||||
stepCount++
|
let timeout = this._job.data.event.timeout
|
||||||
let input: any,
|
// check if this is a recurring automation,
|
||||||
iterations = 1,
|
if (isProdAppID(this._appId) && isRecurring(automation)) {
|
||||||
iterationCount = 0
|
span?.addTags({ recurring: true })
|
||||||
|
metadata = await this.getMetadata()
|
||||||
if (step.stepId === LOOP_STEP_ID) {
|
const shouldStop = await this.checkIfShouldStop(metadata)
|
||||||
loopStep = step
|
if (shouldStop) {
|
||||||
loopStepNumber = stepCount
|
span?.addTags({ shouldStop: true })
|
||||||
continue
|
return
|
||||||
}
|
|
||||||
|
|
||||||
if (loopStep) {
|
|
||||||
input = await processObject(loopStep.inputs, this._context)
|
|
||||||
iterations = getLoopIterations(loopStep as LoopStep)
|
|
||||||
}
|
|
||||||
for (let index = 0; index < iterations; index++) {
|
|
||||||
let originalStepInput = cloneDeep(step.inputs)
|
|
||||||
// Handle if the user has set a max iteration count or if it reaches the max limit set by us
|
|
||||||
if (loopStep && input.binding) {
|
|
||||||
let tempOutput = { items: loopSteps, iterations: iterationCount }
|
|
||||||
try {
|
|
||||||
loopStep.inputs.binding = automationUtils.typecastForLooping(
|
|
||||||
loopStep as LoopStep,
|
|
||||||
loopStep.inputs as LoopInput
|
|
||||||
)
|
|
||||||
} catch (err) {
|
|
||||||
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
|
||||||
status: AutomationErrors.INCORRECT_TYPE,
|
|
||||||
success: false,
|
|
||||||
})
|
|
||||||
loopSteps = undefined
|
|
||||||
loopStep = undefined
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let item = []
|
|
||||||
if (
|
|
||||||
typeof loopStep.inputs.binding === "string" &&
|
|
||||||
loopStep.inputs.option === "String"
|
|
||||||
) {
|
|
||||||
item = automationUtils.stringSplit(loopStep.inputs.binding)
|
|
||||||
} else if (Array.isArray(loopStep.inputs.binding)) {
|
|
||||||
item = loopStep.inputs.binding
|
|
||||||
}
|
|
||||||
this._context.steps[loopStepNumber] = {
|
|
||||||
currentItem: item[index],
|
|
||||||
}
|
|
||||||
|
|
||||||
// The "Loop" binding in the front end is "fake", so replace it here so the context can understand it
|
|
||||||
// Pretty hacky because we need to account for the row object
|
|
||||||
for (let [key, value] of Object.entries(originalStepInput)) {
|
|
||||||
if (typeof value === "object") {
|
|
||||||
for (let [innerKey, innerValue] of Object.entries(
|
|
||||||
originalStepInput[key]
|
|
||||||
)) {
|
|
||||||
if (typeof innerValue === "string") {
|
|
||||||
originalStepInput[key][innerKey] =
|
|
||||||
automationUtils.substituteLoopStep(
|
|
||||||
innerValue,
|
|
||||||
`steps.${loopStepNumber}`
|
|
||||||
)
|
|
||||||
} else if (typeof value === "object") {
|
|
||||||
for (let [innerObject, innerValue] of Object.entries(
|
|
||||||
originalStepInput[key][innerKey]
|
|
||||||
)) {
|
|
||||||
originalStepInput[key][innerKey][innerObject] =
|
|
||||||
automationUtils.substituteLoopStep(
|
|
||||||
innerValue as string,
|
|
||||||
`steps.${loopStepNumber}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeof value === "string") {
|
|
||||||
originalStepInput[key] = automationUtils.substituteLoopStep(
|
|
||||||
value,
|
|
||||||
`steps.${loopStepNumber}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
index === env.AUTOMATION_MAX_ITERATIONS ||
|
|
||||||
index === parseInt(loopStep.inputs.iterations)
|
|
||||||
) {
|
|
||||||
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
|
||||||
status: AutomationErrors.MAX_ITERATIONS,
|
|
||||||
success: true,
|
|
||||||
})
|
|
||||||
loopSteps = undefined
|
|
||||||
loopStep = undefined
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
let isFailure = false
|
|
||||||
const currentItem = this._context.steps[loopStepNumber]?.currentItem
|
|
||||||
if (currentItem && typeof currentItem === "object") {
|
|
||||||
isFailure = Object.keys(currentItem).some(value => {
|
|
||||||
return currentItem[value] === loopStep?.inputs.failure
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
isFailure = currentItem && currentItem === loopStep.inputs.failure
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFailure) {
|
|
||||||
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
|
||||||
status: AutomationErrors.FAILURE_CONDITION,
|
|
||||||
success: false,
|
|
||||||
})
|
|
||||||
loopSteps = undefined
|
|
||||||
loopStep = undefined
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const start = performance.now()
|
||||||
// execution stopped, record state for that
|
for (let step of automation.definition.steps) {
|
||||||
if (stopped) {
|
const stepSpan = tracer.startSpan("Orchestrator.execute.step", {
|
||||||
this.updateExecutionOutput(step.id, step.stepId, {}, STOPPED_STATUS)
|
childOf: span,
|
||||||
continue
|
})
|
||||||
}
|
stepSpan.addTags({
|
||||||
|
resource: "automation",
|
||||||
// If it's a loop step, we need to manually add the bindings to the context
|
step: {
|
||||||
let stepFn = await this.getStepFunctionality(step.stepId)
|
stepId: step.stepId,
|
||||||
let inputs = await processObject(originalStepInput, this._context)
|
id: step.id,
|
||||||
inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs)
|
name: step.name,
|
||||||
|
type: step.type,
|
||||||
try {
|
title: step.stepTitle,
|
||||||
// appId is always passed
|
internal: step.internal,
|
||||||
const outputs = await stepFn({
|
deprecated: step.deprecated,
|
||||||
inputs: inputs,
|
},
|
||||||
appId: this._appId,
|
|
||||||
emitter: this._emitter,
|
|
||||||
context: this._context,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this._context.steps[stepCount] = outputs
|
let input: any,
|
||||||
// if filter causes us to stop execution don't break the loop, set a var
|
iterations = 1,
|
||||||
// so that we can finish iterating through the steps and record that it stopped
|
iterationCount = 0
|
||||||
if (step.stepId === FILTER_STEP_ID && !outputs.result) {
|
|
||||||
stopped = true
|
|
||||||
this.updateExecutionOutput(step.id, step.stepId, step.inputs, {
|
|
||||||
...outputs,
|
|
||||||
...STOPPED_STATUS,
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (loopStep && loopSteps) {
|
|
||||||
loopSteps.push(outputs)
|
|
||||||
} else {
|
|
||||||
this.updateExecutionOutput(
|
|
||||||
step.id,
|
|
||||||
step.stepId,
|
|
||||||
step.inputs,
|
|
||||||
outputs
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Automation error - ${step.stepId} - ${err}`)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loopStep) {
|
try {
|
||||||
iterationCount++
|
if (timeoutFlag) {
|
||||||
if (index === iterations - 1) {
|
span?.addTags({ timedOut: true })
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
setTimeout(() => {
|
||||||
|
timeoutFlag = true
|
||||||
|
}, timeout || 12000)
|
||||||
|
}
|
||||||
|
|
||||||
|
stepCount++
|
||||||
|
if (step.stepId === LOOP_STEP_ID) {
|
||||||
|
loopStep = step
|
||||||
|
loopStepNumber = stepCount
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loopStep) {
|
||||||
|
input = await processObject(loopStep.inputs, this._context)
|
||||||
|
iterations = getLoopIterations(loopStep as LoopStep)
|
||||||
|
stepSpan?.addTags({ step: { iterations } })
|
||||||
|
}
|
||||||
|
for (let index = 0; index < iterations; index++) {
|
||||||
|
let originalStepInput = cloneDeep(step.inputs)
|
||||||
|
// Handle if the user has set a max iteration count or if it reaches the max limit set by us
|
||||||
|
if (loopStep && input.binding) {
|
||||||
|
let tempOutput = {
|
||||||
|
items: loopSteps,
|
||||||
|
iterations: iterationCount,
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
loopStep.inputs.binding = automationUtils.typecastForLooping(
|
||||||
|
loopStep as LoopStep,
|
||||||
|
loopStep.inputs as LoopInput
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
this.updateContextAndOutput(
|
||||||
|
loopStepNumber,
|
||||||
|
step,
|
||||||
|
tempOutput,
|
||||||
|
{
|
||||||
|
status: AutomationErrors.INCORRECT_TYPE,
|
||||||
|
success: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
loopSteps = undefined
|
||||||
|
loopStep = undefined
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let item = []
|
||||||
|
if (
|
||||||
|
typeof loopStep.inputs.binding === "string" &&
|
||||||
|
loopStep.inputs.option === "String"
|
||||||
|
) {
|
||||||
|
item = automationUtils.stringSplit(loopStep.inputs.binding)
|
||||||
|
} else if (Array.isArray(loopStep.inputs.binding)) {
|
||||||
|
item = loopStep.inputs.binding
|
||||||
|
}
|
||||||
|
this._context.steps[loopStepNumber] = {
|
||||||
|
currentItem: item[index],
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "Loop" binding in the front end is "fake", so replace it here so the context can understand it
|
||||||
|
// Pretty hacky because we need to account for the row object
|
||||||
|
for (let [key, value] of Object.entries(originalStepInput)) {
|
||||||
|
if (typeof value === "object") {
|
||||||
|
for (let [innerKey, innerValue] of Object.entries(
|
||||||
|
originalStepInput[key]
|
||||||
|
)) {
|
||||||
|
if (typeof innerValue === "string") {
|
||||||
|
originalStepInput[key][innerKey] =
|
||||||
|
automationUtils.substituteLoopStep(
|
||||||
|
innerValue,
|
||||||
|
`steps.${loopStepNumber}`
|
||||||
|
)
|
||||||
|
} else if (typeof value === "object") {
|
||||||
|
for (let [innerObject, innerValue] of Object.entries(
|
||||||
|
originalStepInput[key][innerKey]
|
||||||
|
)) {
|
||||||
|
originalStepInput[key][innerKey][innerObject] =
|
||||||
|
automationUtils.substituteLoopStep(
|
||||||
|
innerValue as string,
|
||||||
|
`steps.${loopStepNumber}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (typeof value === "string") {
|
||||||
|
originalStepInput[key] =
|
||||||
|
automationUtils.substituteLoopStep(
|
||||||
|
value,
|
||||||
|
`steps.${loopStepNumber}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
index === env.AUTOMATION_MAX_ITERATIONS ||
|
||||||
|
index === parseInt(loopStep.inputs.iterations)
|
||||||
|
) {
|
||||||
|
this.updateContextAndOutput(
|
||||||
|
loopStepNumber,
|
||||||
|
step,
|
||||||
|
tempOutput,
|
||||||
|
{
|
||||||
|
status: AutomationErrors.MAX_ITERATIONS,
|
||||||
|
success: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
loopSteps = undefined
|
||||||
|
loopStep = undefined
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let isFailure = false
|
||||||
|
const currentItem =
|
||||||
|
this._context.steps[loopStepNumber]?.currentItem
|
||||||
|
if (currentItem && typeof currentItem === "object") {
|
||||||
|
isFailure = Object.keys(currentItem).some(value => {
|
||||||
|
return currentItem[value] === loopStep?.inputs.failure
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
isFailure =
|
||||||
|
currentItem && currentItem === loopStep.inputs.failure
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFailure) {
|
||||||
|
this.updateContextAndOutput(
|
||||||
|
loopStepNumber,
|
||||||
|
step,
|
||||||
|
tempOutput,
|
||||||
|
{
|
||||||
|
status: AutomationErrors.FAILURE_CONDITION,
|
||||||
|
success: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
loopSteps = undefined
|
||||||
|
loopStep = undefined
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// execution stopped, record state for that
|
||||||
|
if (stopped) {
|
||||||
|
this.updateExecutionOutput(
|
||||||
|
step.id,
|
||||||
|
step.stepId,
|
||||||
|
{},
|
||||||
|
STOPPED_STATUS
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a loop step, we need to manually add the bindings to the context
|
||||||
|
let stepFn = await this.getStepFunctionality(step.stepId)
|
||||||
|
let inputs = await processObject(originalStepInput, this._context)
|
||||||
|
inputs = automationUtils.cleanInputValues(
|
||||||
|
inputs,
|
||||||
|
step.schema.inputs
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// appId is always passed
|
||||||
|
const outputs = await stepFn({
|
||||||
|
inputs: inputs,
|
||||||
|
appId: this._appId,
|
||||||
|
emitter: this._emitter,
|
||||||
|
context: this._context,
|
||||||
|
})
|
||||||
|
|
||||||
|
this._context.steps[stepCount] = outputs
|
||||||
|
// if filter causes us to stop execution don't break the loop, set a var
|
||||||
|
// so that we can finish iterating through the steps and record that it stopped
|
||||||
|
if (step.stepId === FILTER_STEP_ID && !outputs.result) {
|
||||||
|
stopped = true
|
||||||
|
this.updateExecutionOutput(
|
||||||
|
step.id,
|
||||||
|
step.stepId,
|
||||||
|
step.inputs,
|
||||||
|
{
|
||||||
|
...outputs,
|
||||||
|
...STOPPED_STATUS,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (loopStep && loopSteps) {
|
||||||
|
loopSteps.push(outputs)
|
||||||
|
} else {
|
||||||
|
this.updateExecutionOutput(
|
||||||
|
step.id,
|
||||||
|
step.stepId,
|
||||||
|
step.inputs,
|
||||||
|
outputs
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Automation error - ${step.stepId} - ${err}`)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loopStep) {
|
||||||
|
iterationCount++
|
||||||
|
if (index === iterations - 1) {
|
||||||
|
loopStep = undefined
|
||||||
|
this._context.steps.splice(loopStepNumber, 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stepSpan?.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loopStep && iterations === 0) {
|
||||||
loopStep = undefined
|
loopStep = undefined
|
||||||
|
this.executionOutput.steps.splice(loopStepNumber + 1, 0, {
|
||||||
|
id: step.id,
|
||||||
|
stepId: step.stepId,
|
||||||
|
outputs: {
|
||||||
|
status: AutomationStepStatus.NO_ITERATIONS,
|
||||||
|
success: true,
|
||||||
|
},
|
||||||
|
inputs: {},
|
||||||
|
})
|
||||||
|
|
||||||
this._context.steps.splice(loopStepNumber, 1)
|
this._context.steps.splice(loopStepNumber, 1)
|
||||||
break
|
iterations = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the step after the loop step as it's irrelevant, since information is included
|
||||||
|
// in the loop step
|
||||||
|
if (wasLoopStep && !loopStep) {
|
||||||
|
this._context.steps.splice(loopStepNumber + 1, 1)
|
||||||
|
wasLoopStep = false
|
||||||
|
}
|
||||||
|
if (loopSteps && loopSteps.length) {
|
||||||
|
let tempOutput = {
|
||||||
|
success: true,
|
||||||
|
items: loopSteps,
|
||||||
|
iterations: iterationCount,
|
||||||
|
}
|
||||||
|
this.executionOutput.steps.splice(loopStepNumber + 1, 0, {
|
||||||
|
id: step.id,
|
||||||
|
stepId: step.stepId,
|
||||||
|
outputs: tempOutput,
|
||||||
|
inputs: step.inputs,
|
||||||
|
})
|
||||||
|
this._context.steps[loopStepNumber] = tempOutput
|
||||||
|
|
||||||
|
wasLoopStep = true
|
||||||
|
loopSteps = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (loopStep && iterations === 0) {
|
const end = performance.now()
|
||||||
loopStep = undefined
|
const executionTime = end - start
|
||||||
this.executionOutput.steps.splice(loopStepNumber + 1, 0, {
|
|
||||||
id: step.id,
|
|
||||||
stepId: step.stepId,
|
|
||||||
outputs: {
|
|
||||||
status: AutomationStepStatus.NO_ITERATIONS,
|
|
||||||
success: true,
|
|
||||||
},
|
|
||||||
inputs: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
this._context.steps.splice(loopStepNumber, 1)
|
console.info(
|
||||||
iterations = 1
|
`Automation ID: ${automation._id} Execution time: ${executionTime} milliseconds`,
|
||||||
}
|
{
|
||||||
|
_logKey: "automation",
|
||||||
|
executionTime,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Delete the step after the loop step as it's irrelevant, since information is included
|
// store the logs for the automation run
|
||||||
// in the loop step
|
try {
|
||||||
if (wasLoopStep && !loopStep) {
|
await storeLog(this._automation, this.executionOutput)
|
||||||
this._context.steps.splice(loopStepNumber + 1, 1)
|
} catch (e: any) {
|
||||||
wasLoopStep = false
|
if (e.status === 413 && e.request?.data) {
|
||||||
}
|
// if content is too large we shouldn't log it
|
||||||
if (loopSteps && loopSteps.length) {
|
delete e.request.data
|
||||||
let tempOutput = {
|
e.request.data = { message: "removed due to large size" }
|
||||||
success: true,
|
}
|
||||||
items: loopSteps,
|
logging.logAlert("Error writing automation log", e)
|
||||||
iterations: iterationCount,
|
|
||||||
}
|
}
|
||||||
this.executionOutput.steps.splice(loopStepNumber + 1, 0, {
|
if (isProdAppID(this._appId) && isRecurring(automation) && metadata) {
|
||||||
id: step.id,
|
await this.updateMetadata(metadata)
|
||||||
stepId: step.stepId,
|
}
|
||||||
outputs: tempOutput,
|
return this.executionOutput
|
||||||
inputs: step.inputs,
|
|
||||||
})
|
|
||||||
this._context.steps[loopStepNumber] = tempOutput
|
|
||||||
|
|
||||||
wasLoopStep = true
|
|
||||||
loopSteps = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const end = performance.now()
|
|
||||||
const executionTime = end - start
|
|
||||||
|
|
||||||
console.info(
|
|
||||||
`Automation ID: ${automation._id} Execution time: ${executionTime} milliseconds`,
|
|
||||||
{
|
|
||||||
_logKey: "automation",
|
|
||||||
executionTime,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// store the logs for the automation run
|
|
||||||
try {
|
|
||||||
await storeLog(this._automation, this.executionOutput)
|
|
||||||
} catch (e: any) {
|
|
||||||
if (e.status === 413 && e.request?.data) {
|
|
||||||
// if content is too large we shouldn't log it
|
|
||||||
delete e.request.data
|
|
||||||
e.request.data = { message: "removed due to large size" }
|
|
||||||
}
|
|
||||||
logging.logAlert("Error writing automation log", e)
|
|
||||||
}
|
|
||||||
if (isProdAppID(this._appId) && isRecurring(automation) && metadata) {
|
|
||||||
await this.updateMetadata(metadata)
|
|
||||||
}
|
|
||||||
return this.executionOutput
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
Row,
|
Row,
|
||||||
Table,
|
Table,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
import tracer from "dd-trace"
|
||||||
|
|
||||||
interface FormulaOpts {
|
interface FormulaOpts {
|
||||||
dynamic?: boolean
|
dynamic?: boolean
|
||||||
|
@ -50,33 +51,42 @@ export function processFormulas<T extends Row | Row[]>(
|
||||||
inputRows: T,
|
inputRows: T,
|
||||||
{ dynamic, contextRows }: FormulaOpts = { dynamic: true }
|
{ dynamic, contextRows }: FormulaOpts = { dynamic: true }
|
||||||
): T {
|
): T {
|
||||||
const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
return tracer.trace("processFormulas", {}, span => {
|
||||||
if (rows)
|
const numRows = Array.isArray(inputRows) ? inputRows.length : 1
|
||||||
for (let [column, schema] of Object.entries(table.schema)) {
|
span?.addTags({ table_id: table._id, dynamic, numRows })
|
||||||
if (schema.type !== FieldTypes.FORMULA) {
|
const rows = Array.isArray(inputRows) ? inputRows : [inputRows]
|
||||||
continue
|
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 (
|
if (
|
||||||
schema.formula == null ||
|
schema.formula == null ||
|
||||||
(dynamic && isStatic) ||
|
(dynamic && isStatic) ||
|
||||||
(!dynamic && !isStatic)
|
(!dynamic && !isStatic)
|
||||||
) {
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// iterate through rows and process formula
|
// iterate through rows and process formula
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
let row = rows[i]
|
let row = rows[i]
|
||||||
let context = contextRows ? contextRows[i] : row
|
let context = contextRows ? contextRows[i] : row
|
||||||
rows[i] = {
|
let formula = schema.formula
|
||||||
...row,
|
rows[i] = {
|
||||||
[column]: processStringSync(schema.formula, context),
|
...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]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1180,6 +1180,14 @@
|
||||||
"description": "<p>Stringify an object using <code>JSON.stringify</code>.</p>\n"
|
"description": "<p>Stringify an object using <code>JSON.stringify</code>.</p>\n"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"uuid": {
|
||||||
|
"args": [],
|
||||||
|
"numArgs": 0,
|
||||||
|
"example": "{{ uuid }} -> f34ebc66-93bd-4f7c-b79b-92b5569138bc",
|
||||||
|
"description": "<p>Generates a UUID, using the V4 method (identical to the browser crypto.randomUUID function).</p>\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
"date": {
|
"date": {
|
||||||
"date": {
|
"date": {
|
||||||
"args": [
|
"args": [
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
"manifest": "node ./scripts/gen-collection-info.js"
|
"manifest": "node ./scripts/gen-collection-info.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/handlebars-helpers": "^0.11.9",
|
"@budibase/handlebars-helpers": "^0.11.11",
|
||||||
"dayjs": "^1.10.8",
|
"dayjs": "^1.10.8",
|
||||||
"handlebars": "^4.7.6",
|
"handlebars": "^4.7.6",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
|
|
|
@ -20,6 +20,7 @@ const COLLECTIONS = [
|
||||||
"string",
|
"string",
|
||||||
"comparison",
|
"comparison",
|
||||||
"object",
|
"object",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
const FILENAME = join(__dirname, "..", "manifest.json")
|
const FILENAME = join(__dirname, "..", "manifest.json")
|
||||||
const outputJSON = {}
|
const outputJSON = {}
|
||||||
|
|
|
@ -16,6 +16,7 @@ const EXTERNAL_FUNCTION_COLLECTIONS = [
|
||||||
"comparison",
|
"comparison",
|
||||||
"object",
|
"object",
|
||||||
"regex",
|
"regex",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
const ADDED_HELPERS = {
|
const ADDED_HELPERS = {
|
||||||
|
|
|
@ -56,6 +56,12 @@ module.exports.processJS = (handlebars, context) => {
|
||||||
const res = { data: runJS(js, sandboxContext) }
|
const res = { data: runJS(js, sandboxContext) }
|
||||||
return `{{${LITERAL_MARKER} js_result-${JSON.stringify(res)}}}`
|
return `{{${LITERAL_MARKER} js_result-${JSON.stringify(res)}}}`
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
|
||||||
|
return "Timed out while executing JS"
|
||||||
|
}
|
||||||
|
if (error.name === "ExecutionTimeoutError") {
|
||||||
|
return "Request JS execution limit hit"
|
||||||
|
}
|
||||||
return "Error while executing JS"
|
return "Error while executing JS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
const externalHandlebars = require("./external")
|
const externalHandlebars = require("./external")
|
||||||
const helperList = require("@budibase/handlebars-helpers")
|
const helperList = require("@budibase/handlebars-helpers")
|
||||||
|
|
||||||
|
let helpers = undefined
|
||||||
|
|
||||||
module.exports.getHelperList = () => {
|
module.exports.getHelperList = () => {
|
||||||
|
if (helpers) {
|
||||||
|
return helpers
|
||||||
|
}
|
||||||
|
|
||||||
|
helpers = {}
|
||||||
let constructed = []
|
let constructed = []
|
||||||
for (let collection of externalHandlebars.externalCollections) {
|
for (let collection of externalHandlebars.externalCollections) {
|
||||||
constructed.push(helperList[collection]())
|
constructed.push(helperList[collection]())
|
||||||
}
|
}
|
||||||
const fullMap = {}
|
|
||||||
for (let collection of constructed) {
|
for (let collection of constructed) {
|
||||||
for (let [key, func] of Object.entries(collection)) {
|
for (let [key, func] of Object.entries(collection)) {
|
||||||
fullMap[key] = func
|
helpers[key] = func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let key of Object.keys(externalHandlebars.addedHelpers)) {
|
for (let key of Object.keys(externalHandlebars.addedHelpers)) {
|
||||||
fullMap[key] = externalHandlebars.addedHelpers[key]
|
helpers[key] = externalHandlebars.addedHelpers[key]
|
||||||
}
|
}
|
||||||
return fullMap
|
Object.freeze(helpers)
|
||||||
|
return helpers
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ module.exports.doesContainString = templates.doesContainString
|
||||||
module.exports.disableEscaping = templates.disableEscaping
|
module.exports.disableEscaping = templates.disableEscaping
|
||||||
module.exports.findHBSBlocks = templates.findHBSBlocks
|
module.exports.findHBSBlocks = templates.findHBSBlocks
|
||||||
module.exports.convertToJS = templates.convertToJS
|
module.exports.convertToJS = templates.convertToJS
|
||||||
|
module.exports.setJSRunner = templates.setJSRunner
|
||||||
module.exports.FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
module.exports.FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
||||||
|
|
||||||
if (!process.env.NO_JS) {
|
if (!process.env.NO_JS) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ const {
|
||||||
findDoubleHbsInstances,
|
findDoubleHbsInstances,
|
||||||
} = require("./utilities")
|
} = require("./utilities")
|
||||||
const { convertHBSBlock } = require("./conversion")
|
const { convertHBSBlock } = require("./conversion")
|
||||||
|
const javascript = require("./helpers/javascript")
|
||||||
|
|
||||||
const hbsInstance = handlebars.create()
|
const hbsInstance = handlebars.create()
|
||||||
registerAll(hbsInstance)
|
registerAll(hbsInstance)
|
||||||
|
@ -362,6 +363,8 @@ module.exports.doesContainString = (template, string) => {
|
||||||
return exports.doesContainStrings(template, [string])
|
return exports.doesContainStrings(template, [string])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.setJSRunner = javascript.setJSRunner
|
||||||
|
|
||||||
module.exports.convertToJS = hbs => {
|
module.exports.convertToJS = hbs => {
|
||||||
const blocks = exports.findHBSBlocks(hbs)
|
const blocks = exports.findHBSBlocks(hbs)
|
||||||
let js = "return `",
|
let js = "return `",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import vm from "vm"
|
import vm from "vm"
|
||||||
import templates from "./index.js"
|
import templates from "./index.js"
|
||||||
import { setJSRunner } from "./helpers/javascript"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ES6 entrypoint for rollup
|
* ES6 entrypoint for rollup
|
||||||
|
@ -20,6 +19,7 @@ export const doesContainString = templates.doesContainString
|
||||||
export const disableEscaping = templates.disableEscaping
|
export const disableEscaping = templates.disableEscaping
|
||||||
export const findHBSBlocks = templates.findHBSBlocks
|
export const findHBSBlocks = templates.findHBSBlocks
|
||||||
export const convertToJS = templates.convertToJS
|
export const convertToJS = templates.convertToJS
|
||||||
|
export const setJSRunner = templates.setJSRunner
|
||||||
export const FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
export const FIND_ANY_HBS_REGEX = templates.FIND_ANY_HBS_REGEX
|
||||||
|
|
||||||
if (process && !process.env.NO_JS) {
|
if (process && !process.env.NO_JS) {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
module.exports.UUID_REGEX =
|
||||||
|
/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
|
|
@ -1,6 +1,7 @@
|
||||||
const { processString, processObject, isValid } = require("../src/index.cjs")
|
const { processString, processObject, isValid } = require("../src/index.cjs")
|
||||||
const tableJson = require("./examples/table.json")
|
const tableJson = require("./examples/table.json")
|
||||||
const dayjs = require("dayjs")
|
const dayjs = require("dayjs")
|
||||||
|
const { UUID_REGEX } = require("./constants")
|
||||||
|
|
||||||
describe("test the custom helpers we have applied", () => {
|
describe("test the custom helpers we have applied", () => {
|
||||||
it("should be able to use the object helper", async () => {
|
it("should be able to use the object helper", async () => {
|
||||||
|
@ -477,3 +478,10 @@ describe("Cover a few complex use cases", () => {
|
||||||
expect(output.dataProvider).toBe("%5B%221%22%2C%221%22%5D")
|
expect(output.dataProvider).toBe("%5B%221%22%2C%221%22%5D")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("uuid", () => {
|
||||||
|
it("should be able to generate a UUID", async () => {
|
||||||
|
const output = await processString("{{ uuid }}", {})
|
||||||
|
expect(output).toMatch(UUID_REGEX)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const { processStringSync, encodeJSBinding } = require("../src/index.cjs")
|
const { processStringSync, encodeJSBinding } = require("../src/index.cjs")
|
||||||
|
const { UUID_REGEX } = require("./constants")
|
||||||
|
|
||||||
const processJS = (js, context) => {
|
const processJS = (js, context) => {
|
||||||
return processStringSync(encodeJSBinding(js), context)
|
return processStringSync(encodeJSBinding(js), context)
|
||||||
|
@ -114,7 +115,7 @@ describe("Test the JavaScript helper", () => {
|
||||||
|
|
||||||
it("should timeout after one second", () => {
|
it("should timeout after one second", () => {
|
||||||
const output = processJS(`while (true) {}`)
|
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", () => {
|
it("should prevent access to the process global", () => {
|
||||||
|
@ -140,4 +141,9 @@ describe("check JS helpers", () => {
|
||||||
const output = processJS(`return helpers.toInt(4.3)`)
|
const output = processJS(`return helpers.toInt(4.3)`)
|
||||||
expect(output).toBe(4)
|
expect(output).toBe(4)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should be able to use uuid", () => {
|
||||||
|
const output = processJS(`return helpers.uuid()`)
|
||||||
|
expect(output).toMatch(UUID_REGEX)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,6 +21,7 @@ export enum LockName {
|
||||||
PERSIST_WRITETHROUGH = "persist_writethrough",
|
PERSIST_WRITETHROUGH = "persist_writethrough",
|
||||||
QUOTA_USAGE_EVENT = "quota_usage_event",
|
QUOTA_USAGE_EVENT = "quota_usage_event",
|
||||||
APP_MIGRATION = "app_migrations",
|
APP_MIGRATION = "app_migrations",
|
||||||
|
PROCESS_AUTO_COLUMNS = "process_auto_columns",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LockOptions = {
|
export type LockOptions = {
|
||||||
|
|
|
@ -51,8 +51,12 @@ ENV TENANT_FEATURE_FLAGS=*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR
|
||||||
ENV ACCOUNT_PORTAL_URL=https://account.budibase.app
|
ENV ACCOUNT_PORTAL_URL=https://account.budibase.app
|
||||||
|
|
||||||
ARG BUDIBASE_VERSION
|
ARG BUDIBASE_VERSION
|
||||||
|
ARG GIT_COMMIT_SHA
|
||||||
# Ensuring the version argument is sent
|
# Ensuring the version argument is sent
|
||||||
RUN test -n "$BUDIBASE_VERSION"
|
RUN test -n "$BUDIBASE_VERSION"
|
||||||
ENV BUDIBASE_VERSION=$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"]
|
CMD ["./docker_run.sh"]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import tk from "timekeeper"
|
import tk from "timekeeper"
|
||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import { mocks, structures } from "@budibase/backend-core/tests"
|
import { generator, mocks, structures } from "@budibase/backend-core/tests"
|
||||||
import {
|
import {
|
||||||
ScimCreateUserRequest,
|
ScimCreateUserRequest,
|
||||||
ScimGroupResponse,
|
ScimGroupResponse,
|
||||||
|
@ -14,9 +14,14 @@ import { events } from "@budibase/backend-core"
|
||||||
jest.retryTimes(2, { logErrorsBeforeRetry: true })
|
jest.retryTimes(2, { logErrorsBeforeRetry: true })
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
mocks.licenses.useScimIntegration()
|
|
||||||
|
|
||||||
describe("scim", () => {
|
describe("scim", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
tk.freeze(mocks.date.MOCK_DATE)
|
||||||
|
mocks.licenses.useScimIntegration()
|
||||||
|
|
||||||
|
await config.setSCIMConfig(true)
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.resetAllMocks()
|
jest.resetAllMocks()
|
||||||
tk.freeze(mocks.date.MOCK_DATE)
|
tk.freeze(mocks.date.MOCK_DATE)
|
||||||
|
@ -570,8 +575,15 @@ describe("scim", () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
groups = []
|
groups = []
|
||||||
|
|
||||||
for (let i = 0; i < groupCount; i++) {
|
const groupNames = generator.unique(
|
||||||
const body = structures.scim.createGroupRequest()
|
() => generator.word(),
|
||||||
|
groupCount
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const groupName of groupNames) {
|
||||||
|
const body = structures.scim.createGroupRequest({
|
||||||
|
displayName: groupName,
|
||||||
|
})
|
||||||
groups.push(await config.api.scimGroupsAPI.post({ body }))
|
groups.push(await config.api.scimGroupsAPI.post({ body }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,9 @@ export const resetUpdate = async (resetCode: string, password: string) => {
|
||||||
user.password = password
|
user.password = password
|
||||||
user = await userSdk.db.save(user)
|
user = await userSdk.db.save(user)
|
||||||
|
|
||||||
|
await cache.passwordReset.invalidateCode(resetCode)
|
||||||
|
await sessions.invalidateSessions(userId)
|
||||||
|
|
||||||
// remove password from the user before sending events
|
// remove password from the user before sending events
|
||||||
delete user.password
|
delete user.password
|
||||||
await events.user.passwordReset(user)
|
await events.user.passwordReset(user)
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { cache, context, sessions, utils } from "@budibase/backend-core"
|
||||||
|
import { loginUser, resetUpdate } from "../auth"
|
||||||
|
import { generator, structures } from "@budibase/backend-core/tests"
|
||||||
|
import { TestConfiguration } from "../../../tests"
|
||||||
|
|
||||||
|
describe("auth", () => {
|
||||||
|
const config = new TestConfiguration()
|
||||||
|
|
||||||
|
describe("resetUpdate", () => {
|
||||||
|
it("providing a valid code will update the password", async () => {
|
||||||
|
await context.doInTenant(structures.tenant.id(), async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
const previousPassword = user.password
|
||||||
|
|
||||||
|
const code = await cache.passwordReset.createCode(user._id!, {})
|
||||||
|
const newPassword = generator.hash()
|
||||||
|
|
||||||
|
await resetUpdate(code, newPassword)
|
||||||
|
|
||||||
|
const persistedUser = await config.getUser(user.email)
|
||||||
|
expect(persistedUser.password).not.toBe(previousPassword)
|
||||||
|
expect(
|
||||||
|
await utils.compare(newPassword, persistedUser.password!)
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("wrong code will not allow to reset the password", async () => {
|
||||||
|
await context.doInTenant(structures.tenant.id(), async () => {
|
||||||
|
const code = generator.hash()
|
||||||
|
const newPassword = generator.hash()
|
||||||
|
|
||||||
|
await expect(resetUpdate(code, newPassword)).rejects.toThrow(
|
||||||
|
"Provided information is not valid, cannot reset password - please try again."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("the same code cannot be used twice", async () => {
|
||||||
|
await context.doInTenant(structures.tenant.id(), async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
|
||||||
|
const code = await cache.passwordReset.createCode(user._id!, {})
|
||||||
|
const newPassword = generator.hash()
|
||||||
|
|
||||||
|
await resetUpdate(code, newPassword)
|
||||||
|
await expect(resetUpdate(code, newPassword)).rejects.toThrow(
|
||||||
|
"Provided information is not valid, cannot reset password - please try again."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("updating the password will invalidate all the sessions", async () => {
|
||||||
|
await context.doInTenant(structures.tenant.id(), async () => {
|
||||||
|
const user = await config.createUser()
|
||||||
|
|
||||||
|
await loginUser(user)
|
||||||
|
|
||||||
|
expect(await sessions.getSessionsForUser(user._id!)).toHaveLength(1)
|
||||||
|
|
||||||
|
const code = await cache.passwordReset.createCode(user._id!, {})
|
||||||
|
const newPassword = generator.hash()
|
||||||
|
|
||||||
|
await resetUpdate(code, newPassword)
|
||||||
|
|
||||||
|
expect(await sessions.getSessionsForUser(user._id!)).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,6 +1,5 @@
|
||||||
import { structures, mocks } from "../../../tests"
|
import { structures, mocks } from "../../../tests"
|
||||||
import { env, context } from "@budibase/backend-core"
|
import { env, context } from "@budibase/backend-core"
|
||||||
import * as users from "../users"
|
|
||||||
import { db as userDb } from "../"
|
import { db as userDb } from "../"
|
||||||
import { CloudAccount } from "@budibase/types"
|
import { CloudAccount } from "@budibase/types"
|
||||||
|
|
||||||
|
|
1026
qa-core/yarn.lock
1026
qa-core/yarn.lock
File diff suppressed because it is too large
Load Diff
|
@ -59,6 +59,7 @@ function runBuild(entry, outfile) {
|
||||||
"pouchdb",
|
"pouchdb",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"bcryptjs",
|
"bcryptjs",
|
||||||
|
"graphql/*",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
372
yarn.lock
372
yarn.lock
|
@ -829,12 +829,13 @@
|
||||||
events "^3.0.0"
|
events "^3.0.0"
|
||||||
tslib "^2.2.0"
|
tslib "^2.2.0"
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5":
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.22.5", "@babel/code-frame@^7.23.5":
|
||||||
version "7.22.5"
|
version "7.23.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244"
|
||||||
integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==
|
integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/highlight" "^7.22.5"
|
"@babel/highlight" "^7.23.4"
|
||||||
|
chalk "^2.4.2"
|
||||||
|
|
||||||
"@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5":
|
"@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
|
@ -880,12 +881,12 @@
|
||||||
"@jridgewell/gen-mapping" "^0.3.0"
|
"@jridgewell/gen-mapping" "^0.3.0"
|
||||||
jsesc "^2.5.1"
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
"@babel/generator@^7.22.5", "@babel/generator@^7.7.2":
|
"@babel/generator@^7.22.5", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2":
|
||||||
version "7.22.5"
|
version "7.23.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7"
|
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e"
|
||||||
integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==
|
integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.23.6"
|
||||||
"@jridgewell/gen-mapping" "^0.3.2"
|
"@jridgewell/gen-mapping" "^0.3.2"
|
||||||
"@jridgewell/trace-mapping" "^0.3.17"
|
"@jridgewell/trace-mapping" "^0.3.17"
|
||||||
jsesc "^2.5.1"
|
jsesc "^2.5.1"
|
||||||
|
@ -963,18 +964,18 @@
|
||||||
resolve "^1.14.2"
|
resolve "^1.14.2"
|
||||||
semver "^6.1.2"
|
semver "^6.1.2"
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.5":
|
"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.20"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98"
|
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
|
||||||
integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==
|
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
|
||||||
|
|
||||||
"@babel/helper-function-name@^7.22.5":
|
"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0":
|
||||||
version "7.22.5"
|
version "7.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be"
|
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
|
||||||
integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==
|
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/template" "^7.22.5"
|
"@babel/template" "^7.22.15"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.23.0"
|
||||||
|
|
||||||
"@babel/helper-hoist-variables@^7.22.5":
|
"@babel/helper-hoist-variables@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
|
@ -1059,22 +1060,22 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@^7.22.5":
|
"@babel/helper-split-export-declaration@^7.22.5", "@babel/helper-split-export-declaration@^7.22.6":
|
||||||
version "7.22.5"
|
version "7.22.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08"
|
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
|
||||||
integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==
|
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/helper-string-parser@^7.22.5":
|
"@babel/helper-string-parser@^7.23.4":
|
||||||
version "7.22.5"
|
version "7.23.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
|
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
|
||||||
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
|
integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.22.5":
|
"@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.20"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193"
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
|
||||||
integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==
|
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
|
||||||
|
|
||||||
"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.22.5":
|
"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
|
@ -1100,13 +1101,13 @@
|
||||||
"@babel/traverse" "^7.22.5"
|
"@babel/traverse" "^7.22.5"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/highlight@^7.22.5":
|
"@babel/highlight@^7.23.4":
|
||||||
version "7.22.5"
|
version "7.23.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031"
|
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b"
|
||||||
integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==
|
integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-validator-identifier" "^7.22.5"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
chalk "^2.0.0"
|
chalk "^2.4.2"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
"@babel/parser@7.18.4":
|
"@babel/parser@7.18.4":
|
||||||
|
@ -1114,10 +1115,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef"
|
||||||
integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==
|
integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==
|
||||||
|
|
||||||
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5":
|
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.22.5", "@babel/parser@^7.23.6":
|
||||||
version "7.23.0"
|
version "7.23.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b"
|
||||||
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
|
integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==
|
||||||
|
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5":
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
|
@ -1999,43 +2000,36 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||||
|
|
||||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||||
version "7.23.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
|
|
||||||
integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.14.0"
|
|
||||||
|
|
||||||
"@babel/runtime@^7.13.10":
|
|
||||||
version "7.23.6"
|
version "7.23.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.6.tgz#c05e610dc228855dc92ef1b53d07389ed8ab521d"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.6.tgz#c05e610dc228855dc92ef1b53d07389ed8ab521d"
|
||||||
integrity sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==
|
integrity sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/template@^7.22.5", "@babel/template@^7.3.3":
|
"@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3":
|
||||||
version "7.22.5"
|
version "7.22.15"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
||||||
integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==
|
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.22.5"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/parser" "^7.22.5"
|
"@babel/parser" "^7.22.15"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.22.15"
|
||||||
|
|
||||||
"@babel/traverse@^7.22.5":
|
"@babel/traverse@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.23.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1"
|
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.6.tgz#b53526a2367a0dd6edc423637f3d2d0f2521abc5"
|
||||||
integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==
|
integrity sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.22.5"
|
"@babel/code-frame" "^7.23.5"
|
||||||
"@babel/generator" "^7.22.5"
|
"@babel/generator" "^7.23.6"
|
||||||
"@babel/helper-environment-visitor" "^7.22.5"
|
"@babel/helper-environment-visitor" "^7.22.20"
|
||||||
"@babel/helper-function-name" "^7.22.5"
|
"@babel/helper-function-name" "^7.23.0"
|
||||||
"@babel/helper-hoist-variables" "^7.22.5"
|
"@babel/helper-hoist-variables" "^7.22.5"
|
||||||
"@babel/helper-split-export-declaration" "^7.22.5"
|
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||||
"@babel/parser" "^7.22.5"
|
"@babel/parser" "^7.23.6"
|
||||||
"@babel/types" "^7.22.5"
|
"@babel/types" "^7.23.6"
|
||||||
debug "^4.1.0"
|
debug "^4.3.1"
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
|
|
||||||
"@babel/types@7.18.4":
|
"@babel/types@7.18.4":
|
||||||
|
@ -2046,13 +2040,13 @@
|
||||||
"@babel/helper-validator-identifier" "^7.16.7"
|
"@babel/helper-validator-identifier" "^7.16.7"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.2", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
|
"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.2", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
|
||||||
version "7.22.5"
|
version "7.23.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd"
|
||||||
integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==
|
integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-string-parser" "^7.22.5"
|
"@babel/helper-string-parser" "^7.23.4"
|
||||||
"@babel/helper-validator-identifier" "^7.22.5"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@balena/dockerignore@^1.0.2":
|
"@balena/dockerignore@^1.0.2":
|
||||||
|
@ -2065,10 +2059,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/handlebars-helpers@^0.11.9":
|
"@budibase/handlebars-helpers@^0.11.11":
|
||||||
version "0.11.9"
|
version "0.11.11"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.9.tgz#46ec9c571cd667b73e94918f82d6414acc52a281"
|
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.11.tgz#1bf3c0bfe338811ce7e7c482faa88602c2ad9787"
|
||||||
integrity sha512-S1AGkrljacSddTDw+SuPTU+HH+GZr83aodj5fZ2Ia3ZBSmKK6/WKq3ZXlX7L14s6N7HYzEY2MXdC7ZDd3bpfjQ==
|
integrity sha512-+G7zrttvI3BGgYJY+DZMvEIyUgqu2zNTKcxvuFVC3Sj1OJhWhBjyScjEj5cjMP2tYD+sncnW7oMmjeevovHGfA==
|
||||||
dependencies:
|
dependencies:
|
||||||
array-sort "^1.0.0"
|
array-sort "^1.0.0"
|
||||||
define-property "^2.0.2"
|
define-property "^2.0.2"
|
||||||
|
@ -2086,6 +2080,7 @@
|
||||||
relative "^3.0.2"
|
relative "^3.0.2"
|
||||||
striptags "^3.1.1"
|
striptags "^3.1.1"
|
||||||
to-gfm-code-block "^0.1.1"
|
to-gfm-code-block "^0.1.1"
|
||||||
|
uuid "^9.0.1"
|
||||||
|
|
||||||
"@budibase/nano@10.1.4":
|
"@budibase/nano@10.1.4":
|
||||||
version "10.1.4"
|
version "10.1.4"
|
||||||
|
@ -2271,6 +2266,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build "^3.9.0"
|
node-gyp-build "^3.9.0"
|
||||||
|
|
||||||
|
"@datadog/native-appsec@4.0.0":
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-4.0.0.tgz#ee08138b987dec557eac3650a43a972dac85b6a6"
|
||||||
|
integrity sha512-myTguXJ3VQHS2E1ylNsSF1avNpDmq5t+K4Q47wdzeakGc3sDIDDyEbvuFTujl9c9wBIkup94O1mZj5DR37ajzA==
|
||||||
|
dependencies:
|
||||||
|
node-gyp-build "^3.9.0"
|
||||||
|
|
||||||
"@datadog/native-iast-rewriter@1.1.2":
|
"@datadog/native-iast-rewriter@1.1.2":
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-1.1.2.tgz#793cbf92d218ec80d645be0830023656b81018ea"
|
resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-1.1.2.tgz#793cbf92d218ec80d645be0830023656b81018ea"
|
||||||
|
@ -2278,6 +2280,14 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build "^4.5.0"
|
node-gyp-build "^4.5.0"
|
||||||
|
|
||||||
|
"@datadog/native-iast-rewriter@2.2.1":
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.2.1.tgz#3c74c5a8caa0b876e091e9c5a95256add0d73e1c"
|
||||||
|
integrity sha512-DyZlE8gNa5AoOFNKGRJU4RYF/Y/tJzv4bIAMuVBbEnMA0xhiIYqpYQG8T3OKkALl3VSEeBMjYwuOR2fCrJ6gzA==
|
||||||
|
dependencies:
|
||||||
|
lru-cache "^7.14.0"
|
||||||
|
node-gyp-build "^4.5.0"
|
||||||
|
|
||||||
"@datadog/native-iast-taint-tracking@1.1.0":
|
"@datadog/native-iast-taint-tracking@1.1.0":
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-1.1.0.tgz#8f7d0016157b32dbf5c01b15b8afb1c4286b4a18"
|
resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-1.1.0.tgz#8f7d0016157b32dbf5c01b15b8afb1c4286b4a18"
|
||||||
|
@ -2285,13 +2295,39 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build "^3.9.0"
|
node-gyp-build "^3.9.0"
|
||||||
|
|
||||||
"@datadog/native-metrics@^1.5.0":
|
"@datadog/native-iast-taint-tracking@1.6.4":
|
||||||
version "1.5.0"
|
version "1.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-1.5.0.tgz#e71b6b6d65f4bd58dfdffab2737890e8eef34584"
|
resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-1.6.4.tgz#16c21ad7c36a53420c0d3c5a3720731809cc7e98"
|
||||||
integrity sha512-K63XMDx74RLhOpM8I9GGZR9ft0CNNB/RkjYPLHcVGvVnBR47zmWE2KFa7Yrtzjbk73+88PXI4nzqLyR3PJsaIQ==
|
integrity sha512-Owxk7hQ4Dxwv4zJAoMjRga0IvE6lhvxnNc8pJCHsemCWBXchjr/9bqg05Zy5JnMbKUWn4XuZeJD6RFZpRa8bfw==
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build "^3.9.0"
|
node-gyp-build "^3.9.0"
|
||||||
|
|
||||||
|
"@datadog/native-metrics@^1.5.0":
|
||||||
|
version "1.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-1.6.0.tgz#1c7958964460149911f6964c32b1a8692ee3ce8f"
|
||||||
|
integrity sha512-+8jBzd0nlLV+ay3Vb87DLwz8JHAS817hRhSRQ6zxhud9TyvvcNTNN+VA2sb2fe5UK4aMDvj/sGVJjEtgr4RHew==
|
||||||
|
dependencies:
|
||||||
|
node-gyp-build "^3.9.0"
|
||||||
|
|
||||||
|
"@datadog/native-metrics@^2.0.0":
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-2.0.0.tgz#65bf03313ee419956361e097551db36173e85712"
|
||||||
|
integrity sha512-YklGVwUtmKGYqFf1MNZuOHvTYdKuR4+Af1XkWcMD8BwOAjxmd9Z+97328rCOY8TFUJzlGUPaXzB8j2qgG/BMwA==
|
||||||
|
dependencies:
|
||||||
|
node-addon-api "^6.1.0"
|
||||||
|
node-gyp-build "^3.9.0"
|
||||||
|
|
||||||
|
"@datadog/pprof@4.0.1":
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-4.0.1.tgz#f8629ecb62646d90ed49b6252dd0583d8d5001d3"
|
||||||
|
integrity sha512-TavqyiyQZOaUM9eQB07r8+K/T1CqKyOdsUGxpN79+BF+eOQBpTj/Cte6KdlhcUSKL3h5hSjC+vlgA7uW2qtVhA==
|
||||||
|
dependencies:
|
||||||
|
delay "^5.0.0"
|
||||||
|
node-gyp-build "<4.0"
|
||||||
|
p-limit "^3.1.0"
|
||||||
|
pprof-format "^2.0.7"
|
||||||
|
source-map "^0.7.4"
|
||||||
|
|
||||||
"@datadog/pprof@^1.1.1":
|
"@datadog/pprof@^1.1.1":
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-1.1.1.tgz#17e86035140523ac3a96f3662e5dd29822042d61"
|
resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-1.1.1.tgz#17e86035140523ac3a96f3662e5dd29822042d61"
|
||||||
|
@ -3795,10 +3831,22 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@octokit/openapi-types" "^18.0.0"
|
"@octokit/openapi-types" "^18.0.0"
|
||||||
|
|
||||||
"@opentelemetry/api@^1.0.1":
|
"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.0.1":
|
||||||
version "1.4.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.0.tgz#2c91791a9ba6ca0a0f4aaac5e45d58df13639ac8"
|
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.7.0.tgz#b139c81999c23e3c8d3c0a7234480e945920fc40"
|
||||||
integrity sha512-IgMK9i3sFGNUqPMbjABm0G26g0QCKCUBfglhQ7rQq6WcxbKfEHRcmwsoER4hZcuYqJgkYn2OeuoJIv7Jsftp7g==
|
integrity sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==
|
||||||
|
|
||||||
|
"@opentelemetry/core@^1.14.0":
|
||||||
|
version "1.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.19.0.tgz#6563bb65465bf232d8435553b9a122d9351c0fbb"
|
||||||
|
integrity sha512-w42AukJh3TP8R0IZZOVJVM/kMWu8g+lm4LzT70WtuKqhwq7KVhcDzZZuZinWZa6TtQCl7Smt2wolEYzpHabOgw==
|
||||||
|
dependencies:
|
||||||
|
"@opentelemetry/semantic-conventions" "1.19.0"
|
||||||
|
|
||||||
|
"@opentelemetry/semantic-conventions@1.19.0":
|
||||||
|
version "1.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz#0c17f80b45de1c8778dfdf17acb1e9d4c4aa4ba8"
|
||||||
|
integrity sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==
|
||||||
|
|
||||||
"@parcel/watcher@2.0.4":
|
"@parcel/watcher@2.0.4":
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
|
@ -5661,10 +5709,10 @@
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
form-data "^3.0.0"
|
form-data "^3.0.0"
|
||||||
|
|
||||||
"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12.12.47", "@types/node@>=13.13.4", "@types/node@>=13.7.0":
|
"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12.12.47", "@types/node@>=13.13.4", "@types/node@>=13.7.0", "@types/node@>=8.1.0":
|
||||||
version "20.8.9"
|
version "20.10.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.9.tgz#646390b4fab269abce59c308fc286dcd818a2b08"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2"
|
||||||
integrity sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==
|
integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~5.26.4"
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
|
@ -5688,13 +5736,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.37.tgz#0bfcd173e8e1e328337473a8317e37b3b14fd30d"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.37.tgz#0bfcd173e8e1e328337473a8317e37b3b14fd30d"
|
||||||
integrity sha512-7GgtHCs/QZrBrDzgIJnQtuSvhFSwhyYSI2uafSwZoNt1iOGhEN5fwNrQMjtONyHm9+/LoA4453jH0CMYcr06Pg==
|
integrity sha512-7GgtHCs/QZrBrDzgIJnQtuSvhFSwhyYSI2uafSwZoNt1iOGhEN5fwNrQMjtONyHm9+/LoA4453jH0CMYcr06Pg==
|
||||||
|
|
||||||
"@types/node@>=8.1.0":
|
|
||||||
version "20.10.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198"
|
|
||||||
integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==
|
|
||||||
dependencies:
|
|
||||||
undici-types "~5.26.4"
|
|
||||||
|
|
||||||
"@types/nodemailer@^6.4.4":
|
"@types/nodemailer@^6.4.4":
|
||||||
version "6.4.14"
|
version "6.4.14"
|
||||||
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.14.tgz#5c81a5e856db7f8ede80013e6dbad7c5fb2283e2"
|
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.14.tgz#5c81a5e856db7f8ede80013e6dbad7c5fb2283e2"
|
||||||
|
@ -7159,9 +7200,9 @@ axios@^0.26.0:
|
||||||
follow-redirects "^1.14.8"
|
follow-redirects "^1.14.8"
|
||||||
|
|
||||||
axios@^1.0.0, axios@^1.1.3, axios@^1.5.0:
|
axios@^1.0.0, axios@^1.1.3, axios@^1.5.0:
|
||||||
version "1.5.1"
|
version "1.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2"
|
||||||
integrity sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==
|
integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.15.0"
|
follow-redirects "^1.15.0"
|
||||||
form-data "^4.0.0"
|
form-data "^4.0.0"
|
||||||
|
@ -8014,7 +8055,7 @@ chalk@4.1.0:
|
||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chalk@^2.0.0, chalk@^2.4.2:
|
chalk@^2.4.2:
|
||||||
version "2.4.2"
|
version "2.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||||
|
@ -9072,6 +9113,11 @@ dayjs@^1.10.8, dayjs@^1.8.15:
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
|
||||||
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
|
||||||
|
|
||||||
|
dc-polyfill@^0.1.2:
|
||||||
|
version "0.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/dc-polyfill/-/dc-polyfill-0.1.3.tgz#fe9eefc86813439dd46d6f9ad9582ec079c39720"
|
||||||
|
integrity sha512-Wyk5n/5KUj3GfVKV2jtDbtChC/Ff9fjKsBcg4ZtYW1yQe3DXNHcGURvmoxhqQdfOQ9TwyMjnfyv1lyYcOkFkFA==
|
||||||
|
|
||||||
dd-trace@3.13.2:
|
dd-trace@3.13.2:
|
||||||
version "3.13.2"
|
version "3.13.2"
|
||||||
resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-3.13.2.tgz#95b1ec480ab9ac406e1da7591a8c6f678d3799fd"
|
resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-3.13.2.tgz#95b1ec480ab9ac406e1da7591a8c6f678d3799fd"
|
||||||
|
@ -9105,6 +9151,45 @@ dd-trace@3.13.2:
|
||||||
retry "^0.10.1"
|
retry "^0.10.1"
|
||||||
semver "^5.5.0"
|
semver "^5.5.0"
|
||||||
|
|
||||||
|
dd-trace@4.20.0:
|
||||||
|
version "4.20.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-4.20.0.tgz#9a2cc3f28ff558c5605927b1362eb64605df76c1"
|
||||||
|
integrity sha512-y7IDLSSt6nww6zMdw/I8oLdfgOQADIOkERCNdfSzlBrXHz5CHimEOFfsHN87ag0mn6vusr06aoi+CQRZSNJz2g==
|
||||||
|
dependencies:
|
||||||
|
"@datadog/native-appsec" "4.0.0"
|
||||||
|
"@datadog/native-iast-rewriter" "2.2.1"
|
||||||
|
"@datadog/native-iast-taint-tracking" "1.6.4"
|
||||||
|
"@datadog/native-metrics" "^2.0.0"
|
||||||
|
"@datadog/pprof" "4.0.1"
|
||||||
|
"@datadog/sketches-js" "^2.1.0"
|
||||||
|
"@opentelemetry/api" "^1.0.0"
|
||||||
|
"@opentelemetry/core" "^1.14.0"
|
||||||
|
crypto-randomuuid "^1.0.0"
|
||||||
|
dc-polyfill "^0.1.2"
|
||||||
|
ignore "^5.2.4"
|
||||||
|
import-in-the-middle "^1.4.2"
|
||||||
|
int64-buffer "^0.1.9"
|
||||||
|
ipaddr.js "^2.1.0"
|
||||||
|
istanbul-lib-coverage "3.2.0"
|
||||||
|
jest-docblock "^29.7.0"
|
||||||
|
koalas "^1.0.2"
|
||||||
|
limiter "^1.1.4"
|
||||||
|
lodash.kebabcase "^4.1.1"
|
||||||
|
lodash.pick "^4.4.0"
|
||||||
|
lodash.sortby "^4.7.0"
|
||||||
|
lodash.uniq "^4.5.0"
|
||||||
|
lru-cache "^7.14.0"
|
||||||
|
methods "^1.1.2"
|
||||||
|
module-details-from-path "^1.0.3"
|
||||||
|
msgpack-lite "^0.1.26"
|
||||||
|
node-abort-controller "^3.1.1"
|
||||||
|
opentracing ">=0.12.1"
|
||||||
|
path-to-regexp "^0.1.2"
|
||||||
|
pprof-format "^2.0.7"
|
||||||
|
protobufjs "^7.2.4"
|
||||||
|
retry "^0.13.1"
|
||||||
|
semver "^7.5.4"
|
||||||
|
|
||||||
debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||||
|
@ -10618,6 +10703,11 @@ esutils@^2.0.2, esutils@^2.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||||
|
|
||||||
|
event-lite@^0.1.1:
|
||||||
|
version "0.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.3.tgz#3dfe01144e808ac46448f0c19b4ab68e403a901d"
|
||||||
|
integrity sha512-8qz9nOz5VeD2z96elrEKD2U433+L3DWdUdDkOINLGOJvx1GsMBbMn0aCeu28y8/e85A6mCigBiFlYMnTBEGlSw==
|
||||||
|
|
||||||
event-target-shim@^5.0.0:
|
event-target-shim@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||||
|
@ -12538,7 +12628,7 @@ ieee754@1.1.13:
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
||||||
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
|
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
|
||||||
|
|
||||||
ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1:
|
ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.1.8, ieee754@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||||
|
@ -12562,10 +12652,10 @@ ignore-walk@^6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimatch "^7.4.2"
|
minimatch "^7.4.2"
|
||||||
|
|
||||||
ignore@^5.0.4, ignore@^5.2.0:
|
ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4:
|
||||||
version "5.2.4"
|
version "5.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
|
||||||
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
|
integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==
|
||||||
|
|
||||||
image-q@^4.0.0:
|
image-q@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
@ -12611,10 +12701,10 @@ import-from@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
resolve-from "^5.0.0"
|
resolve-from "^5.0.0"
|
||||||
|
|
||||||
import-in-the-middle@^1.3.4:
|
import-in-the-middle@^1.3.4, import-in-the-middle@^1.4.2:
|
||||||
version "1.4.2"
|
version "1.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b"
|
resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.7.2.tgz#31c44088271b50ecb9cacbdfb1e5732c802e0658"
|
||||||
integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==
|
integrity sha512-coz7AjRnPyKW36J6JX5Bjz1mcX7MX1H2XsEGseVcnXMdzsAbbAu0HBZhiAem+3SAmuZdi+p8OwoB2qUpTRgjOQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn "^8.8.2"
|
acorn "^8.8.2"
|
||||||
acorn-import-assertions "^1.9.0"
|
acorn-import-assertions "^1.9.0"
|
||||||
|
@ -12745,6 +12835,11 @@ inquirer@^8.2.4:
|
||||||
through "^2.3.6"
|
through "^2.3.6"
|
||||||
wrap-ansi "^7.0.0"
|
wrap-ansi "^7.0.0"
|
||||||
|
|
||||||
|
int64-buffer@^0.1.9:
|
||||||
|
version "0.1.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423"
|
||||||
|
integrity sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==
|
||||||
|
|
||||||
internal-slot@^1.0.4, internal-slot@^1.0.5:
|
internal-slot@^1.0.4, internal-slot@^1.0.5:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930"
|
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930"
|
||||||
|
@ -12828,10 +12923,10 @@ ip@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
|
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
|
||||||
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
|
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
|
||||||
|
|
||||||
ipaddr.js@^2.0.1:
|
ipaddr.js@^2.0.1, ipaddr.js@^2.1.0:
|
||||||
version "2.0.1"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
|
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f"
|
||||||
integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
|
integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==
|
||||||
|
|
||||||
is-accessor-descriptor@^0.1.6:
|
is-accessor-descriptor@^0.1.6:
|
||||||
version "0.1.6"
|
version "0.1.6"
|
||||||
|
@ -15992,6 +16087,16 @@ ms@^2.0.0, ms@^2.1.1, ms@^2.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||||
|
|
||||||
|
msgpack-lite@^0.1.26:
|
||||||
|
version "0.1.26"
|
||||||
|
resolved "https://registry.yarnpkg.com/msgpack-lite/-/msgpack-lite-0.1.26.tgz#dd3c50b26f059f25e7edee3644418358e2a9ad89"
|
||||||
|
integrity sha512-SZ2IxeqZ1oRFGo0xFGbvBJWMp3yLIY9rlIJyxy8CGrwZn1f0ZK4r6jV/AM1r0FZMDUkWkglOk/eeKIL9g77Nxw==
|
||||||
|
dependencies:
|
||||||
|
event-lite "^0.1.1"
|
||||||
|
ieee754 "^1.1.8"
|
||||||
|
int64-buffer "^0.1.9"
|
||||||
|
isarray "^1.0.0"
|
||||||
|
|
||||||
msgpackr-extract@^3.0.1:
|
msgpackr-extract@^3.0.1:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d"
|
resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d"
|
||||||
|
@ -16196,7 +16301,7 @@ node-abi@^2.21.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver "^5.4.1"
|
semver "^5.4.1"
|
||||||
|
|
||||||
node-abort-controller@^3.0.1:
|
node-abort-controller@^3.0.1, node-abort-controller@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
|
resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
|
||||||
integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==
|
integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==
|
||||||
|
@ -16211,6 +16316,11 @@ node-addon-api@^5.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
|
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
|
||||||
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
|
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
|
||||||
|
|
||||||
|
node-addon-api@^6.1.0:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76"
|
||||||
|
integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==
|
||||||
|
|
||||||
node-duration@^1.0.4:
|
node-duration@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/node-duration/-/node-duration-1.0.4.tgz#3e94ecc0e473691c89c4560074503362071cecac"
|
resolved "https://registry.yarnpkg.com/node-duration/-/node-duration-1.0.4.tgz#3e94ecc0e473691c89c4560074503362071cecac"
|
||||||
|
@ -16245,7 +16355,7 @@ node-gyp-build-optional-packages@5.0.7:
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3"
|
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3"
|
||||||
integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==
|
integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==
|
||||||
|
|
||||||
node-gyp-build@^3.9.0:
|
node-gyp-build@<4.0, node-gyp-build@^3.9.0:
|
||||||
version "3.9.0"
|
version "3.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.9.0.tgz#53a350187dd4d5276750da21605d1cb681d09e25"
|
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.9.0.tgz#53a350187dd4d5276750da21605d1cb681d09e25"
|
||||||
integrity sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==
|
integrity sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==
|
||||||
|
@ -18155,17 +18265,10 @@ postgres-interval@^1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
posthog-js@^1.13.4:
|
posthog-js@^1.13.4, posthog-js@^1.36.0:
|
||||||
version "1.94.4"
|
version "1.96.1"
|
||||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.94.4.tgz#f480985e0fc131cc81edeb154d694ac5aca2e135"
|
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.96.1.tgz#4f9719a24e4e14037b0e72d430194d7cdb576447"
|
||||||
integrity sha512-1usqOEkGE0ntit99G4TM1iPoq4QBbd5KcuovWroTy0PRJU8cOAfAT3FaJu+lX4B7izWo4DFjvesM/MOaJzLW/Q==
|
integrity sha512-kv1vQqYMt2BV3YHS+wxsbGuP+tz+M3y1AzNhz8TfkpY1HT8W/ONT0i0eQpeRr9Y+d4x/fZ6M4cXG5GMvi9lRCA==
|
||||||
dependencies:
|
|
||||||
fflate "^0.4.1"
|
|
||||||
|
|
||||||
posthog-js@^1.36.0:
|
|
||||||
version "1.87.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.87.2.tgz#0e9395ea93d50bb624f0be19f254e62e54ae504d"
|
|
||||||
integrity sha512-pdxEylfxwEDwwz7g5dunPucvAN51RAOWWQmkcqHsLNHlV5o5bTaTwcAXaWB1IUn3xKPuKYE2lqbdB3vC4H4rFQ==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
fflate "^0.4.1"
|
fflate "^0.4.1"
|
||||||
|
|
||||||
|
@ -18401,6 +18504,11 @@ pouchdb@7.3.0:
|
||||||
uuid "8.3.2"
|
uuid "8.3.2"
|
||||||
vuvuzela "1.0.3"
|
vuvuzela "1.0.3"
|
||||||
|
|
||||||
|
pprof-format@^2.0.7:
|
||||||
|
version "2.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/pprof-format/-/pprof-format-2.0.7.tgz#526e4361f8b37d16b2ec4bb0696b5292de5046a4"
|
||||||
|
integrity sha512-1qWaGAzwMpaXJP9opRa23nPnt2Egi7RMNoNBptEE/XwHbcn4fC2b/4U4bKc5arkGkIh2ZabpF2bEb+c5GNHEKA==
|
||||||
|
|
||||||
prebuild-install@6.1.4:
|
prebuild-install@6.1.4:
|
||||||
version "6.1.4"
|
version "6.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f"
|
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f"
|
||||||
|
@ -19442,7 +19550,7 @@ retry-request@^5.0.0:
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
extend "^3.0.2"
|
extend "^3.0.2"
|
||||||
|
|
||||||
retry@0.13.1:
|
retry@0.13.1, retry@^0.13.1:
|
||||||
version "0.13.1"
|
version "0.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
|
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
|
||||||
integrity "sha1-GFsVh6z2eRnWOzVzSeA1N7JIRlg= sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
|
integrity "sha1-GFsVh6z2eRnWOzVzSeA1N7JIRlg= sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="
|
||||||
|
@ -22183,10 +22291,10 @@ uuid@^7.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
||||||
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
|
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
|
||||||
|
|
||||||
uuid@^9.0.0:
|
uuid@^9.0.0, uuid@^9.0.1:
|
||||||
version "9.0.0"
|
version "9.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||||
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
|
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
|
||||||
|
|
||||||
v8-compile-cache-lib@^3.0.1:
|
v8-compile-cache-lib@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
|
|
Loading…
Reference in New Issue