Merge branch 'master' into chore/update-to-node-22
This commit is contained in:
commit
541b602f90
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "3.2.11",
|
||||
"version": "3.2.12",
|
||||
"npmClient": "yarn",
|
||||
"concurrency": 20,
|
||||
"command": {
|
||||
|
|
|
@ -25,11 +25,12 @@
|
|||
"prettier": "2.8.8",
|
||||
"prettier-plugin-svelte": "^2.3.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"svelte": "^4.2.10",
|
||||
"svelte": "4.2.19",
|
||||
"svelte-eslint-parser": "^0.33.1",
|
||||
"typescript": "5.5.2",
|
||||
"typescript-eslint": "^7.3.1",
|
||||
"yargs": "^17.7.2"
|
||||
"yargs": "^17.7.2",
|
||||
"cross-spawn": "7.0.6"
|
||||
},
|
||||
"scripts": {
|
||||
"get-past-client-version": "node scripts/getPastClientVersion.js",
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
"bcryptjs": "2.4.3",
|
||||
"bull": "4.10.1",
|
||||
"correlation-id": "4.0.0",
|
||||
"dd-trace": "5.23.0",
|
||||
"dd-trace": "5.26.0",
|
||||
"dotenv": "16.0.1",
|
||||
"google-auth-library": "^8.0.1",
|
||||
"google-spreadsheet": "npm:@budibase/google-spreadsheet@4.1.5",
|
||||
|
|
|
@ -190,7 +190,7 @@ export class DatabaseImpl implements Database {
|
|||
}
|
||||
}
|
||||
|
||||
private async performCall<T>(call: DBCallback<T>): Promise<any> {
|
||||
private async performCall<T>(call: DBCallback<T>): Promise<T> {
|
||||
const db = this.getDb()
|
||||
const fnc = await call(db)
|
||||
try {
|
||||
|
@ -467,7 +467,7 @@ export class DatabaseImpl implements Database {
|
|||
} catch (err: any) {
|
||||
// didn't exist, don't worry
|
||||
if (err.statusCode === 404) {
|
||||
return
|
||||
return { ok: true }
|
||||
} else {
|
||||
throw new CouchDBError(err.message, err)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export class DDInstrumentedDatabase implements Database {
|
|||
|
||||
exists(docId?: string): Promise<boolean> {
|
||||
return tracer.trace("db.exists", span => {
|
||||
span?.addTags({ db_name: this.name, doc_id: docId })
|
||||
span.addTags({ db_name: this.name, doc_id: docId })
|
||||
if (docId) {
|
||||
return this.db.exists(docId)
|
||||
}
|
||||
|
@ -37,15 +37,17 @@ export class DDInstrumentedDatabase implements Database {
|
|||
|
||||
get<T extends Document>(id?: string | undefined): Promise<T> {
|
||||
return tracer.trace("db.get", span => {
|
||||
span?.addTags({ db_name: this.name, doc_id: id })
|
||||
span.addTags({ db_name: this.name, doc_id: id })
|
||||
return this.db.get(id)
|
||||
})
|
||||
}
|
||||
|
||||
tryGet<T extends Document>(id?: string | undefined): Promise<T | undefined> {
|
||||
return tracer.trace("db.tryGet", span => {
|
||||
span?.addTags({ db_name: this.name, doc_id: id })
|
||||
return this.db.tryGet(id)
|
||||
return tracer.trace("db.tryGet", async span => {
|
||||
span.addTags({ db_name: this.name, doc_id: id })
|
||||
const doc = await this.db.tryGet<T>(id)
|
||||
span.addTags({ doc_found: doc !== undefined })
|
||||
return doc
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -53,13 +55,15 @@ export class DDInstrumentedDatabase implements Database {
|
|||
ids: string[],
|
||||
opts?: { allowMissing?: boolean | undefined } | undefined
|
||||
): Promise<T[]> {
|
||||
return tracer.trace("db.getMultiple", span => {
|
||||
span?.addTags({
|
||||
return tracer.trace("db.getMultiple", async span => {
|
||||
span.addTags({
|
||||
db_name: this.name,
|
||||
num_docs: ids.length,
|
||||
allow_missing: opts?.allowMissing,
|
||||
})
|
||||
return this.db.getMultiple(ids, opts)
|
||||
const docs = await this.db.getMultiple<T>(ids, opts)
|
||||
span.addTags({ num_docs_found: docs.length })
|
||||
return docs
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -69,12 +73,14 @@ export class DDInstrumentedDatabase implements Database {
|
|||
idOrDoc: string | Document,
|
||||
rev?: string
|
||||
): Promise<DocumentDestroyResponse> {
|
||||
return tracer.trace("db.remove", span => {
|
||||
span?.addTags({ db_name: this.name, doc_id: idOrDoc })
|
||||
return tracer.trace("db.remove", async span => {
|
||||
span.addTags({ db_name: this.name, doc_id: idOrDoc, rev })
|
||||
const isDocument = typeof idOrDoc === "object"
|
||||
const id = isDocument ? idOrDoc._id! : idOrDoc
|
||||
rev = isDocument ? idOrDoc._rev : rev
|
||||
return this.db.remove(id, rev)
|
||||
const resp = await this.db.remove(id, rev)
|
||||
span.addTags({ ok: resp.ok })
|
||||
return resp
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -83,7 +89,11 @@ export class DDInstrumentedDatabase implements Database {
|
|||
opts?: { silenceErrors?: boolean }
|
||||
): Promise<void> {
|
||||
return tracer.trace("db.bulkRemove", span => {
|
||||
span?.addTags({ db_name: this.name, num_docs: documents.length })
|
||||
span.addTags({
|
||||
db_name: this.name,
|
||||
num_docs: documents.length,
|
||||
silence_errors: opts?.silenceErrors,
|
||||
})
|
||||
return this.db.bulkRemove(documents, opts)
|
||||
})
|
||||
}
|
||||
|
@ -92,15 +102,21 @@ export class DDInstrumentedDatabase implements Database {
|
|||
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)
|
||||
return tracer.trace("db.put", async span => {
|
||||
span.addTags({
|
||||
db_name: this.name,
|
||||
doc_id: document._id,
|
||||
force: opts?.force,
|
||||
})
|
||||
const resp = await this.db.put(document, opts)
|
||||
span.addTags({ ok: resp.ok })
|
||||
return resp
|
||||
})
|
||||
}
|
||||
|
||||
bulkDocs(documents: AnyDocument[]): Promise<DocumentBulkResponse[]> {
|
||||
return tracer.trace("db.bulkDocs", span => {
|
||||
span?.addTags({ db_name: this.name, num_docs: documents.length })
|
||||
span.addTags({ db_name: this.name, num_docs: documents.length })
|
||||
return this.db.bulkDocs(documents)
|
||||
})
|
||||
}
|
||||
|
@ -108,9 +124,15 @@ export class DDInstrumentedDatabase implements Database {
|
|||
allDocs<T extends Document | RowValue>(
|
||||
params: DatabaseQueryOpts
|
||||
): Promise<AllDocsResponse<T>> {
|
||||
return tracer.trace("db.allDocs", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
return this.db.allDocs(params)
|
||||
return tracer.trace("db.allDocs", async span => {
|
||||
span.addTags({ db_name: this.name, ...params })
|
||||
const resp = await this.db.allDocs<T>(params)
|
||||
span.addTags({
|
||||
total_rows: resp.total_rows,
|
||||
rows_length: resp.rows.length,
|
||||
offset: resp.offset,
|
||||
})
|
||||
return resp
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -118,57 +140,75 @@ export class DDInstrumentedDatabase implements Database {
|
|||
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)
|
||||
return tracer.trace("db.query", async span => {
|
||||
span.addTags({ db_name: this.name, view_name: viewName, ...params })
|
||||
const resp = await this.db.query<T>(viewName, params)
|
||||
span.addTags({
|
||||
total_rows: resp.total_rows,
|
||||
rows_length: resp.rows.length,
|
||||
offset: resp.offset,
|
||||
})
|
||||
return resp
|
||||
})
|
||||
}
|
||||
|
||||
destroy(): Promise<void | OkResponse> {
|
||||
return tracer.trace("db.destroy", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
return this.db.destroy()
|
||||
destroy(): Promise<OkResponse> {
|
||||
return tracer.trace("db.destroy", async span => {
|
||||
span.addTags({ db_name: this.name })
|
||||
const resp = await this.db.destroy()
|
||||
span.addTags({ ok: resp.ok })
|
||||
return resp
|
||||
})
|
||||
}
|
||||
|
||||
compact(): Promise<void | OkResponse> {
|
||||
return tracer.trace("db.compact", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
return this.db.compact()
|
||||
compact(): Promise<OkResponse> {
|
||||
return tracer.trace("db.compact", async span => {
|
||||
span.addTags({ db_name: this.name })
|
||||
const resp = await this.db.compact()
|
||||
span.addTags({ ok: resp.ok })
|
||||
return resp
|
||||
})
|
||||
}
|
||||
|
||||
dump(stream: Writable, opts?: DatabaseDumpOpts | undefined): Promise<any> {
|
||||
return tracer.trace("db.dump", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
span.addTags({
|
||||
db_name: this.name,
|
||||
batch_limit: opts?.batch_limit,
|
||||
batch_size: opts?.batch_size,
|
||||
style: opts?.style,
|
||||
timeout: opts?.timeout,
|
||||
num_doc_ids: opts?.doc_ids?.length,
|
||||
view: opts?.view,
|
||||
})
|
||||
return this.db.dump(stream, opts)
|
||||
})
|
||||
}
|
||||
|
||||
load(...args: any[]): Promise<any> {
|
||||
return tracer.trace("db.load", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
span.addTags({ db_name: this.name, num_args: args.length })
|
||||
return this.db.load(...args)
|
||||
})
|
||||
}
|
||||
|
||||
createIndex(...args: any[]): Promise<any> {
|
||||
return tracer.trace("db.createIndex", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
span.addTags({ db_name: this.name, num_args: args.length })
|
||||
return this.db.createIndex(...args)
|
||||
})
|
||||
}
|
||||
|
||||
deleteIndex(...args: any[]): Promise<any> {
|
||||
return tracer.trace("db.deleteIndex", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
span.addTags({ db_name: this.name, num_args: args.length })
|
||||
return this.db.deleteIndex(...args)
|
||||
})
|
||||
}
|
||||
|
||||
getIndexes(...args: any[]): Promise<any> {
|
||||
return tracer.trace("db.getIndexes", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
span.addTags({ db_name: this.name, num_args: args.length })
|
||||
return this.db.getIndexes(...args)
|
||||
})
|
||||
}
|
||||
|
@ -177,22 +217,27 @@ export class DDInstrumentedDatabase implements Database {
|
|||
sql: string,
|
||||
parameters?: SqlQueryBinding
|
||||
): Promise<T[]> {
|
||||
return tracer.trace("db.sql", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
return this.db.sql(sql, parameters)
|
||||
return tracer.trace("db.sql", async span => {
|
||||
span.addTags({ db_name: this.name, num_bindings: parameters?.length })
|
||||
const resp = await this.db.sql<T>(sql, parameters)
|
||||
span.addTags({ num_rows: resp.length })
|
||||
return resp
|
||||
})
|
||||
}
|
||||
|
||||
sqlPurgeDocument(docIds: string[] | string): Promise<void> {
|
||||
return tracer.trace("db.sqlPurgeDocument", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
span.addTags({
|
||||
db_name: this.name,
|
||||
num_docs: Array.isArray(docIds) ? docIds.length : 1,
|
||||
})
|
||||
return this.db.sqlPurgeDocument(docIds)
|
||||
})
|
||||
}
|
||||
|
||||
sqlDiskCleanup(): Promise<void> {
|
||||
return tracer.trace("db.sqlDiskCleanup", span => {
|
||||
span?.addTags({ db_name: this.name })
|
||||
span.addTags({ db_name: this.name })
|
||||
return this.db.sqlDiskCleanup()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 4185d9c617619694ebb1dcd318cad7cc4c94c8f1
|
||||
Subproject commit 7f5c0a2fb9a07cdfa71efde69da181a93352985d
|
|
@ -63,6 +63,7 @@
|
|||
"@bull-board/koa": "5.10.2",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
"@google-cloud/firestore": "7.8.0",
|
||||
"@koa/cors": "5.0.0",
|
||||
"@koa/router": "13.1.0",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
|
@ -80,8 +81,8 @@
|
|||
"cookies": "0.8.0",
|
||||
"csvtojson": "2.0.10",
|
||||
"curlconverter": "3.21.0",
|
||||
"dd-trace": "5.23.0",
|
||||
"dayjs": "^1.10.8",
|
||||
"dd-trace": "5.26.0",
|
||||
"dotenv": "8.2.0",
|
||||
"form-data": "4.0.0",
|
||||
"global-agent": "3.0.0",
|
||||
|
@ -131,6 +132,7 @@
|
|||
"xml2js": "0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
"@babel/preset-env": "7.16.11",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@swc/core": "1.3.71",
|
||||
|
@ -140,6 +142,7 @@
|
|||
"@types/jest": "29.5.5",
|
||||
"@types/koa": "2.13.4",
|
||||
"@types/koa-send": "^4.1.6",
|
||||
"@types/koa__cors": "5.0.0",
|
||||
"@types/koa__router": "12.0.4",
|
||||
"@types/lodash": "4.14.200",
|
||||
"@types/mssql": "9.1.5",
|
||||
|
@ -172,8 +175,7 @@
|
|||
"tsconfig-paths": "4.0.0",
|
||||
"typescript": "5.5.2",
|
||||
"update-dotenv": "1.1.1",
|
||||
"yargs": "13.2.4",
|
||||
"@babel/core": "^7.22.5"
|
||||
"yargs": "13.2.4"
|
||||
},
|
||||
"nx": {
|
||||
"targets": {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,13 @@ components:
|
|||
description: The ID of the table which this request is targeting.
|
||||
schema:
|
||||
type: string
|
||||
viewId:
|
||||
in: path
|
||||
name: viewId
|
||||
required: true
|
||||
description: The ID of the view which this request is targeting.
|
||||
schema:
|
||||
type: string
|
||||
rowId:
|
||||
in: path
|
||||
name: rowId
|
||||
|
@ -36,7 +43,7 @@ components:
|
|||
required: true
|
||||
description: The ID of the app which this request is targeting.
|
||||
schema:
|
||||
default: "{{ appId }}"
|
||||
default: "{{appId}}"
|
||||
type: string
|
||||
appIdUrl:
|
||||
in: path
|
||||
|
@ -44,7 +51,7 @@ components:
|
|||
required: true
|
||||
description: The ID of the app which this request is targeting.
|
||||
schema:
|
||||
default: "{{ appId }}"
|
||||
default: "{{appId}}"
|
||||
type: string
|
||||
queryId:
|
||||
in: path
|
||||
|
@ -442,6 +449,74 @@ components:
|
|||
# TYPE budibase_quota_limit_automations gauge
|
||||
|
||||
budibase_quota_limit_automations 9007199254740991
|
||||
view:
|
||||
value:
|
||||
data:
|
||||
name: peopleView
|
||||
tableId: ta_896a325f7e8147d2a2cda93c5d236511
|
||||
schema:
|
||||
name:
|
||||
visible: true
|
||||
readonly: false
|
||||
order: 1
|
||||
width: 300
|
||||
age:
|
||||
visible: true
|
||||
readonly: true
|
||||
order: 2
|
||||
width: 200
|
||||
salary:
|
||||
visible: false
|
||||
readonly: false
|
||||
query:
|
||||
logicalOperator: all
|
||||
onEmptyFilter: none
|
||||
groups:
|
||||
- logicalOperator: any
|
||||
filters:
|
||||
- operator: string
|
||||
field: name
|
||||
value: John
|
||||
- operator: range
|
||||
field: age
|
||||
value:
|
||||
low: 18
|
||||
high: 100
|
||||
primaryDisplay: name
|
||||
views:
|
||||
value:
|
||||
data:
|
||||
- name: peopleView
|
||||
tableId: ta_896a325f7e8147d2a2cda93c5d236511
|
||||
schema:
|
||||
name:
|
||||
visible: true
|
||||
readonly: false
|
||||
order: 1
|
||||
width: 300
|
||||
age:
|
||||
visible: true
|
||||
readonly: true
|
||||
order: 2
|
||||
width: 200
|
||||
salary:
|
||||
visible: false
|
||||
readonly: false
|
||||
query:
|
||||
logicalOperator: all
|
||||
onEmptyFilter: none
|
||||
groups:
|
||||
- logicalOperator: any
|
||||
filters:
|
||||
- operator: string
|
||||
field: name
|
||||
value: John
|
||||
- operator: range
|
||||
field: age
|
||||
value:
|
||||
low: 18
|
||||
high: 100
|
||||
primaryDisplay: name
|
||||
securitySchemes:
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
|
@ -761,7 +836,6 @@ components:
|
|||
enum:
|
||||
- static
|
||||
- dynamic
|
||||
- ai
|
||||
description: Defines whether this is a static or dynamic formula.
|
||||
- type: object
|
||||
properties:
|
||||
|
@ -931,7 +1005,6 @@ components:
|
|||
enum:
|
||||
- static
|
||||
- dynamic
|
||||
- ai
|
||||
description: Defines whether this is a static or dynamic formula.
|
||||
- type: object
|
||||
properties:
|
||||
|
@ -1108,7 +1181,6 @@ components:
|
|||
enum:
|
||||
- static
|
||||
- dynamic
|
||||
- ai
|
||||
description: Defines whether this is a static or dynamic formula.
|
||||
- type: object
|
||||
properties:
|
||||
|
@ -1704,6 +1776,644 @@ components:
|
|||
- userIds
|
||||
required:
|
||||
- data
|
||||
view:
|
||||
description: The view to be created/updated.
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- schema
|
||||
- tableId
|
||||
properties:
|
||||
name:
|
||||
description: The name of the view.
|
||||
type: string
|
||||
tableId:
|
||||
description: The ID of the table this view is based on.
|
||||
type: string
|
||||
type:
|
||||
description: The type of view - standard (empty value) or calculation.
|
||||
type: string
|
||||
enum:
|
||||
- calculation
|
||||
primaryDisplay:
|
||||
type: string
|
||||
description: A column used to display rows from this view - usually used when
|
||||
rendered in tables.
|
||||
query:
|
||||
description: Search parameters for view
|
||||
type: object
|
||||
required: []
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
onEmptyFilter:
|
||||
description: If no filters match, should the view return all rows, or no rows.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- none
|
||||
groups:
|
||||
description: A grouping of filters to be applied.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
filters:
|
||||
description: A list of filters to apply
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
description: The type of search operation which is being performed.
|
||||
enum:
|
||||
- equal
|
||||
- notEqual
|
||||
- empty
|
||||
- notEmpty
|
||||
- fuzzy
|
||||
- string
|
||||
- contains
|
||||
- notContains
|
||||
- containsAny
|
||||
- oneOf
|
||||
- range
|
||||
field:
|
||||
type: string
|
||||
description: The field in the view to perform the search on.
|
||||
value:
|
||||
description: The value to search for - the type will depend on the operator in
|
||||
use.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- type: object
|
||||
- type: array
|
||||
groups:
|
||||
description: A grouping of filters to be applied.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
filters:
|
||||
description: A list of filters to apply
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
description: The type of search operation which is being performed.
|
||||
enum:
|
||||
- equal
|
||||
- notEqual
|
||||
- empty
|
||||
- notEmpty
|
||||
- fuzzy
|
||||
- string
|
||||
- contains
|
||||
- notContains
|
||||
- containsAny
|
||||
- oneOf
|
||||
- range
|
||||
field:
|
||||
type: string
|
||||
description: The field in the view to perform the search on.
|
||||
value:
|
||||
description: The value to search for - the type will depend on the operator in
|
||||
use.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- type: object
|
||||
- type: array
|
||||
sort:
|
||||
type: object
|
||||
required:
|
||||
- field
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
description: The field from the table/view schema to sort on.
|
||||
order:
|
||||
type: string
|
||||
description: The order in which to sort.
|
||||
enum:
|
||||
- ascending
|
||||
- descending
|
||||
type:
|
||||
type: string
|
||||
description: The type of sort to perform (by number, or by alphabetically).
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
visible:
|
||||
type: boolean
|
||||
description: Defines whether the column is visible or not - rows
|
||||
retrieved/updated through this view will not be able to
|
||||
access it.
|
||||
readonly:
|
||||
type: boolean
|
||||
description: "When used in combination with 'visible: true' the column will be
|
||||
visible in row responses but cannot be updated."
|
||||
order:
|
||||
type: integer
|
||||
description: A number defining where the column shows up in tables, lowest being
|
||||
first.
|
||||
width:
|
||||
type: integer
|
||||
description: A width for the column, defined in pixels - this affects rendering
|
||||
in tables.
|
||||
column:
|
||||
type: array
|
||||
description: If this is a relationship column, we can set the columns we wish to
|
||||
include
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
readonly:
|
||||
type: boolean
|
||||
- type: object
|
||||
properties:
|
||||
calculationType:
|
||||
type: string
|
||||
description: This column should be built from a calculation, specifying a type
|
||||
and field. It is important to note when a calculation is
|
||||
configured all non-calculation columns will be used for
|
||||
grouping.
|
||||
enum:
|
||||
- sum
|
||||
- avg
|
||||
- count
|
||||
- min
|
||||
- max
|
||||
field:
|
||||
type: string
|
||||
description: The field from the table to perform the calculation on.
|
||||
distinct:
|
||||
type: boolean
|
||||
description: Can be used in tandem with the count calculation type, to count
|
||||
unique entries.
|
||||
viewOutput:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
description: The view to be created/updated.
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- schema
|
||||
- tableId
|
||||
- id
|
||||
properties:
|
||||
name:
|
||||
description: The name of the view.
|
||||
type: string
|
||||
tableId:
|
||||
description: The ID of the table this view is based on.
|
||||
type: string
|
||||
type:
|
||||
description: The type of view - standard (empty value) or calculation.
|
||||
type: string
|
||||
enum:
|
||||
- calculation
|
||||
primaryDisplay:
|
||||
type: string
|
||||
description: A column used to display rows from this view - usually used when
|
||||
rendered in tables.
|
||||
query:
|
||||
description: Search parameters for view
|
||||
type: object
|
||||
required: []
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
onEmptyFilter:
|
||||
description: If no filters match, should the view return all rows, or no rows.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- none
|
||||
groups:
|
||||
description: A grouping of filters to be applied.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
filters:
|
||||
description: A list of filters to apply
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
description: The type of search operation which is being performed.
|
||||
enum:
|
||||
- equal
|
||||
- notEqual
|
||||
- empty
|
||||
- notEmpty
|
||||
- fuzzy
|
||||
- string
|
||||
- contains
|
||||
- notContains
|
||||
- containsAny
|
||||
- oneOf
|
||||
- range
|
||||
field:
|
||||
type: string
|
||||
description: The field in the view to perform the search on.
|
||||
value:
|
||||
description: The value to search for - the type will depend on the operator in
|
||||
use.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- type: object
|
||||
- type: array
|
||||
groups:
|
||||
description: A grouping of filters to be applied.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
filters:
|
||||
description: A list of filters to apply
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
description: The type of search operation which is being performed.
|
||||
enum:
|
||||
- equal
|
||||
- notEqual
|
||||
- empty
|
||||
- notEmpty
|
||||
- fuzzy
|
||||
- string
|
||||
- contains
|
||||
- notContains
|
||||
- containsAny
|
||||
- oneOf
|
||||
- range
|
||||
field:
|
||||
type: string
|
||||
description: The field in the view to perform the search on.
|
||||
value:
|
||||
description: The value to search for - the type will depend on the operator in
|
||||
use.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- type: object
|
||||
- type: array
|
||||
sort:
|
||||
type: object
|
||||
required:
|
||||
- field
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
description: The field from the table/view schema to sort on.
|
||||
order:
|
||||
type: string
|
||||
description: The order in which to sort.
|
||||
enum:
|
||||
- ascending
|
||||
- descending
|
||||
type:
|
||||
type: string
|
||||
description: The type of sort to perform (by number, or by alphabetically).
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
visible:
|
||||
type: boolean
|
||||
description: Defines whether the column is visible or not - rows
|
||||
retrieved/updated through this view will not be able
|
||||
to access it.
|
||||
readonly:
|
||||
type: boolean
|
||||
description: "When used in combination with 'visible: true' the column will be
|
||||
visible in row responses but cannot be updated."
|
||||
order:
|
||||
type: integer
|
||||
description: A number defining where the column shows up in tables, lowest being
|
||||
first.
|
||||
width:
|
||||
type: integer
|
||||
description: A width for the column, defined in pixels - this affects rendering
|
||||
in tables.
|
||||
column:
|
||||
type: array
|
||||
description: If this is a relationship column, we can set the columns we wish to
|
||||
include
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
readonly:
|
||||
type: boolean
|
||||
- type: object
|
||||
properties:
|
||||
calculationType:
|
||||
type: string
|
||||
description: This column should be built from a calculation, specifying a type
|
||||
and field. It is important to note when a calculation
|
||||
is configured all non-calculation columns will be used
|
||||
for grouping.
|
||||
enum:
|
||||
- sum
|
||||
- avg
|
||||
- count
|
||||
- min
|
||||
- max
|
||||
field:
|
||||
type: string
|
||||
description: The field from the table to perform the calculation on.
|
||||
distinct:
|
||||
type: boolean
|
||||
description: Can be used in tandem with the count calculation type, to count
|
||||
unique entries.
|
||||
id:
|
||||
description: The ID of the view.
|
||||
type: string
|
||||
required:
|
||||
- data
|
||||
viewSearch:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items:
|
||||
description: The view to be created/updated.
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
- schema
|
||||
- tableId
|
||||
- id
|
||||
properties:
|
||||
name:
|
||||
description: The name of the view.
|
||||
type: string
|
||||
tableId:
|
||||
description: The ID of the table this view is based on.
|
||||
type: string
|
||||
type:
|
||||
description: The type of view - standard (empty value) or calculation.
|
||||
type: string
|
||||
enum:
|
||||
- calculation
|
||||
primaryDisplay:
|
||||
type: string
|
||||
description: A column used to display rows from this view - usually used when
|
||||
rendered in tables.
|
||||
query:
|
||||
description: Search parameters for view
|
||||
type: object
|
||||
required: []
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
onEmptyFilter:
|
||||
description: If no filters match, should the view return all rows, or no rows.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- none
|
||||
groups:
|
||||
description: A grouping of filters to be applied.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
filters:
|
||||
description: A list of filters to apply
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
description: The type of search operation which is being performed.
|
||||
enum:
|
||||
- equal
|
||||
- notEqual
|
||||
- empty
|
||||
- notEmpty
|
||||
- fuzzy
|
||||
- string
|
||||
- contains
|
||||
- notContains
|
||||
- containsAny
|
||||
- oneOf
|
||||
- range
|
||||
field:
|
||||
type: string
|
||||
description: The field in the view to perform the search on.
|
||||
value:
|
||||
description: The value to search for - the type will depend on the operator in
|
||||
use.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- type: object
|
||||
- type: array
|
||||
groups:
|
||||
description: A grouping of filters to be applied.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
logicalOperator:
|
||||
description: When using groups this defines whether all of the filters must
|
||||
match, or only one of them.
|
||||
type: string
|
||||
enum:
|
||||
- all
|
||||
- any
|
||||
filters:
|
||||
description: A list of filters to apply
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
operator:
|
||||
type: string
|
||||
description: The type of search operation which is being performed.
|
||||
enum:
|
||||
- equal
|
||||
- notEqual
|
||||
- empty
|
||||
- notEmpty
|
||||
- fuzzy
|
||||
- string
|
||||
- contains
|
||||
- notContains
|
||||
- containsAny
|
||||
- oneOf
|
||||
- range
|
||||
field:
|
||||
type: string
|
||||
description: The field in the view to perform the search on.
|
||||
value:
|
||||
description: The value to search for - the type will depend on the operator in
|
||||
use.
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- type: object
|
||||
- type: array
|
||||
sort:
|
||||
type: object
|
||||
required:
|
||||
- field
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
description: The field from the table/view schema to sort on.
|
||||
order:
|
||||
type: string
|
||||
description: The order in which to sort.
|
||||
enum:
|
||||
- ascending
|
||||
- descending
|
||||
type:
|
||||
type: string
|
||||
description: The type of sort to perform (by number, or by alphabetically).
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
visible:
|
||||
type: boolean
|
||||
description: Defines whether the column is visible or not - rows
|
||||
retrieved/updated through this view will not be able
|
||||
to access it.
|
||||
readonly:
|
||||
type: boolean
|
||||
description: "When used in combination with 'visible: true' the column will be
|
||||
visible in row responses but cannot be updated."
|
||||
order:
|
||||
type: integer
|
||||
description: A number defining where the column shows up in tables, lowest being
|
||||
first.
|
||||
width:
|
||||
type: integer
|
||||
description: A width for the column, defined in pixels - this affects rendering
|
||||
in tables.
|
||||
column:
|
||||
type: array
|
||||
description: If this is a relationship column, we can set the columns we wish to
|
||||
include
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
readonly:
|
||||
type: boolean
|
||||
- type: object
|
||||
properties:
|
||||
calculationType:
|
||||
type: string
|
||||
description: This column should be built from a calculation, specifying a type
|
||||
and field. It is important to note when a
|
||||
calculation is configured all non-calculation
|
||||
columns will be used for grouping.
|
||||
enum:
|
||||
- sum
|
||||
- avg
|
||||
- count
|
||||
- min
|
||||
- max
|
||||
field:
|
||||
type: string
|
||||
description: The field from the table to perform the calculation on.
|
||||
distinct:
|
||||
type: boolean
|
||||
description: Can be used in tandem with the count calculation type, to count
|
||||
unique entries.
|
||||
id:
|
||||
description: The ID of the view.
|
||||
type: string
|
||||
required:
|
||||
- data
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
paths:
|
||||
|
@ -2136,6 +2846,32 @@ paths:
|
|||
examples:
|
||||
search:
|
||||
$ref: "#/components/examples/rows"
|
||||
"/views/{viewId}/rows/search":
|
||||
post:
|
||||
operationId: rowViewSearch
|
||||
summary: Search for rows in a view
|
||||
tags:
|
||||
- rows
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/viewId"
|
||||
- $ref: "#/components/parameters/appId"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/rowSearch"
|
||||
responses:
|
||||
"200":
|
||||
description: The response will contain an array of rows that match the search
|
||||
parameters.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/searchOutput"
|
||||
examples:
|
||||
search:
|
||||
$ref: "#/components/examples/rows"
|
||||
/tables:
|
||||
post:
|
||||
operationId: tableCreate
|
||||
|
@ -2359,4 +3095,123 @@ paths:
|
|||
examples:
|
||||
users:
|
||||
$ref: "#/components/examples/users"
|
||||
/views:
|
||||
post:
|
||||
operationId: viewCreate
|
||||
summary: Create a view
|
||||
description: Create a view, this can be against an internal or external table.
|
||||
tags:
|
||||
- views
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/appId"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/view"
|
||||
examples:
|
||||
view:
|
||||
$ref: "#/components/examples/view"
|
||||
responses:
|
||||
"200":
|
||||
description: Returns the created view, including the ID which has been generated
|
||||
for it.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/viewOutput"
|
||||
examples:
|
||||
view:
|
||||
$ref: "#/components/examples/view"
|
||||
"/views/{viewId}":
|
||||
put:
|
||||
operationId: viewUpdate
|
||||
summary: Update a view
|
||||
description: Update a view, this can be against an internal or external table.
|
||||
tags:
|
||||
- views
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/viewId"
|
||||
- $ref: "#/components/parameters/appId"
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/view"
|
||||
examples:
|
||||
view:
|
||||
$ref: "#/components/examples/view"
|
||||
responses:
|
||||
"200":
|
||||
description: Returns the updated view.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/viewOutput"
|
||||
examples:
|
||||
view:
|
||||
$ref: "#/components/examples/view"
|
||||
delete:
|
||||
operationId: viewDestroy
|
||||
summary: Delete a view
|
||||
description: Delete a view, this can be against an internal or external table.
|
||||
tags:
|
||||
- views
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/viewId"
|
||||
- $ref: "#/components/parameters/appId"
|
||||
responses:
|
||||
"200":
|
||||
description: Returns the deleted view.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/viewOutput"
|
||||
examples:
|
||||
view:
|
||||
$ref: "#/components/examples/view"
|
||||
get:
|
||||
operationId: viewGetById
|
||||
summary: Retrieve a view
|
||||
description: Lookup a view, this could be internal or external.
|
||||
tags:
|
||||
- views
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/viewId"
|
||||
- $ref: "#/components/parameters/appId"
|
||||
responses:
|
||||
"200":
|
||||
description: Returns the retrieved view.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/viewOutput"
|
||||
examples:
|
||||
view:
|
||||
$ref: "#/components/examples/view"
|
||||
/views/search:
|
||||
post:
|
||||
operationId: viewSearch
|
||||
summary: Search for views
|
||||
description: Based on view properties (currently only name) search for views.
|
||||
tags:
|
||||
- views
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/appId"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/nameSearch"
|
||||
responses:
|
||||
"200":
|
||||
description: Returns the found views, based on the search parameters.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/viewSearch"
|
||||
examples:
|
||||
views:
|
||||
$ref: "#/components/examples/views"
|
||||
tags: []
|
||||
|
|
|
@ -8,6 +8,16 @@ export const tableId = {
|
|||
},
|
||||
}
|
||||
|
||||
export const viewId = {
|
||||
in: "path",
|
||||
name: "viewId",
|
||||
required: true,
|
||||
description: "The ID of the view which this request is targeting.",
|
||||
schema: {
|
||||
type: "string",
|
||||
},
|
||||
}
|
||||
|
||||
export const rowId = {
|
||||
in: "path",
|
||||
name: "rowId",
|
||||
|
|
|
@ -6,6 +6,7 @@ import user from "./user"
|
|||
import metrics from "./metrics"
|
||||
import misc from "./misc"
|
||||
import roles from "./roles"
|
||||
import view from "./view"
|
||||
|
||||
export const examples = {
|
||||
...application.getExamples(),
|
||||
|
@ -16,6 +17,7 @@ export const examples = {
|
|||
...misc.getExamples(),
|
||||
...metrics.getExamples(),
|
||||
...roles.getExamples(),
|
||||
...view.getExamples(),
|
||||
}
|
||||
|
||||
export const schemas = {
|
||||
|
@ -26,4 +28,5 @@ export const schemas = {
|
|||
...user.getSchemas(),
|
||||
...misc.getSchemas(),
|
||||
...roles.getSchemas(),
|
||||
...view.getSchemas(),
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { object } from "./utils"
|
||||
import Resource from "./utils/Resource"
|
||||
|
||||
export default new Resource().setSchemas({
|
||||
rowSearch: object(
|
||||
{
|
||||
query: {
|
||||
export const searchSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
allOr: {
|
||||
|
@ -93,7 +90,12 @@ export default new Resource().setSchemas({
|
|||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default new Resource().setSchemas({
|
||||
rowSearch: object(
|
||||
{
|
||||
query: searchSchema,
|
||||
paginate: {
|
||||
type: "boolean",
|
||||
description: "Enables pagination, by default this is disabled.",
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
import { object } from "./utils"
|
||||
import Resource from "./utils/Resource"
|
||||
import {
|
||||
ArrayOperator,
|
||||
BasicOperator,
|
||||
CalculationType,
|
||||
RangeOperator,
|
||||
SortOrder,
|
||||
SortType,
|
||||
} from "@budibase/types"
|
||||
import { cloneDeep } from "lodash"
|
||||
|
||||
const view = {
|
||||
name: "peopleView",
|
||||
tableId: "ta_896a325f7e8147d2a2cda93c5d236511",
|
||||
schema: {
|
||||
name: {
|
||||
visible: true,
|
||||
readonly: false,
|
||||
order: 1,
|
||||
width: 300,
|
||||
},
|
||||
age: {
|
||||
visible: true,
|
||||
readonly: true,
|
||||
order: 2,
|
||||
width: 200,
|
||||
},
|
||||
salary: {
|
||||
visible: false,
|
||||
readonly: false,
|
||||
},
|
||||
},
|
||||
query: {
|
||||
logicalOperator: "all",
|
||||
onEmptyFilter: "none",
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: "any",
|
||||
filters: [
|
||||
{ operator: "string", field: "name", value: "John" },
|
||||
{ operator: "range", field: "age", value: { low: 18, high: 100 } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
primaryDisplay: "name",
|
||||
}
|
||||
|
||||
const baseColumnDef = {
|
||||
visible: {
|
||||
type: "boolean",
|
||||
description:
|
||||
"Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it.",
|
||||
},
|
||||
readonly: {
|
||||
type: "boolean",
|
||||
description:
|
||||
"When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated.",
|
||||
},
|
||||
order: {
|
||||
type: "integer",
|
||||
description:
|
||||
"A number defining where the column shows up in tables, lowest being first.",
|
||||
},
|
||||
width: {
|
||||
type: "integer",
|
||||
description:
|
||||
"A width for the column, defined in pixels - this affects rendering in tables.",
|
||||
},
|
||||
column: {
|
||||
type: "array",
|
||||
description:
|
||||
"If this is a relationship column, we can set the columns we wish to include",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
readonly: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const logicalOperator = {
|
||||
description:
|
||||
"When using groups this defines whether all of the filters must match, or only one of them.",
|
||||
type: "string",
|
||||
enum: ["all", "any"],
|
||||
}
|
||||
|
||||
const filterGroup = {
|
||||
description: "A grouping of filters to be applied.",
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
logicalOperator,
|
||||
filters: {
|
||||
description: "A list of filters to apply",
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
operator: {
|
||||
type: "string",
|
||||
description:
|
||||
"The type of search operation which is being performed.",
|
||||
enum: [
|
||||
...Object.values(BasicOperator),
|
||||
...Object.values(ArrayOperator),
|
||||
...Object.values(RangeOperator),
|
||||
],
|
||||
},
|
||||
field: {
|
||||
type: "string",
|
||||
description: "The field in the view to perform the search on.",
|
||||
},
|
||||
value: {
|
||||
description:
|
||||
"The value to search for - the type will depend on the operator in use.",
|
||||
oneOf: [
|
||||
{ type: "string" },
|
||||
{ type: "number" },
|
||||
{ type: "boolean" },
|
||||
{ type: "object" },
|
||||
{ type: "array" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// have to clone to avoid constantly recursive structure - we can't represent this easily
|
||||
const layeredFilterGroup: any = cloneDeep(filterGroup)
|
||||
layeredFilterGroup.items.properties.groups = filterGroup
|
||||
|
||||
const viewQuerySchema = {
|
||||
description: "Search parameters for view",
|
||||
type: "object",
|
||||
required: [],
|
||||
properties: {
|
||||
logicalOperator,
|
||||
onEmptyFilter: {
|
||||
description:
|
||||
"If no filters match, should the view return all rows, or no rows.",
|
||||
type: "string",
|
||||
enum: ["all", "none"],
|
||||
},
|
||||
groups: layeredFilterGroup,
|
||||
},
|
||||
}
|
||||
|
||||
const viewSchema = {
|
||||
description: "The view to be created/updated.",
|
||||
type: "object",
|
||||
required: ["name", "schema", "tableId"],
|
||||
properties: {
|
||||
name: {
|
||||
description: "The name of the view.",
|
||||
type: "string",
|
||||
},
|
||||
tableId: {
|
||||
description: "The ID of the table this view is based on.",
|
||||
type: "string",
|
||||
},
|
||||
type: {
|
||||
description: "The type of view - standard (empty value) or calculation.",
|
||||
type: "string",
|
||||
enum: ["calculation"],
|
||||
},
|
||||
primaryDisplay: {
|
||||
type: "string",
|
||||
description:
|
||||
"A column used to display rows from this view - usually used when rendered in tables.",
|
||||
},
|
||||
query: viewQuerySchema,
|
||||
sort: {
|
||||
type: "object",
|
||||
required: ["field"],
|
||||
properties: {
|
||||
field: {
|
||||
type: "string",
|
||||
description: "The field from the table/view schema to sort on.",
|
||||
},
|
||||
order: {
|
||||
type: "string",
|
||||
description: "The order in which to sort.",
|
||||
enum: Object.values(SortOrder),
|
||||
},
|
||||
type: {
|
||||
type: "string",
|
||||
description:
|
||||
"The type of sort to perform (by number, or by alphabetically).",
|
||||
enum: Object.values(SortType),
|
||||
},
|
||||
},
|
||||
},
|
||||
schema: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
oneOf: [
|
||||
{
|
||||
type: "object",
|
||||
properties: baseColumnDef,
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
calculationType: {
|
||||
type: "string",
|
||||
description:
|
||||
"This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.",
|
||||
enum: Object.values(CalculationType),
|
||||
},
|
||||
field: {
|
||||
type: "string",
|
||||
description:
|
||||
"The field from the table to perform the calculation on.",
|
||||
},
|
||||
distinct: {
|
||||
type: "boolean",
|
||||
description:
|
||||
"Can be used in tandem with the count calculation type, to count unique entries.",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const viewOutputSchema = {
|
||||
...viewSchema,
|
||||
properties: {
|
||||
...viewSchema.properties,
|
||||
id: {
|
||||
description: "The ID of the view.",
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
required: [...viewSchema.required, "id"],
|
||||
}
|
||||
|
||||
export default new Resource()
|
||||
.setExamples({
|
||||
view: {
|
||||
value: {
|
||||
data: view,
|
||||
},
|
||||
},
|
||||
views: {
|
||||
value: {
|
||||
data: [view],
|
||||
},
|
||||
},
|
||||
})
|
||||
.setSchemas({
|
||||
view: viewSchema,
|
||||
viewOutput: object({
|
||||
data: viewOutputSchema,
|
||||
}),
|
||||
viewSearch: object({
|
||||
data: {
|
||||
type: "array",
|
||||
items: viewOutputSchema,
|
||||
},
|
||||
}),
|
||||
})
|
|
@ -1,6 +1,7 @@
|
|||
import { Application } from "./types"
|
||||
import { RequiredKeys } from "@budibase/types"
|
||||
|
||||
function application(body: any): Application {
|
||||
function application(body: any): RequiredKeys<Application> {
|
||||
let app = body?.application ? body.application : body
|
||||
return {
|
||||
_id: app.appId,
|
||||
|
|
|
@ -3,6 +3,7 @@ import applications from "./applications"
|
|||
import users from "./users"
|
||||
import rows from "./rows"
|
||||
import queries from "./queries"
|
||||
import views from "./views"
|
||||
|
||||
export default {
|
||||
...tables,
|
||||
|
@ -10,4 +11,5 @@ export default {
|
|||
...users,
|
||||
...rows,
|
||||
...queries,
|
||||
...views,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Query, ExecuteQuery } from "./types"
|
||||
import { RequiredKeys } from "@budibase/types"
|
||||
|
||||
function query(body: any): Query {
|
||||
function query(body: any): RequiredKeys<Query> {
|
||||
return {
|
||||
_id: body._id,
|
||||
datasourceId: body.datasourceId,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Row, RowSearch } from "./types"
|
||||
import { RequiredKeys } from "@budibase/types"
|
||||
|
||||
function row(body: any): Row {
|
||||
function row(body: any): RequiredKeys<Row> {
|
||||
delete body._rev
|
||||
// have to input everything, since structure unknown
|
||||
return {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Table } from "./types"
|
||||
import { RequiredKeys } from "@budibase/types"
|
||||
|
||||
function table(body: any): Table {
|
||||
function table(body: any): RequiredKeys<Table> {
|
||||
return {
|
||||
_id: body._id,
|
||||
name: body.name,
|
||||
|
|
|
@ -9,6 +9,9 @@ export type CreateApplicationParams = components["schemas"]["application"]
|
|||
export type Table = components["schemas"]["tableOutput"]["data"]
|
||||
export type CreateTableParams = components["schemas"]["table"]
|
||||
|
||||
export type View = components["schemas"]["viewOutput"]["data"]
|
||||
export type CreateViewParams = components["schemas"]["view"]
|
||||
|
||||
export type Row = components["schemas"]["rowOutput"]["data"]
|
||||
export type RowSearch = components["schemas"]["searchOutput"]
|
||||
export type CreateRowParams = components["schemas"]["row"]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { User } from "./types"
|
||||
import { RequiredKeys } from "@budibase/types"
|
||||
|
||||
function user(body: any): User {
|
||||
function user(body: any): RequiredKeys<User> {
|
||||
return {
|
||||
_id: body._id,
|
||||
email: body.email,
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { View } from "./types"
|
||||
import { ViewV2, Ctx, RequiredKeys } from "@budibase/types"
|
||||
import { dataFilters } from "@budibase/shared-core"
|
||||
|
||||
function view(body: ViewV2): RequiredKeys<View> {
|
||||
return {
|
||||
id: body.id,
|
||||
tableId: body.tableId,
|
||||
type: body.type,
|
||||
name: body.name,
|
||||
schema: body.schema!,
|
||||
primaryDisplay: body.primaryDisplay,
|
||||
query: dataFilters.buildQuery(body.query),
|
||||
sort: body.sort,
|
||||
}
|
||||
}
|
||||
|
||||
function mapView(ctx: Ctx<{ data: ViewV2 }>): { data: View } {
|
||||
return {
|
||||
data: view(ctx.body.data),
|
||||
}
|
||||
}
|
||||
|
||||
function mapViews(ctx: Ctx<{ data: ViewV2[] }>): { data: View[] } {
|
||||
const views = ctx.body.data.map((body: ViewV2) => view(body))
|
||||
return { data: views }
|
||||
}
|
||||
|
||||
export default {
|
||||
mapView,
|
||||
mapViews,
|
||||
}
|
|
@ -22,13 +22,13 @@ export function fixRow(row: Row, params: any) {
|
|||
return row
|
||||
}
|
||||
|
||||
export async function search(ctx: UserCtx, next: Next) {
|
||||
function buildSearchRequestBody(ctx: UserCtx) {
|
||||
let { sort, paginate, bookmark, limit, query } = ctx.request.body
|
||||
// update the body to the correct format of the internal search
|
||||
if (!sort) {
|
||||
sort = {}
|
||||
}
|
||||
ctx.request.body = {
|
||||
return {
|
||||
sort: sort.column,
|
||||
sortType: sort.type,
|
||||
sortOrder: sort.order,
|
||||
|
@ -37,10 +37,23 @@ export async function search(ctx: UserCtx, next: Next) {
|
|||
limit,
|
||||
query,
|
||||
}
|
||||
}
|
||||
|
||||
export async function search(ctx: UserCtx, next: Next) {
|
||||
ctx.request.body = buildSearchRequestBody(ctx)
|
||||
await rowController.search(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function viewSearch(ctx: UserCtx, next: Next) {
|
||||
ctx.request.body = buildSearchRequestBody(ctx)
|
||||
ctx.params = {
|
||||
viewId: ctx.params.viewId,
|
||||
}
|
||||
await rowController.views.searchView(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function create(ctx: UserCtx, next: Next) {
|
||||
ctx.request.body = fixRow(ctx.request.body, ctx.params)
|
||||
await rowController.save(ctx)
|
||||
|
@ -79,4 +92,5 @@ export default {
|
|||
update,
|
||||
destroy,
|
||||
search,
|
||||
viewSearch,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
import { search as stringSearch } from "./utils"
|
||||
import * as controller from "../view"
|
||||
import { ViewV2, UserCtx, UISearchFilter, PublicAPIView } from "@budibase/types"
|
||||
import { Next } from "koa"
|
||||
import { merge } from "lodash"
|
||||
|
||||
function viewRequest(view: PublicAPIView, params?: { viewId: string }) {
|
||||
const viewV2: ViewV2 = view
|
||||
if (!viewV2) {
|
||||
return viewV2
|
||||
}
|
||||
if (params?.viewId) {
|
||||
viewV2.id = params.viewId
|
||||
}
|
||||
if (!view.query) {
|
||||
viewV2.query = {}
|
||||
} else {
|
||||
// public API only has one form of query
|
||||
viewV2.queryUI = viewV2.query as UISearchFilter
|
||||
}
|
||||
viewV2.version = 2
|
||||
return viewV2
|
||||
}
|
||||
|
||||
function viewResponse(view: ViewV2): PublicAPIView {
|
||||
// remove our internal structure - always un-necessary
|
||||
delete view.query
|
||||
return {
|
||||
...view,
|
||||
query: view.queryUI,
|
||||
}
|
||||
}
|
||||
|
||||
function viewsResponse(views: ViewV2[]): PublicAPIView[] {
|
||||
return views.map(viewResponse)
|
||||
}
|
||||
|
||||
export async function search(ctx: UserCtx, next: Next) {
|
||||
const { name } = ctx.request.body
|
||||
await controller.v2.fetch(ctx)
|
||||
ctx.body.data = viewsResponse(stringSearch(ctx.body.data, name))
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function create(ctx: UserCtx, next: Next) {
|
||||
ctx = merge(ctx, {
|
||||
request: {
|
||||
body: viewRequest(ctx.request.body),
|
||||
},
|
||||
})
|
||||
await controller.v2.create(ctx)
|
||||
ctx.body.data = viewResponse(ctx.body.data)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function read(ctx: UserCtx, next: Next) {
|
||||
ctx = merge(ctx, {
|
||||
params: {
|
||||
viewId: ctx.params.viewId,
|
||||
},
|
||||
})
|
||||
await controller.v2.get(ctx)
|
||||
ctx.body.data = viewResponse(ctx.body.data)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function update(ctx: UserCtx, next: Next) {
|
||||
const viewId = ctx.params.viewId
|
||||
ctx = merge(ctx, {
|
||||
request: {
|
||||
body: {
|
||||
data: viewRequest(ctx.request.body, { viewId }),
|
||||
},
|
||||
},
|
||||
params: {
|
||||
viewId,
|
||||
},
|
||||
})
|
||||
await controller.v2.update(ctx)
|
||||
ctx.body.data = viewResponse(ctx.body.data)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function destroy(ctx: UserCtx, next: Next) {
|
||||
await controller.v2.remove(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
export default {
|
||||
create,
|
||||
read,
|
||||
update,
|
||||
destroy,
|
||||
search,
|
||||
}
|
|
@ -50,6 +50,7 @@ export async function searchView(
|
|||
result.rows.forEach(r => (r._viewId = view.id))
|
||||
ctx.body = result
|
||||
}
|
||||
|
||||
function getSortOptions(request: SearchViewRowRequest, view: ViewV2) {
|
||||
if (request.sort) {
|
||||
return {
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
RelationSchemaField,
|
||||
ViewFieldMetadata,
|
||||
CalculationType,
|
||||
ViewFetchResponseEnriched,
|
||||
CountDistinctCalculationFieldMetadata,
|
||||
CountCalculationFieldMetadata,
|
||||
} from "@budibase/types"
|
||||
|
@ -125,6 +126,12 @@ export async function get(ctx: Ctx<void, ViewResponseEnriched>) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function fetch(ctx: Ctx<void, ViewFetchResponseEnriched>) {
|
||||
ctx.body = {
|
||||
data: await sdk.views.getAllEnriched(),
|
||||
}
|
||||
}
|
||||
|
||||
export async function create(ctx: Ctx<CreateViewRequest, ViewResponse>) {
|
||||
const view = ctx.request.body
|
||||
const { tableId } = view
|
||||
|
|
|
@ -4,19 +4,21 @@ import queryEndpoints from "./queries"
|
|||
import tableEndpoints from "./tables"
|
||||
import rowEndpoints from "./rows"
|
||||
import userEndpoints from "./users"
|
||||
import viewEndpoints from "./views"
|
||||
import roleEndpoints from "./roles"
|
||||
import authorized from "../../../middleware/authorized"
|
||||
import publicApi from "../../../middleware/publicApi"
|
||||
import { paramResource, paramSubResource } from "../../../middleware/resourceId"
|
||||
import { PermissionType, PermissionLevel } from "@budibase/types"
|
||||
import { PermissionLevel, PermissionType } from "@budibase/types"
|
||||
import { CtxFn } from "./utils/Endpoint"
|
||||
import mapperMiddleware from "./middleware/mapper"
|
||||
import env from "../../../environment"
|
||||
import { middleware, redis } from "@budibase/backend-core"
|
||||
import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils"
|
||||
import cors from "@koa/cors"
|
||||
// below imports don't have declaration files
|
||||
const Router = require("@koa/router")
|
||||
const { RateLimit, Stores } = require("koa2-ratelimit")
|
||||
import { middleware, redis } from "@budibase/backend-core"
|
||||
import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils"
|
||||
|
||||
interface KoaRateLimitOptions {
|
||||
socket: {
|
||||
|
@ -81,6 +83,7 @@ const publicRouter = new Router({
|
|||
if (limiter && !env.isDev()) {
|
||||
publicRouter.use(limiter)
|
||||
}
|
||||
publicRouter.use(cors())
|
||||
|
||||
function addMiddleware(
|
||||
endpoints: any,
|
||||
|
@ -149,6 +152,7 @@ applyAdminRoutes(metricEndpoints)
|
|||
applyAdminRoutes(roleEndpoints)
|
||||
applyRoutes(appEndpoints, PermissionType.APP, "appId")
|
||||
applyRoutes(tableEndpoints, PermissionType.TABLE, "tableId")
|
||||
applyRoutes(viewEndpoints, PermissionType.VIEW, "viewId")
|
||||
applyRoutes(userEndpoints, PermissionType.USER, "userId")
|
||||
applyRoutes(queryEndpoints, PermissionType.QUERY, "queryId")
|
||||
// needs to be applied last for routing purposes, don't override other endpoints
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Ctx } from "@budibase/types"
|
||||
import mapping from "../../../controllers/public/mapping"
|
||||
|
||||
enum Resources {
|
||||
enum Resource {
|
||||
APPLICATION = "applications",
|
||||
TABLES = "tables",
|
||||
VIEWS = "views",
|
||||
ROWS = "rows",
|
||||
USERS = "users",
|
||||
QUERIES = "queries",
|
||||
|
@ -15,7 +16,7 @@ function isAttachment(ctx: Ctx) {
|
|||
}
|
||||
|
||||
function isArrayResponse(ctx: Ctx) {
|
||||
return ctx.url.endsWith(Resources.SEARCH) || Array.isArray(ctx.body)
|
||||
return ctx.url.endsWith(Resource.SEARCH) || Array.isArray(ctx.body)
|
||||
}
|
||||
|
||||
function noResponse(ctx: Ctx) {
|
||||
|
@ -38,6 +39,14 @@ function processTables(ctx: Ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
function processViews(ctx: Ctx) {
|
||||
if (isArrayResponse(ctx)) {
|
||||
return mapping.mapViews(ctx)
|
||||
} else {
|
||||
return mapping.mapView(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
function processRows(ctx: Ctx) {
|
||||
if (isArrayResponse(ctx)) {
|
||||
return mapping.mapRowSearch(ctx)
|
||||
|
@ -71,20 +80,27 @@ export default async (ctx: Ctx, next: any) => {
|
|||
let body = {}
|
||||
|
||||
switch (urlParts[0]) {
|
||||
case Resources.APPLICATION:
|
||||
case Resource.APPLICATION:
|
||||
body = processApplications(ctx)
|
||||
break
|
||||
case Resources.TABLES:
|
||||
if (urlParts[2] === Resources.ROWS) {
|
||||
case Resource.TABLES:
|
||||
if (urlParts[2] === Resource.ROWS) {
|
||||
body = processRows(ctx)
|
||||
} else {
|
||||
body = processTables(ctx)
|
||||
}
|
||||
break
|
||||
case Resources.USERS:
|
||||
case Resource.VIEWS:
|
||||
if (urlParts[2] === Resource.ROWS) {
|
||||
body = processRows(ctx)
|
||||
} else {
|
||||
body = processViews(ctx)
|
||||
}
|
||||
break
|
||||
case Resource.USERS:
|
||||
body = processUsers(ctx)
|
||||
break
|
||||
case Resources.QUERIES:
|
||||
case Resource.QUERIES:
|
||||
body = processQueries(ctx)
|
||||
break
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import controller from "../../controllers/public/rows"
|
||||
import controller, { viewSearch } from "../../controllers/public/rows"
|
||||
import Endpoint from "./utils/Endpoint"
|
||||
import { externalSearchValidator } from "../utils/validators"
|
||||
|
||||
|
@ -168,4 +168,40 @@ read.push(
|
|||
).addMiddleware(externalSearchValidator())
|
||||
)
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /views/{viewId}/rows/search:
|
||||
* post:
|
||||
* operationId: rowViewSearch
|
||||
* summary: Search for rows in a view
|
||||
* tags:
|
||||
* - rows
|
||||
* parameters:
|
||||
* - $ref: '#/components/parameters/viewId'
|
||||
* - $ref: '#/components/parameters/appId'
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/rowSearch'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: The response will contain an array of rows that match the search parameters.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/searchOutput'
|
||||
* examples:
|
||||
* search:
|
||||
* $ref: '#/components/examples/rows'
|
||||
*/
|
||||
read.push(
|
||||
new Endpoint(
|
||||
"post",
|
||||
"/views/:viewId/rows/search",
|
||||
controller.viewSearch
|
||||
).addMiddleware(externalSearchValidator())
|
||||
)
|
||||
|
||||
export default { read, write }
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
import { User, Table, SearchFilters, Row } from "@budibase/types"
|
||||
import {
|
||||
User,
|
||||
Table,
|
||||
SearchFilters,
|
||||
Row,
|
||||
ViewV2Schema,
|
||||
ViewV2,
|
||||
ViewV2Type,
|
||||
PublicAPIView,
|
||||
} from "@budibase/types"
|
||||
import { HttpMethod, MakeRequestResponse, generateMakeRequest } from "./utils"
|
||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||
import { Expectations } from "../../../../tests/utilities/api/base"
|
||||
|
||||
type RequestOpts = { internal?: boolean; appId?: string }
|
||||
|
||||
type Response<T> = { data: T }
|
||||
|
||||
export interface PublicAPIExpectations {
|
||||
status?: number
|
||||
body?: Record<string, any>
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
|
||||
export class PublicAPIRequest {
|
||||
|
@ -15,6 +26,7 @@ export class PublicAPIRequest {
|
|||
private appId: string | undefined
|
||||
|
||||
tables: PublicTableAPI
|
||||
views: PublicViewAPI
|
||||
rows: PublicRowAPI
|
||||
apiKey: string
|
||||
|
||||
|
@ -28,6 +40,7 @@ export class PublicAPIRequest {
|
|||
this.appId = appId
|
||||
this.tables = new PublicTableAPI(this)
|
||||
this.rows = new PublicRowAPI(this)
|
||||
this.views = new PublicViewAPI(this)
|
||||
}
|
||||
|
||||
static async init(config: TestConfiguration, user: User, opts?: RequestOpts) {
|
||||
|
@ -59,6 +72,12 @@ export class PublicAPIRequest {
|
|||
if (expectations?.body) {
|
||||
expect(res.body).toEqual(expectations?.body)
|
||||
}
|
||||
if (expectations?.headers) {
|
||||
for (let [header, value] of Object.entries(expectations.headers)) {
|
||||
const found = res.headers[header]
|
||||
expect(found?.toLowerCase()).toEqual(value)
|
||||
}
|
||||
}
|
||||
return res.body
|
||||
}
|
||||
}
|
||||
|
@ -73,9 +92,16 @@ export class PublicTableAPI {
|
|||
async create(
|
||||
table: Table,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<{ data: Table }> {
|
||||
): Promise<Response<Table>> {
|
||||
return this.request.send("post", "/tables", table, expectations)
|
||||
}
|
||||
|
||||
async search(
|
||||
name: string,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<Table[]>> {
|
||||
return this.request.send("post", "/tables/search", { name }, expectations)
|
||||
}
|
||||
}
|
||||
|
||||
export class PublicRowAPI {
|
||||
|
@ -85,11 +111,24 @@ export class PublicRowAPI {
|
|||
this.request = request
|
||||
}
|
||||
|
||||
async create(
|
||||
tableId: string,
|
||||
row: Row,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<Row>> {
|
||||
return this.request.send(
|
||||
"post",
|
||||
`/tables/${tableId}/rows`,
|
||||
row,
|
||||
expectations
|
||||
)
|
||||
}
|
||||
|
||||
async search(
|
||||
tableId: string,
|
||||
query: SearchFilters,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<{ data: Row[] }> {
|
||||
): Promise<Response<Row[]>> {
|
||||
return this.request.send(
|
||||
"post",
|
||||
`/tables/${tableId}/rows/search`,
|
||||
|
@ -99,4 +138,75 @@ export class PublicRowAPI {
|
|||
expectations
|
||||
)
|
||||
}
|
||||
|
||||
async viewSearch(
|
||||
viewId: string,
|
||||
query: SearchFilters,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<Row[]>> {
|
||||
return this.request.send(
|
||||
"post",
|
||||
`/views/${viewId}/rows/search`,
|
||||
{
|
||||
query,
|
||||
},
|
||||
expectations
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class PublicViewAPI {
|
||||
request: PublicAPIRequest
|
||||
|
||||
constructor(request: PublicAPIRequest) {
|
||||
this.request = request
|
||||
}
|
||||
|
||||
async create(
|
||||
view: Omit<PublicAPIView, "id" | "version">,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<PublicAPIView>> {
|
||||
return this.request.send("post", "/views", view, expectations)
|
||||
}
|
||||
|
||||
async update(
|
||||
viewId: string,
|
||||
view: Omit<PublicAPIView, "id" | "version">,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<PublicAPIView>> {
|
||||
return this.request.send("put", `/views/${viewId}`, view, expectations)
|
||||
}
|
||||
|
||||
async destroy(
|
||||
viewId: string,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<void> {
|
||||
return this.request.send(
|
||||
"delete",
|
||||
`/views/${viewId}`,
|
||||
undefined,
|
||||
expectations
|
||||
)
|
||||
}
|
||||
|
||||
async find(
|
||||
viewId: string,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<PublicAPIView>> {
|
||||
return this.request.send("get", `/views/${viewId}`, undefined, expectations)
|
||||
}
|
||||
|
||||
async search(
|
||||
viewName: string,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<PublicAPIView[]>> {
|
||||
return this.request.send(
|
||||
"post",
|
||||
"/views/search",
|
||||
{
|
||||
name: viewName,
|
||||
},
|
||||
expectations
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import * as setup from "../../tests/utilities"
|
||||
import { PublicAPIRequest } from "./Request"
|
||||
|
||||
describe("check public API security", () => {
|
||||
const config = setup.getConfig()
|
||||
let request: PublicAPIRequest
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.init()
|
||||
request = await PublicAPIRequest.init(config, await config.globalUser())
|
||||
})
|
||||
|
||||
it("should have Access-Control-Allow-Origin set to *", async () => {
|
||||
await request.tables.search("", {
|
||||
status: 200,
|
||||
headers: {
|
||||
"access-control-allow-origin": "*",
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,95 @@
|
|||
import * as setup from "../../tests/utilities"
|
||||
import { basicTable } from "../../../../tests/utilities/structures"
|
||||
import { BasicOperator, Table, UILogicalOperator } from "@budibase/types"
|
||||
import { PublicAPIRequest } from "./Request"
|
||||
import { generator } from "@budibase/backend-core/tests"
|
||||
|
||||
describe("check public API security", () => {
|
||||
const config = setup.getConfig()
|
||||
let request: PublicAPIRequest, table: Table
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.init()
|
||||
request = await PublicAPIRequest.init(config, await config.globalUser())
|
||||
table = (await request.tables.create(basicTable())).data
|
||||
})
|
||||
|
||||
function baseView() {
|
||||
return {
|
||||
name: generator.word(),
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
schema: {
|
||||
name: {
|
||||
readonly: true,
|
||||
visible: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
it("should be able to create a view", async () => {
|
||||
await request.views.create(baseView(), { status: 201 })
|
||||
})
|
||||
|
||||
it("should be able to update a view", async () => {
|
||||
const view = await request.views.create(baseView(), { status: 201 })
|
||||
const response = await request.views.update(view.data.id, {
|
||||
...view.data,
|
||||
name: "new name",
|
||||
})
|
||||
})
|
||||
|
||||
it("should be able to search views", async () => {
|
||||
const viewName = "view to search for"
|
||||
const view = await request.views.create(
|
||||
{
|
||||
...baseView(),
|
||||
name: viewName,
|
||||
},
|
||||
{ status: 201 }
|
||||
)
|
||||
const results = await request.views.search(viewName, {
|
||||
status: 200,
|
||||
})
|
||||
expect(results.data.length).toEqual(1)
|
||||
expect(results.data[0].id).toEqual(view.data.id)
|
||||
})
|
||||
|
||||
it("should be able to delete a view", async () => {
|
||||
const view = await request.views.create(baseView(), { status: 201 })
|
||||
const result = await request.views.destroy(view.data.id, { status: 204 })
|
||||
expect(result).toBeDefined()
|
||||
})
|
||||
|
||||
it("should be able to search rows through a view", async () => {
|
||||
const row1 = await request.rows.create(
|
||||
table._id!,
|
||||
{ name: "hello world" },
|
||||
{ status: 200 }
|
||||
)
|
||||
await request.rows.create(table._id!, { name: "foo bar" }, { status: 200 })
|
||||
const response = await request.views.create(
|
||||
{
|
||||
...baseView(),
|
||||
query: {
|
||||
logicalOperator: UILogicalOperator.ANY,
|
||||
groups: [
|
||||
{
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.STRING,
|
||||
field: "name",
|
||||
value: "hello",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{ status: 201 }
|
||||
)
|
||||
const results = await request.rows.viewSearch(response.data.id, {})
|
||||
expect(results.data.length).toEqual(1)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,165 @@
|
|||
import controller from "../../controllers/public/views"
|
||||
import Endpoint from "./utils/Endpoint"
|
||||
import { viewValidator, nameValidator } from "../utils/validators"
|
||||
|
||||
const read = [],
|
||||
write = []
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /views:
|
||||
* post:
|
||||
* operationId: viewCreate
|
||||
* summary: Create a view
|
||||
* description: Create a view, this can be against an internal or external table.
|
||||
* tags:
|
||||
* - views
|
||||
* parameters:
|
||||
* - $ref: '#/components/parameters/appId'
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/view'
|
||||
* examples:
|
||||
* view:
|
||||
* $ref: '#/components/examples/view'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Returns the created view, including the ID which has been generated for it.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/viewOutput'
|
||||
* examples:
|
||||
* view:
|
||||
* $ref: '#/components/examples/view'
|
||||
*/
|
||||
write.push(
|
||||
new Endpoint("post", "/views", controller.create).addMiddleware(
|
||||
viewValidator()
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /views/{viewId}:
|
||||
* put:
|
||||
* operationId: viewUpdate
|
||||
* summary: Update a view
|
||||
* description: Update a view, this can be against an internal or external table.
|
||||
* tags:
|
||||
* - views
|
||||
* parameters:
|
||||
* - $ref: '#/components/parameters/viewId'
|
||||
* - $ref: '#/components/parameters/appId'
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/view'
|
||||
* examples:
|
||||
* view:
|
||||
* $ref: '#/components/examples/view'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Returns the updated view.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/viewOutput'
|
||||
* examples:
|
||||
* view:
|
||||
* $ref: '#/components/examples/view'
|
||||
*/
|
||||
write.push(
|
||||
new Endpoint("put", "/views/:viewId", controller.update).addMiddleware(
|
||||
viewValidator()
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /views/{viewId}:
|
||||
* delete:
|
||||
* operationId: viewDestroy
|
||||
* summary: Delete a view
|
||||
* description: Delete a view, this can be against an internal or external table.
|
||||
* tags:
|
||||
* - views
|
||||
* parameters:
|
||||
* - $ref: '#/components/parameters/viewId'
|
||||
* - $ref: '#/components/parameters/appId'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Returns the deleted view.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/viewOutput'
|
||||
* examples:
|
||||
* view:
|
||||
* $ref: '#/components/examples/view'
|
||||
*/
|
||||
write.push(new Endpoint("delete", "/views/:viewId", controller.destroy))
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /views/{viewId}:
|
||||
* get:
|
||||
* operationId: viewGetById
|
||||
* summary: Retrieve a view
|
||||
* description: Lookup a view, this could be internal or external.
|
||||
* tags:
|
||||
* - views
|
||||
* parameters:
|
||||
* - $ref: '#/components/parameters/viewId'
|
||||
* - $ref: '#/components/parameters/appId'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Returns the retrieved view.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/viewOutput'
|
||||
* examples:
|
||||
* view:
|
||||
* $ref: '#/components/examples/view'
|
||||
*/
|
||||
read.push(new Endpoint("get", "/views/:viewId", controller.read))
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /views/search:
|
||||
* post:
|
||||
* operationId: viewSearch
|
||||
* summary: Search for views
|
||||
* description: Based on view properties (currently only name) search for views.
|
||||
* tags:
|
||||
* - views
|
||||
* parameters:
|
||||
* - $ref: '#/components/parameters/appId'
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/nameSearch'
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Returns the found views, based on the search parameters.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/viewSearch'
|
||||
* examples:
|
||||
* views:
|
||||
* $ref: '#/components/examples/views'
|
||||
*/
|
||||
read.push(
|
||||
new Endpoint("post", "/views/search", controller.search).addMiddleware(
|
||||
nameValidator()
|
||||
)
|
||||
)
|
||||
|
||||
export default { read, write }
|
|
@ -2592,6 +2592,33 @@ if (descriptions.length) {
|
|||
})
|
||||
})
|
||||
|
||||
describe("fetch", () => {
|
||||
let view: ViewV2, view2: ViewV2
|
||||
let table: Table, table2: Table
|
||||
beforeEach(async () => {
|
||||
table = await config.api.table.save(saveTableRequest())
|
||||
table2 = await config.api.table.save(saveTableRequest())
|
||||
view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
schema: {},
|
||||
})
|
||||
view2 = await config.api.viewV2.create({
|
||||
tableId: table2._id!,
|
||||
name: generator.guid(),
|
||||
schema: {},
|
||||
})
|
||||
})
|
||||
|
||||
it("should be able to list views", async () => {
|
||||
const response = await config.api.viewV2.fetch({
|
||||
status: 200,
|
||||
})
|
||||
expect(response.data.find(v => v.id === view.id)).toBeDefined()
|
||||
expect(response.data.find(v => v.id === view2.id)).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("destroy", () => {
|
||||
const getRowUsage = async () => {
|
||||
const { total } = await config.doInContext(undefined, () =>
|
||||
|
|
|
@ -9,6 +9,13 @@ import {
|
|||
Table,
|
||||
WebhookActionType,
|
||||
BuiltinPermissionID,
|
||||
ViewV2Type,
|
||||
SortOrder,
|
||||
SortType,
|
||||
UILogicalOperator,
|
||||
BasicOperator,
|
||||
ArrayOperator,
|
||||
RangeOperator,
|
||||
} from "@budibase/types"
|
||||
import Joi, { CustomValidator } from "joi"
|
||||
import { ValidSnippetNameRegex, helpers } from "@budibase/shared-core"
|
||||
|
@ -66,6 +73,66 @@ export function tableValidator() {
|
|||
)
|
||||
}
|
||||
|
||||
function searchUIFilterValidator() {
|
||||
const logicalOperator = Joi.string().valid(
|
||||
...Object.values(UILogicalOperator)
|
||||
)
|
||||
const operators = [
|
||||
...Object.values(BasicOperator),
|
||||
...Object.values(ArrayOperator),
|
||||
...Object.values(RangeOperator),
|
||||
]
|
||||
const filters = Joi.array().items(
|
||||
Joi.object({
|
||||
operator: Joi.string()
|
||||
.valid(...operators)
|
||||
.required(),
|
||||
field: Joi.string().required(),
|
||||
// could do with better validation of value based on operator
|
||||
value: Joi.any().required(),
|
||||
})
|
||||
)
|
||||
return Joi.object({
|
||||
logicalOperator,
|
||||
onEmptyFilter: Joi.string().valid(...Object.values(EmptyFilterOption)),
|
||||
groups: Joi.array().items(
|
||||
Joi.object({
|
||||
logicalOperator,
|
||||
filters,
|
||||
groups: Joi.array().items(
|
||||
Joi.object({
|
||||
filters,
|
||||
logicalOperator,
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
export function viewValidator() {
|
||||
return auth.joiValidator.body(
|
||||
Joi.object({
|
||||
id: OPTIONAL_STRING,
|
||||
tableId: Joi.string().required(),
|
||||
name: Joi.string().required(),
|
||||
type: Joi.string().optional().valid(null, ViewV2Type.CALCULATION),
|
||||
primaryDisplay: OPTIONAL_STRING,
|
||||
schema: Joi.object().required(),
|
||||
query: searchUIFilterValidator().optional(),
|
||||
sort: Joi.object({
|
||||
field: Joi.string().required(),
|
||||
order: Joi.string()
|
||||
.optional()
|
||||
.valid(...Object.values(SortOrder)),
|
||||
type: Joi.string()
|
||||
.optional()
|
||||
.valid(...Object.values(SortType)),
|
||||
}).optional(),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export function nameValidator() {
|
||||
return auth.joiValidator.body(
|
||||
Joi.object({
|
||||
|
@ -91,8 +158,7 @@ export function datasourceValidator() {
|
|||
)
|
||||
}
|
||||
|
||||
function filterObject(opts?: { unknown: boolean }) {
|
||||
const { unknown = true } = opts || {}
|
||||
function searchFiltersValidator() {
|
||||
const conditionalFilteringObject = () =>
|
||||
Joi.object({
|
||||
conditions: Joi.array().items(Joi.link("#schema")).required(),
|
||||
|
@ -119,7 +185,14 @@ function filterObject(opts?: { unknown: boolean }) {
|
|||
fuzzyOr: Joi.forbidden(),
|
||||
documentType: Joi.forbidden(),
|
||||
}
|
||||
return Joi.object(filtersValidators).unknown(unknown).id("schema")
|
||||
|
||||
return Joi.object(filtersValidators)
|
||||
}
|
||||
|
||||
function filterObject(opts?: { unknown: boolean }) {
|
||||
const { unknown = true } = opts || {}
|
||||
|
||||
return searchFiltersValidator().unknown(unknown).id("schema")
|
||||
}
|
||||
|
||||
export function internalSearchValidator() {
|
||||
|
|
|
@ -8,6 +8,11 @@ import { permissions } from "@budibase/backend-core"
|
|||
const router: Router = new Router()
|
||||
|
||||
router
|
||||
.get(
|
||||
"/api/v2/views",
|
||||
authorized(permissions.BUILDER),
|
||||
viewController.v2.fetch
|
||||
)
|
||||
.get(
|
||||
"/api/v2/views/:viewId",
|
||||
authorizedResource(
|
||||
|
|
|
@ -65,6 +65,9 @@ export interface paths {
|
|||
"/tables/{tableId}/rows/search": {
|
||||
post: operations["rowSearch"];
|
||||
};
|
||||
"/views/{viewId}/rows/search": {
|
||||
post: operations["rowViewSearch"];
|
||||
};
|
||||
"/tables": {
|
||||
/** Create a table, this could be internal or external. */
|
||||
post: operations["tableCreate"];
|
||||
|
@ -93,6 +96,22 @@ export interface paths {
|
|||
/** Based on user properties (currently only name) search for users. */
|
||||
post: operations["userSearch"];
|
||||
};
|
||||
"/views": {
|
||||
/** Create a view, this can be against an internal or external table. */
|
||||
post: operations["viewCreate"];
|
||||
};
|
||||
"/views/{viewId}": {
|
||||
/** Lookup a view, this could be internal or external. */
|
||||
get: operations["viewGetById"];
|
||||
/** Update a view, this can be against an internal or external table. */
|
||||
put: operations["viewUpdate"];
|
||||
/** Delete a view, this can be against an internal or external table. */
|
||||
delete: operations["viewDestroy"];
|
||||
};
|
||||
"/views/search": {
|
||||
/** Based on view properties (currently only name) search for views. */
|
||||
post: operations["viewSearch"];
|
||||
};
|
||||
}
|
||||
|
||||
export interface components {
|
||||
|
@ -813,10 +832,442 @@ export interface components {
|
|||
userIds: string[];
|
||||
};
|
||||
};
|
||||
/** @description The view to be created/updated. */
|
||||
view: {
|
||||
/** @description The name of the view. */
|
||||
name: string;
|
||||
/** @description The ID of the table this view is based on. */
|
||||
tableId: string;
|
||||
/**
|
||||
* @description The type of view - standard (empty value) or calculation.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "calculation";
|
||||
/** @description A column used to display rows from this view - usually used when rendered in tables. */
|
||||
primaryDisplay?: string;
|
||||
/** @description Search parameters for view */
|
||||
query?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/**
|
||||
* @description If no filters match, should the view return all rows, or no rows.
|
||||
* @enum {string}
|
||||
*/
|
||||
onEmptyFilter?: "all" | "none";
|
||||
/** @description A grouping of filters to be applied. */
|
||||
groups?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/** @description A list of filters to apply */
|
||||
filters?: {
|
||||
/**
|
||||
* @description The type of search operation which is being performed.
|
||||
* @enum {string}
|
||||
*/
|
||||
operator?:
|
||||
| "equal"
|
||||
| "notEqual"
|
||||
| "empty"
|
||||
| "notEmpty"
|
||||
| "fuzzy"
|
||||
| "string"
|
||||
| "contains"
|
||||
| "notContains"
|
||||
| "containsAny"
|
||||
| "oneOf"
|
||||
| "range";
|
||||
/** @description The field in the view to perform the search on. */
|
||||
field?: string;
|
||||
/** @description The value to search for - the type will depend on the operator in use. */
|
||||
value?:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: unknown }
|
||||
| unknown[];
|
||||
}[];
|
||||
/** @description A grouping of filters to be applied. */
|
||||
groups?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/** @description A list of filters to apply */
|
||||
filters?: {
|
||||
/**
|
||||
* @description The type of search operation which is being performed.
|
||||
* @enum {string}
|
||||
*/
|
||||
operator?:
|
||||
| "equal"
|
||||
| "notEqual"
|
||||
| "empty"
|
||||
| "notEmpty"
|
||||
| "fuzzy"
|
||||
| "string"
|
||||
| "contains"
|
||||
| "notContains"
|
||||
| "containsAny"
|
||||
| "oneOf"
|
||||
| "range";
|
||||
/** @description The field in the view to perform the search on. */
|
||||
field?: string;
|
||||
/** @description The value to search for - the type will depend on the operator in use. */
|
||||
value?:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: unknown }
|
||||
| unknown[];
|
||||
}[];
|
||||
}[];
|
||||
}[];
|
||||
};
|
||||
sort?: {
|
||||
/** @description The field from the table/view schema to sort on. */
|
||||
field: string;
|
||||
/**
|
||||
* @description The order in which to sort.
|
||||
* @enum {string}
|
||||
*/
|
||||
order?: "ascending" | "descending";
|
||||
/**
|
||||
* @description The type of sort to perform (by number, or by alphabetically).
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "string" | "number";
|
||||
};
|
||||
schema: {
|
||||
[key: string]:
|
||||
| {
|
||||
/** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */
|
||||
visible?: boolean;
|
||||
/** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */
|
||||
readonly?: boolean;
|
||||
/** @description A number defining where the column shows up in tables, lowest being first. */
|
||||
order?: number;
|
||||
/** @description A width for the column, defined in pixels - this affects rendering in tables. */
|
||||
width?: number;
|
||||
/** @description If this is a relationship column, we can set the columns we wish to include */
|
||||
column?: {
|
||||
readonly?: boolean;
|
||||
}[];
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @description This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.
|
||||
* @enum {string}
|
||||
*/
|
||||
calculationType?: "sum" | "avg" | "count" | "min" | "max";
|
||||
/** @description The field from the table to perform the calculation on. */
|
||||
field?: string;
|
||||
/** @description Can be used in tandem with the count calculation type, to count unique entries. */
|
||||
distinct?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
viewOutput: {
|
||||
/** @description The view to be created/updated. */
|
||||
data: {
|
||||
/** @description The name of the view. */
|
||||
name: string;
|
||||
/** @description The ID of the table this view is based on. */
|
||||
tableId: string;
|
||||
/**
|
||||
* @description The type of view - standard (empty value) or calculation.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "calculation";
|
||||
/** @description A column used to display rows from this view - usually used when rendered in tables. */
|
||||
primaryDisplay?: string;
|
||||
/** @description Search parameters for view */
|
||||
query?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/**
|
||||
* @description If no filters match, should the view return all rows, or no rows.
|
||||
* @enum {string}
|
||||
*/
|
||||
onEmptyFilter?: "all" | "none";
|
||||
/** @description A grouping of filters to be applied. */
|
||||
groups?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/** @description A list of filters to apply */
|
||||
filters?: {
|
||||
/**
|
||||
* @description The type of search operation which is being performed.
|
||||
* @enum {string}
|
||||
*/
|
||||
operator?:
|
||||
| "equal"
|
||||
| "notEqual"
|
||||
| "empty"
|
||||
| "notEmpty"
|
||||
| "fuzzy"
|
||||
| "string"
|
||||
| "contains"
|
||||
| "notContains"
|
||||
| "containsAny"
|
||||
| "oneOf"
|
||||
| "range";
|
||||
/** @description The field in the view to perform the search on. */
|
||||
field?: string;
|
||||
/** @description The value to search for - the type will depend on the operator in use. */
|
||||
value?:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: unknown }
|
||||
| unknown[];
|
||||
}[];
|
||||
/** @description A grouping of filters to be applied. */
|
||||
groups?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/** @description A list of filters to apply */
|
||||
filters?: {
|
||||
/**
|
||||
* @description The type of search operation which is being performed.
|
||||
* @enum {string}
|
||||
*/
|
||||
operator?:
|
||||
| "equal"
|
||||
| "notEqual"
|
||||
| "empty"
|
||||
| "notEmpty"
|
||||
| "fuzzy"
|
||||
| "string"
|
||||
| "contains"
|
||||
| "notContains"
|
||||
| "containsAny"
|
||||
| "oneOf"
|
||||
| "range";
|
||||
/** @description The field in the view to perform the search on. */
|
||||
field?: string;
|
||||
/** @description The value to search for - the type will depend on the operator in use. */
|
||||
value?:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: unknown }
|
||||
| unknown[];
|
||||
}[];
|
||||
}[];
|
||||
}[];
|
||||
};
|
||||
sort?: {
|
||||
/** @description The field from the table/view schema to sort on. */
|
||||
field: string;
|
||||
/**
|
||||
* @description The order in which to sort.
|
||||
* @enum {string}
|
||||
*/
|
||||
order?: "ascending" | "descending";
|
||||
/**
|
||||
* @description The type of sort to perform (by number, or by alphabetically).
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "string" | "number";
|
||||
};
|
||||
schema: {
|
||||
[key: string]:
|
||||
| {
|
||||
/** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */
|
||||
visible?: boolean;
|
||||
/** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */
|
||||
readonly?: boolean;
|
||||
/** @description A number defining where the column shows up in tables, lowest being first. */
|
||||
order?: number;
|
||||
/** @description A width for the column, defined in pixels - this affects rendering in tables. */
|
||||
width?: number;
|
||||
/** @description If this is a relationship column, we can set the columns we wish to include */
|
||||
column?: {
|
||||
readonly?: boolean;
|
||||
}[];
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @description This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.
|
||||
* @enum {string}
|
||||
*/
|
||||
calculationType?: "sum" | "avg" | "count" | "min" | "max";
|
||||
/** @description The field from the table to perform the calculation on. */
|
||||
field?: string;
|
||||
/** @description Can be used in tandem with the count calculation type, to count unique entries. */
|
||||
distinct?: boolean;
|
||||
};
|
||||
};
|
||||
/** @description The ID of the view. */
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
viewSearch: {
|
||||
data: {
|
||||
/** @description The name of the view. */
|
||||
name: string;
|
||||
/** @description The ID of the table this view is based on. */
|
||||
tableId: string;
|
||||
/**
|
||||
* @description The type of view - standard (empty value) or calculation.
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "calculation";
|
||||
/** @description A column used to display rows from this view - usually used when rendered in tables. */
|
||||
primaryDisplay?: string;
|
||||
/** @description Search parameters for view */
|
||||
query?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/**
|
||||
* @description If no filters match, should the view return all rows, or no rows.
|
||||
* @enum {string}
|
||||
*/
|
||||
onEmptyFilter?: "all" | "none";
|
||||
/** @description A grouping of filters to be applied. */
|
||||
groups?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/** @description A list of filters to apply */
|
||||
filters?: {
|
||||
/**
|
||||
* @description The type of search operation which is being performed.
|
||||
* @enum {string}
|
||||
*/
|
||||
operator?:
|
||||
| "equal"
|
||||
| "notEqual"
|
||||
| "empty"
|
||||
| "notEmpty"
|
||||
| "fuzzy"
|
||||
| "string"
|
||||
| "contains"
|
||||
| "notContains"
|
||||
| "containsAny"
|
||||
| "oneOf"
|
||||
| "range";
|
||||
/** @description The field in the view to perform the search on. */
|
||||
field?: string;
|
||||
/** @description The value to search for - the type will depend on the operator in use. */
|
||||
value?:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: unknown }
|
||||
| unknown[];
|
||||
}[];
|
||||
/** @description A grouping of filters to be applied. */
|
||||
groups?: {
|
||||
/**
|
||||
* @description When using groups this defines whether all of the filters must match, or only one of them.
|
||||
* @enum {string}
|
||||
*/
|
||||
logicalOperator?: "all" | "any";
|
||||
/** @description A list of filters to apply */
|
||||
filters?: {
|
||||
/**
|
||||
* @description The type of search operation which is being performed.
|
||||
* @enum {string}
|
||||
*/
|
||||
operator?:
|
||||
| "equal"
|
||||
| "notEqual"
|
||||
| "empty"
|
||||
| "notEmpty"
|
||||
| "fuzzy"
|
||||
| "string"
|
||||
| "contains"
|
||||
| "notContains"
|
||||
| "containsAny"
|
||||
| "oneOf"
|
||||
| "range";
|
||||
/** @description The field in the view to perform the search on. */
|
||||
field?: string;
|
||||
/** @description The value to search for - the type will depend on the operator in use. */
|
||||
value?:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| { [key: string]: unknown }
|
||||
| unknown[];
|
||||
}[];
|
||||
}[];
|
||||
}[];
|
||||
};
|
||||
sort?: {
|
||||
/** @description The field from the table/view schema to sort on. */
|
||||
field: string;
|
||||
/**
|
||||
* @description The order in which to sort.
|
||||
* @enum {string}
|
||||
*/
|
||||
order?: "ascending" | "descending";
|
||||
/**
|
||||
* @description The type of sort to perform (by number, or by alphabetically).
|
||||
* @enum {string}
|
||||
*/
|
||||
type?: "string" | "number";
|
||||
};
|
||||
schema: {
|
||||
[key: string]:
|
||||
| {
|
||||
/** @description Defines whether the column is visible or not - rows retrieved/updated through this view will not be able to access it. */
|
||||
visible?: boolean;
|
||||
/** @description When used in combination with 'visible: true' the column will be visible in row responses but cannot be updated. */
|
||||
readonly?: boolean;
|
||||
/** @description A number defining where the column shows up in tables, lowest being first. */
|
||||
order?: number;
|
||||
/** @description A width for the column, defined in pixels - this affects rendering in tables. */
|
||||
width?: number;
|
||||
/** @description If this is a relationship column, we can set the columns we wish to include */
|
||||
column?: {
|
||||
readonly?: boolean;
|
||||
}[];
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @description This column should be built from a calculation, specifying a type and field. It is important to note when a calculation is configured all non-calculation columns will be used for grouping.
|
||||
* @enum {string}
|
||||
*/
|
||||
calculationType?: "sum" | "avg" | "count" | "min" | "max";
|
||||
/** @description The field from the table to perform the calculation on. */
|
||||
field?: string;
|
||||
/** @description Can be used in tandem with the count calculation type, to count unique entries. */
|
||||
distinct?: boolean;
|
||||
};
|
||||
};
|
||||
/** @description The ID of the view. */
|
||||
id: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
parameters: {
|
||||
/** @description The ID of the table which this request is targeting. */
|
||||
tableId: string;
|
||||
/** @description The ID of the view which this request is targeting. */
|
||||
viewId: string;
|
||||
/** @description The ID of the row which this request is targeting. */
|
||||
rowId: string;
|
||||
/** @description The ID of the app which this request is targeting. */
|
||||
|
@ -1213,6 +1664,31 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
rowViewSearch: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the view which this request is targeting. */
|
||||
viewId: components["parameters"]["viewId"];
|
||||
};
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** The response will contain an array of rows that match the search parameters. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["searchOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["rowSearch"];
|
||||
};
|
||||
};
|
||||
};
|
||||
/** Create a table, this could be internal or external. */
|
||||
tableCreate: {
|
||||
parameters: {
|
||||
|
@ -1409,6 +1885,118 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
/** Create a view, this can be against an internal or external table. */
|
||||
viewCreate: {
|
||||
parameters: {
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** Returns the created view, including the ID which has been generated for it. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["viewOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["view"];
|
||||
};
|
||||
};
|
||||
};
|
||||
/** Lookup a view, this could be internal or external. */
|
||||
viewGetById: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the view which this request is targeting. */
|
||||
viewId: components["parameters"]["viewId"];
|
||||
};
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** Returns the retrieved view. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["viewOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** Update a view, this can be against an internal or external table. */
|
||||
viewUpdate: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the view which this request is targeting. */
|
||||
viewId: components["parameters"]["viewId"];
|
||||
};
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** Returns the updated view. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["viewOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["view"];
|
||||
};
|
||||
};
|
||||
};
|
||||
/** Delete a view, this can be against an internal or external table. */
|
||||
viewDestroy: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** The ID of the view which this request is targeting. */
|
||||
viewId: components["parameters"]["viewId"];
|
||||
};
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** Returns the deleted view. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["viewOutput"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** Based on view properties (currently only name) search for views. */
|
||||
viewSearch: {
|
||||
parameters: {
|
||||
header: {
|
||||
/** The ID of the app which this request is targeting. */
|
||||
"x-budibase-app-id": components["parameters"]["appId"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** Returns the found views, based on the search parameters. */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["viewSearch"];
|
||||
};
|
||||
};
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["nameSearch"];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface external {}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { constants, utils } from "@budibase/backend-core"
|
||||
import { BBContext } from "@budibase/types"
|
||||
import { Ctx } from "@budibase/types"
|
||||
|
||||
export default function ({ requiresAppId }: { requiresAppId?: boolean } = {}) {
|
||||
return async (ctx: BBContext, next: any) => {
|
||||
return async (ctx: Ctx, next: any) => {
|
||||
const appId = await utils.getAppIdFromCtx(ctx)
|
||||
if (requiresAppId && !appId) {
|
||||
ctx.throw(
|
||||
|
|
|
@ -78,8 +78,11 @@ export async function getAllInternalTables(db?: Database): Promise<Table[]> {
|
|||
}
|
||||
|
||||
async function getAllExternalTables(): Promise<Table[]> {
|
||||
// this is all datasources, we'll need to filter out internal
|
||||
const datasources = await sdk.datasources.fetch({ enriched: true })
|
||||
const allEntities = datasources.map(datasource => datasource.entities)
|
||||
const allEntities = datasources
|
||||
.filter(datasource => datasource._id !== INTERNAL_TABLE_SOURCE_ID)
|
||||
.map(datasource => datasource.entities)
|
||||
let final: Table[] = []
|
||||
for (let entities of allEntities) {
|
||||
if (entities) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import { isExternalTableID } from "../../../integrations/utils"
|
|||
import * as internal from "./internal"
|
||||
import * as external from "./external"
|
||||
import sdk from "../../../sdk"
|
||||
import { ensureQueryUISet } from "./utils"
|
||||
|
||||
function pickApi(tableId: any) {
|
||||
if (isExternalTableID(tableId)) {
|
||||
|
@ -44,6 +45,24 @@ export async function getEnriched(viewId: string): Promise<ViewV2Enriched> {
|
|||
return pickApi(tableId).getEnriched(viewId)
|
||||
}
|
||||
|
||||
export async function getAllEnriched(): Promise<ViewV2Enriched[]> {
|
||||
const tables = await sdk.tables.getAllTables()
|
||||
let views: ViewV2Enriched[] = []
|
||||
for (let table of tables) {
|
||||
if (!table.views || Object.keys(table.views).length === 0) {
|
||||
continue
|
||||
}
|
||||
const v2Views = Object.values(table.views).filter(isV2)
|
||||
const enrichedViews = await Promise.all(
|
||||
v2Views.map(view =>
|
||||
enrichSchema(ensureQueryUISet(view), table.schema, tables)
|
||||
)
|
||||
)
|
||||
views = views.concat(enrichedViews)
|
||||
}
|
||||
return views
|
||||
}
|
||||
|
||||
export async function getTable(view: string | ViewV2): Promise<Table> {
|
||||
const viewId = typeof view === "string" ? view : view.id
|
||||
const cached = context.getTableForView(viewId)
|
||||
|
@ -333,13 +352,19 @@ export function allowedFields(
|
|||
|
||||
export async function enrichSchema(
|
||||
view: ViewV2,
|
||||
tableSchema: TableSchema
|
||||
tableSchema: TableSchema,
|
||||
tables?: Table[]
|
||||
): Promise<ViewV2Enriched> {
|
||||
async function populateRelTableSchema(
|
||||
tableId: string,
|
||||
viewFields: Record<string, RelationSchemaField>
|
||||
) {
|
||||
const relTable = await sdk.tables.getTable(tableId)
|
||||
let relTable = tables
|
||||
? tables?.find(t => t._id === tableId)
|
||||
: await sdk.tables.getTable(tableId)
|
||||
if (!relTable) {
|
||||
throw new Error("Cannot enrich relationship, table not found")
|
||||
}
|
||||
const result: Record<string, ViewV2ColumnEnriched> = {}
|
||||
for (const relTableFieldName of Object.keys(relTable.schema)) {
|
||||
const relTableField = relTable.schema[relTableFieldName]
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
SearchViewRowRequest,
|
||||
PaginatedSearchRowResponse,
|
||||
ViewResponseEnriched,
|
||||
ViewFetchResponseEnriched,
|
||||
} from "@budibase/types"
|
||||
import { Expectations, TestAPI } from "./base"
|
||||
|
||||
|
@ -49,6 +50,12 @@ export class ViewV2API extends TestAPI {
|
|||
.data
|
||||
}
|
||||
|
||||
fetch = async (expectations?: Expectations) => {
|
||||
return await this._get<ViewFetchResponseEnriched>(`/api/v2/views`, {
|
||||
expectations,
|
||||
})
|
||||
}
|
||||
|
||||
search = async (
|
||||
viewId: string,
|
||||
params?: SearchViewRowRequest,
|
||||
|
|
|
@ -9,6 +9,10 @@ export interface ViewResponseEnriched {
|
|||
data: ViewV2Enriched
|
||||
}
|
||||
|
||||
export interface ViewFetchResponseEnriched {
|
||||
data: ViewV2Enriched[]
|
||||
}
|
||||
|
||||
export interface CreateViewRequest extends Omit<ViewV2, "version" | "id"> {}
|
||||
|
||||
export interface UpdateViewRequest extends ViewV2 {}
|
||||
|
|
|
@ -101,6 +101,10 @@ export interface ViewV2 {
|
|||
schema?: ViewV2Schema
|
||||
}
|
||||
|
||||
export interface PublicAPIView extends Omit<ViewV2, "query" | "queryUI"> {
|
||||
query?: UISearchFilter
|
||||
}
|
||||
|
||||
export type ViewV2Schema = Record<string, ViewFieldMetadata>
|
||||
|
||||
export type ViewSchema = ViewCountOrSumSchema | ViewStatisticsSchema
|
||||
|
|
|
@ -8,7 +8,7 @@ export interface RowValue {
|
|||
export interface RowResponse<T extends Document | RowValue> {
|
||||
id: string
|
||||
key: string
|
||||
error: string
|
||||
error?: string
|
||||
value: T
|
||||
doc?: T
|
||||
}
|
||||
|
|
|
@ -163,8 +163,8 @@ export interface Database {
|
|||
viewName: string,
|
||||
params: DatabaseQueryOpts
|
||||
): Promise<AllDocsResponse<T>>
|
||||
destroy(): Promise<Nano.OkResponse | void>
|
||||
compact(): Promise<Nano.OkResponse | void>
|
||||
destroy(): Promise<Nano.OkResponse>
|
||||
compact(): Promise<Nano.OkResponse>
|
||||
// these are all PouchDB related functions that are rarely used - in future
|
||||
// should be replaced by better typed/non-pouch implemented methods
|
||||
dump(stream: Writable, opts?: DatabaseDumpOpts): Promise<any>
|
||||
|
|
|
@ -50,8 +50,9 @@
|
|||
"bcrypt": "5.1.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bull": "4.10.1",
|
||||
"dd-trace": "5.23.0",
|
||||
"dd-trace": "5.26.0",
|
||||
"dotenv": "8.6.0",
|
||||
"email-validator": "^2.0.4",
|
||||
"global-agent": "3.0.0",
|
||||
"ical-generator": "4.1.0",
|
||||
"joi": "17.6.0",
|
||||
|
|
|
@ -40,6 +40,7 @@ import {
|
|||
import { checkAnyUserExists } from "../../../utilities/users"
|
||||
import { isEmailConfigured } from "../../../utilities/email"
|
||||
import { BpmStatusKey, BpmStatusValue, utils } from "@budibase/shared-core"
|
||||
import emailValidator from "email-validator"
|
||||
import crypto from "crypto"
|
||||
|
||||
const MAX_USERS_UPLOAD_LIMIT = 1000
|
||||
|
@ -300,6 +301,10 @@ export const find = async (ctx: any) => {
|
|||
|
||||
export const tenantUserLookup = async (ctx: any) => {
|
||||
const id = ctx.params.id
|
||||
// is email, check its valid
|
||||
if (id.includes("@") && !emailValidator.validate(id)) {
|
||||
ctx.throw(400, `${id} is not a valid email address to lookup.`)
|
||||
}
|
||||
const user = await userSdk.core.getFirstPlatformUser(id)
|
||||
if (user) {
|
||||
ctx.body = user
|
||||
|
|
267
yarn.lock
267
yarn.lock
|
@ -2196,7 +2196,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@3.2.7":
|
||||
"@budibase/backend-core@3.2.11":
|
||||
version "0.0.0"
|
||||
dependencies:
|
||||
"@budibase/nano" "10.1.5"
|
||||
|
@ -2210,7 +2210,7 @@
|
|||
bcryptjs "2.4.3"
|
||||
bull "4.10.1"
|
||||
correlation-id "4.0.0"
|
||||
dd-trace "5.23.0"
|
||||
dd-trace "5.26.0"
|
||||
dotenv "16.0.1"
|
||||
google-auth-library "^8.0.1"
|
||||
google-spreadsheet "npm:@budibase/google-spreadsheet@4.1.5"
|
||||
|
@ -2280,18 +2280,18 @@
|
|||
through2 "^2.0.0"
|
||||
|
||||
"@budibase/pro@npm:@budibase/pro@latest":
|
||||
version "3.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.7.tgz#4dfc48f3e2ba3a3b235872e54c3de4de54ea7941"
|
||||
integrity sha512-GRRaf1qSqQfoodjvKjBDvNOVHZrcSXF7so6Y9Xv/MiRTjDES5nmhIT5rL/PQ1+Mb+sPytYmMaJ2mlTtwqSQZgQ==
|
||||
version "3.2.11"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.11.tgz#40d4929b3958dacca3f4c207718a4647c08a6100"
|
||||
integrity sha512-xE1tx/C2cnbyR4s/6XkkweoQw6CW5fsQt++gzrrML8abgsODru+tA7M2NbWlKsEnVWHDQRvUVcXm4wqDeNNZ9g==
|
||||
dependencies:
|
||||
"@anthropic-ai/sdk" "^0.27.3"
|
||||
"@budibase/backend-core" "3.2.7"
|
||||
"@budibase/shared-core" "3.2.7"
|
||||
"@budibase/string-templates" "3.2.7"
|
||||
"@budibase/types" "3.2.7"
|
||||
"@koa/router" "8.0.8"
|
||||
"@budibase/backend-core" "3.2.11"
|
||||
"@budibase/shared-core" "3.2.11"
|
||||
"@budibase/string-templates" "3.2.11"
|
||||
"@budibase/types" "3.2.11"
|
||||
"@koa/router" "13.1.0"
|
||||
bull "4.10.1"
|
||||
dd-trace "5.2.0"
|
||||
dd-trace "5.23.0"
|
||||
joi "17.6.0"
|
||||
jsonwebtoken "9.0.2"
|
||||
lru-cache "^7.14.1"
|
||||
|
@ -2301,13 +2301,13 @@
|
|||
scim-patch "^0.8.1"
|
||||
scim2-parse-filter "^0.2.8"
|
||||
|
||||
"@budibase/shared-core@3.2.7":
|
||||
"@budibase/shared-core@3.2.11":
|
||||
version "0.0.0"
|
||||
dependencies:
|
||||
"@budibase/types" "0.0.0"
|
||||
cron-validate "1.4.5"
|
||||
|
||||
"@budibase/string-templates@3.2.7":
|
||||
"@budibase/string-templates@3.2.11":
|
||||
version "0.0.0"
|
||||
dependencies:
|
||||
"@budibase/handlebars-helpers" "^0.13.2"
|
||||
|
@ -2315,7 +2315,7 @@
|
|||
handlebars "^4.7.8"
|
||||
lodash.clonedeep "^4.5.0"
|
||||
|
||||
"@budibase/types@3.2.7":
|
||||
"@budibase/types@3.2.11":
|
||||
version "0.0.0"
|
||||
dependencies:
|
||||
scim-patch "^0.8.1"
|
||||
|
@ -2477,12 +2477,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@dagrejs/graphlib/-/graphlib-2.2.4.tgz#d77bfa9ff49e2307c0c6e6b8b26b5dd3c05816c4"
|
||||
integrity sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==
|
||||
|
||||
"@datadog/native-appsec@7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-7.0.0.tgz#a380174dd49aef2d9bb613a0ec8ead6dc7822095"
|
||||
integrity sha512-bywstWFW2hWxzPuS0+mFMVHHL0geulx5yQFtsjfszaH2LTAgk2D+Rt40MKbAoZ8q3tRw2dy6aYQ7svO3ca8jpA==
|
||||
dependencies:
|
||||
node-gyp-build "^3.9.0"
|
||||
"@datadog/libdatadog@^0.2.2":
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/libdatadog/-/libdatadog-0.2.2.tgz#ac02c76ac9a38250dca740727c7cdf00244ce3d3"
|
||||
integrity sha512-rTWo96mEPTY5UbtGoFj8/wY0uKSViJhsPg/Z6aoFWBFXQ8b45Ix2e/yvf92AAwrhG+gPLTxEqTXh3kef2dP8Ow==
|
||||
|
||||
"@datadog/native-appsec@8.1.1":
|
||||
version "8.1.1"
|
||||
|
@ -2491,13 +2489,12 @@
|
|||
dependencies:
|
||||
node-gyp-build "^3.9.0"
|
||||
|
||||
"@datadog/native-iast-rewriter@2.2.2":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.2.2.tgz#3f7feaf6be1af4c83ad063065b8ed509bbaf11cb"
|
||||
integrity sha512-13ZBhJpjZ/tiV6rYfyAf/ITye9cyd3x12M/2NKhD4Ivev4N4uKBREAjpArOtzKtPXZ5b6oXwVV4ofT1SHoYyzA==
|
||||
"@datadog/native-appsec@8.3.0":
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/native-appsec/-/native-appsec-8.3.0.tgz#91afd89d18d386be4da8a1b0e04500f2f8b5eb66"
|
||||
integrity sha512-RYHbSJ/MwJcJaLzaCaZvUyNLUKFbMshayIiv4ckpFpQJDiq1T8t9iM2k7008s75g1vRuXfsRNX7MaLn4aoFuWA==
|
||||
dependencies:
|
||||
lru-cache "^7.14.0"
|
||||
node-gyp-build "^4.5.0"
|
||||
node-gyp-build "^3.9.0"
|
||||
|
||||
"@datadog/native-iast-rewriter@2.4.1":
|
||||
version "2.4.1"
|
||||
|
@ -2507,12 +2504,13 @@
|
|||
lru-cache "^7.14.0"
|
||||
node-gyp-build "^4.5.0"
|
||||
|
||||
"@datadog/native-iast-taint-tracking@1.6.4":
|
||||
version "1.6.4"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-1.6.4.tgz#16c21ad7c36a53420c0d3c5a3720731809cc7e98"
|
||||
integrity sha512-Owxk7hQ4Dxwv4zJAoMjRga0IvE6lhvxnNc8pJCHsemCWBXchjr/9bqg05Zy5JnMbKUWn4XuZeJD6RFZpRa8bfw==
|
||||
"@datadog/native-iast-rewriter@2.5.0":
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/native-iast-rewriter/-/native-iast-rewriter-2.5.0.tgz#b613defe86e78168f750d1f1662d4ffb3cf002e6"
|
||||
integrity sha512-WRu34A3Wwp6oafX8KWNAbedtDaaJO+nzfYQht7pcJKjyC2ggfPeF7SoP+eDo9wTn4/nQwEOscSR4hkJqTRlpXQ==
|
||||
dependencies:
|
||||
node-gyp-build "^3.9.0"
|
||||
lru-cache "^7.14.0"
|
||||
node-gyp-build "^4.5.0"
|
||||
|
||||
"@datadog/native-iast-taint-tracking@3.1.0":
|
||||
version "3.1.0"
|
||||
|
@ -2521,6 +2519,13 @@
|
|||
dependencies:
|
||||
node-gyp-build "^3.9.0"
|
||||
|
||||
"@datadog/native-iast-taint-tracking@3.2.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/native-iast-taint-tracking/-/native-iast-taint-tracking-3.2.0.tgz#9fb6823d82f934e12c06ea1baa7399ca80deb2ec"
|
||||
integrity sha512-Mc6FzCoyvU5yXLMsMS9yKnEqJMWoImAukJXolNWCTm+JQYCMf2yMsJ8pBAm7KyZKliamM9rCn7h7Tr2H3lXwjA==
|
||||
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"
|
||||
|
@ -2529,16 +2534,13 @@
|
|||
node-addon-api "^6.1.0"
|
||||
node-gyp-build "^3.9.0"
|
||||
|
||||
"@datadog/pprof@5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.0.0.tgz#0c0aaf06def6d2bc4b2d353ec7b264dadbfbefab"
|
||||
integrity sha512-vhNan4SBuNWLpexunDJQ+hNbRAgWdk2qy5Iyh7Nn94uSSHXigAJMAvu4jwMKKQKFfchtobOkWT8GQUWW3tgpFg==
|
||||
"@datadog/native-metrics@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/native-metrics/-/native-metrics-3.0.1.tgz#dc276c93785c0377a048e316f23b7c8ff3acfa84"
|
||||
integrity sha512-0GuMyYyXf+Qpb/F+Fcekz58f2mO37lit9U3jMbWY/m8kac44gCPABzL5q3gWbdH+hWgqYfQoEYsdNDGSrKfwoQ==
|
||||
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"
|
||||
node-addon-api "^6.1.0"
|
||||
node-gyp-build "^3.9.0"
|
||||
|
||||
"@datadog/pprof@5.3.0":
|
||||
version "5.3.0"
|
||||
|
@ -2551,6 +2553,17 @@
|
|||
pprof-format "^2.1.0"
|
||||
source-map "^0.7.4"
|
||||
|
||||
"@datadog/pprof@5.4.1":
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/pprof/-/pprof-5.4.1.tgz#08c9bcf5d8efb2eeafdfc9f5bb5402f79fb41266"
|
||||
integrity sha512-IvpL96e/cuh8ugP5O8Czdup7XQOLHeIDgM5pac5W7Lc1YzGe5zTtebKFpitvb1CPw1YY+1qFx0pWGgKP2kOfHg==
|
||||
dependencies:
|
||||
delay "^5.0.0"
|
||||
node-gyp-build "<4.0"
|
||||
p-limit "^3.1.0"
|
||||
pprof-format "^2.1.0"
|
||||
source-map "^0.7.4"
|
||||
|
||||
"@datadog/sketches-js@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@datadog/sketches-js/-/sketches-js-2.1.0.tgz#8c7e8028a5fc22ad102fa542b0a446c956830455"
|
||||
|
@ -2884,6 +2897,11 @@
|
|||
wrap-ansi "^8.1.0"
|
||||
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
|
||||
|
||||
"@isaacs/ttlcache@^1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2"
|
||||
integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
|
@ -3466,6 +3484,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@jsep-plugin/regex/-/regex-1.0.4.tgz#cb2fc423220fa71c609323b9ba7f7d344a755fcc"
|
||||
integrity sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==
|
||||
|
||||
"@koa/cors@5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-5.0.0.tgz#0029b5f057fa0d0ae0e37dd2c89ece315a0daffd"
|
||||
integrity sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==
|
||||
dependencies:
|
||||
vary "^1.1.2"
|
||||
|
||||
"@koa/router@13.1.0":
|
||||
version "13.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@koa/router/-/router-13.1.0.tgz#43f4c554444ea4f4a148a5735a9525c6d16fd1b5"
|
||||
|
@ -3475,18 +3500,6 @@
|
|||
koa-compose "^4.1.0"
|
||||
path-to-regexp "^6.3.0"
|
||||
|
||||
"@koa/router@8.0.8":
|
||||
version "8.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.8.tgz#95f32d11373d03d89dcb63fabe9ac6f471095236"
|
||||
integrity sha512-FnT93N4NUehnXr+juupDmG2yfi0JnWdCmNEuIXpCG4TtG+9xvtrLambBH3RclycopVUOEYAim2lydiNBI7IRVg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
http-errors "^1.7.3"
|
||||
koa-compose "^4.1.0"
|
||||
methods "^1.1.2"
|
||||
path-to-regexp "1.x"
|
||||
urijs "^1.19.2"
|
||||
|
||||
"@lerna/child-process@7.4.2":
|
||||
version "7.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.2.tgz#a2fd013ac2150dc288270d3e0d0b850c06bec511"
|
||||
|
@ -3950,7 +3963,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.8.0.tgz#5aa7abb48f23f693068ed2999ae627d2f7d902ec"
|
||||
integrity sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==
|
||||
|
||||
"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.0.1":
|
||||
"@opentelemetry/api@^1.0.1":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe"
|
||||
integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==
|
||||
|
@ -5749,6 +5762,13 @@
|
|||
"@types/koa-compose" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/koa__cors@5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-5.0.0.tgz#74567a045b599266e2cd3940cef96cedecc2ef1f"
|
||||
integrity sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g==
|
||||
dependencies:
|
||||
"@types/koa" "*"
|
||||
|
||||
"@types/koa__router@12.0.4":
|
||||
version "12.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-12.0.4.tgz#a1f9afec9dc7e7d9fa1252d1938c44b403e19a28"
|
||||
|
@ -8901,6 +8921,15 @@ cron-validate@1.4.5:
|
|||
dependencies:
|
||||
yup "0.32.9"
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
dependencies:
|
||||
path-key "^3.1.0"
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
cross-spawn@^6.0.0:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
|
@ -9231,48 +9260,11 @@ dayjs@^1.10.8:
|
|||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||
|
||||
dc-polyfill@^0.1.2, dc-polyfill@^0.1.4:
|
||||
dc-polyfill@^0.1.4:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/dc-polyfill/-/dc-polyfill-0.1.6.tgz#c2940fa68ffb24a7bf127cc6cfdd15b39f0e7f02"
|
||||
integrity sha512-UV33cugmCC49a5uWAApM+6Ev9ZdvIUMTrtCO9fj96TPGOQiea54oeO3tiEVdVeo3J9N2UdJEmbS4zOkkEA35uQ==
|
||||
|
||||
dd-trace@5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.2.0.tgz#6ca2d76ece95f08d98468d7782c22f24192afa53"
|
||||
integrity sha512-Z5ql3ZKzVW3DPstHPkTPcIPvKljHNtzTYY/WuZRlgT4XK7rMaN0j5nA8LlUh7m+tOPWs05IiKngbYVZjsqhRgA==
|
||||
dependencies:
|
||||
"@datadog/native-appsec" "7.0.0"
|
||||
"@datadog/native-iast-rewriter" "2.2.2"
|
||||
"@datadog/native-iast-taint-tracking" "1.6.4"
|
||||
"@datadog/native-metrics" "^2.0.0"
|
||||
"@datadog/pprof" "5.0.0"
|
||||
"@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.7.3"
|
||||
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.5"
|
||||
lodash.sortby "^4.7.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.5"
|
||||
retry "^0.13.1"
|
||||
semver "^7.5.4"
|
||||
tlhunter-sorted-set "^0.1.0"
|
||||
|
||||
dd-trace@5.23.0:
|
||||
version "5.23.0"
|
||||
resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.23.0.tgz#a0c11863406de440a6675648caf06e1d07d67ba8"
|
||||
|
@ -9310,6 +9302,44 @@ dd-trace@5.23.0:
|
|||
shell-quote "^1.8.1"
|
||||
tlhunter-sorted-set "^0.1.0"
|
||||
|
||||
dd-trace@5.26.0:
|
||||
version "5.26.0"
|
||||
resolved "https://registry.yarnpkg.com/dd-trace/-/dd-trace-5.26.0.tgz#cc55061f66742bf01d0d7dc9f75c0e4937c82f40"
|
||||
integrity sha512-AQ4usxrbAG41f7CKUUe7fayZgfrh24D0L0vNzcU2mMJOmqQ3bXeDz9uSHkF3aFY8Epcsegrep3ifjRC0/zOxTw==
|
||||
dependencies:
|
||||
"@datadog/libdatadog" "^0.2.2"
|
||||
"@datadog/native-appsec" "8.3.0"
|
||||
"@datadog/native-iast-rewriter" "2.5.0"
|
||||
"@datadog/native-iast-taint-tracking" "3.2.0"
|
||||
"@datadog/native-metrics" "^3.0.1"
|
||||
"@datadog/pprof" "5.4.1"
|
||||
"@datadog/sketches-js" "^2.1.0"
|
||||
"@isaacs/ttlcache" "^1.4.1"
|
||||
"@opentelemetry/api" ">=1.0.0 <1.9.0"
|
||||
"@opentelemetry/core" "^1.14.0"
|
||||
crypto-randomuuid "^1.0.0"
|
||||
dc-polyfill "^0.1.4"
|
||||
ignore "^5.2.4"
|
||||
import-in-the-middle "1.11.2"
|
||||
int64-buffer "^0.1.9"
|
||||
istanbul-lib-coverage "3.2.0"
|
||||
jest-docblock "^29.7.0"
|
||||
koalas "^1.0.2"
|
||||
limiter "1.1.5"
|
||||
lodash.sortby "^4.7.0"
|
||||
lru-cache "^7.14.0"
|
||||
module-details-from-path "^1.0.3"
|
||||
msgpack-lite "^0.1.26"
|
||||
opentracing ">=0.12.1"
|
||||
path-to-regexp "^0.1.10"
|
||||
pprof-format "^2.1.0"
|
||||
protobufjs "^7.2.5"
|
||||
retry "^0.13.1"
|
||||
rfdc "^1.3.1"
|
||||
semver "^7.5.4"
|
||||
shell-quote "^1.8.1"
|
||||
tlhunter-sorted-set "^0.1.0"
|
||||
|
||||
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, 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.3.4:
|
||||
version "4.3.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
|
||||
|
@ -10172,6 +10202,11 @@ elliptic@^6.5.3, elliptic@^6.5.5:
|
|||
minimalistic-assert "^1.0.1"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
email-validator@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed"
|
||||
integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==
|
||||
|
||||
emittery@^0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
|
||||
|
@ -12531,11 +12566,6 @@ husky@^8.0.3:
|
|||
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
|
||||
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
|
||||
|
||||
husky@^9.1.4:
|
||||
version "9.1.7"
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d"
|
||||
integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==
|
||||
|
||||
ical-generator@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ical-generator/-/ical-generator-4.1.0.tgz#2a336c951864c5583a2aa715d16f2edcdfd2d90b"
|
||||
|
@ -12652,7 +12682,7 @@ import-from@^3.0.0:
|
|||
dependencies:
|
||||
resolve-from "^5.0.0"
|
||||
|
||||
import-in-the-middle@1.11.2, import-in-the-middle@^1.7.3:
|
||||
import-in-the-middle@1.11.2:
|
||||
version "1.11.2"
|
||||
resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.2.tgz#dd848e72b63ca6cd7c34df8b8d97fc9baee6174f"
|
||||
integrity sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA==
|
||||
|
@ -12866,11 +12896,6 @@ ip@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
|
||||
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
|
||||
|
||||
ipaddr.js@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8"
|
||||
integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==
|
||||
|
||||
is-arguments@^1.0.4, is-arguments@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
||||
|
@ -16008,7 +16033,7 @@ node-abi@^3.3.0:
|
|||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
node-abort-controller@^3.0.1, node-abort-controller@^3.1.1:
|
||||
node-abort-controller@^3.0.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548"
|
||||
integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==
|
||||
|
@ -17192,14 +17217,7 @@ path-scurry@^1.11.1, path-scurry@^1.6.1:
|
|||
lru-cache "^10.2.0"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
|
||||
path-to-regexp@1.x:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24"
|
||||
integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==
|
||||
dependencies:
|
||||
isarray "0.0.1"
|
||||
|
||||
path-to-regexp@^0.1.10, path-to-regexp@^0.1.2:
|
||||
path-to-regexp@^0.1.10:
|
||||
version "0.1.11"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.11.tgz#a527e662c89efc4646dbfa8100bf3e847e495761"
|
||||
integrity sha512-c0t+KCuUkO/YDLPG4WWzEwx3J5F/GHXsD1h/SNZfySqAIKe/BaP95x8fWtOfRJokpS5yYHRJjMtYlXD8jxnpbw==
|
||||
|
@ -18126,7 +18144,7 @@ pouchdb@9.0.0:
|
|||
uuid "8.3.2"
|
||||
vuvuzela "1.0.3"
|
||||
|
||||
pprof-format@^2.0.7, pprof-format@^2.1.0:
|
||||
pprof-format@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pprof-format/-/pprof-format-2.1.0.tgz#acc8d7773bcf4faf0a3d3df11bceefba7ac06664"
|
||||
integrity sha512-0+G5bHH0RNr8E5hoZo/zJYsL92MhkZjwrHp3O2IxmY8RJL9ooKeuZ8Tm0ZNBw5sGZ9TiM71sthTjWoR2Vf5/xw==
|
||||
|
@ -20544,6 +20562,26 @@ svelte-spa-router@^4.0.1:
|
|||
dependencies:
|
||||
regexparam "2.0.2"
|
||||
|
||||
svelte@4.2.19:
|
||||
version "4.2.19"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.19.tgz#4e6e84a8818e2cd04ae0255fcf395bc211e61d4c"
|
||||
integrity sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||
"@jridgewell/trace-mapping" "^0.3.18"
|
||||
"@types/estree" "^1.0.1"
|
||||
acorn "^8.9.0"
|
||||
aria-query "^5.3.0"
|
||||
axobject-query "^4.0.0"
|
||||
code-red "^1.0.3"
|
||||
css-tree "^2.3.1"
|
||||
estree-walker "^3.0.3"
|
||||
is-reference "^3.0.1"
|
||||
locate-character "^3.0.0"
|
||||
magic-string "^0.30.4"
|
||||
periscopic "^3.1.0"
|
||||
|
||||
svelte@^4.2.10:
|
||||
version "4.2.12"
|
||||
resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.12.tgz#13d98d2274d24d3ad216c8fdc801511171c70bb1"
|
||||
|
@ -21584,11 +21622,6 @@ uri-js@^4.2.2:
|
|||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
urijs@^1.19.2:
|
||||
version "1.19.11"
|
||||
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc"
|
||||
integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==
|
||||
|
||||
url-parse-lax@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
|
||||
|
|
Loading…
Reference in New Issue