@budibase/server 2.4.26 → 2.4.27-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builder/assets/{index.b0e3aca6.css → index.68ae453c.css} +2 -2
- package/builder/assets/{index.9e2f47ef.js → index.a661155b.js} +351 -350
- package/builder/index.html +2 -2
- package/dist/api/controllers/datasource.js +2 -1
- package/dist/api/controllers/public/metrics.js +113 -0
- package/dist/api/controllers/row/external.js +16 -8
- package/dist/api/controllers/row/index.js +5 -1
- package/dist/api/controllers/row/internal.js +1 -10
- package/dist/api/controllers/row/utils.js +3 -4
- package/dist/api/controllers/table/external.js +16 -12
- package/dist/api/controllers/table/utils.js +15 -1
- package/dist/api/routes/public/index.js +8 -0
- package/dist/api/routes/public/metrics.js +30 -0
- package/dist/constants/index.js +2 -1
- package/dist/integrations/googlesheets.js +125 -59
- package/dist/integrations/utils.js +17 -2
- package/dist/package.json +7 -7
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/specs/openapi.json +39 -0
- package/specs/openapi.yaml +169 -0
- package/specs/resources/application.ts +11 -0
- package/specs/resources/index.ts +2 -0
- package/specs/resources/metrics.ts +81 -0
- package/src/api/controllers/datasource.ts +2 -1
- package/src/api/controllers/public/metrics.ts +251 -0
- package/src/api/controllers/row/external.ts +26 -16
- package/src/api/controllers/row/index.ts +7 -2
- package/src/api/controllers/row/internal.ts +0 -7
- package/src/api/controllers/row/utils.ts +3 -4
- package/src/api/controllers/table/external.ts +24 -17
- package/src/api/controllers/table/index.ts +9 -9
- package/src/api/controllers/table/utils.ts +18 -2
- package/src/api/routes/public/index.ts +10 -1
- package/src/api/routes/public/metrics.ts +28 -0
- package/src/api/routes/public/tests/metrics.spec.js +34 -0
- package/src/constants/index.ts +1 -0
- package/src/definitions/openapi.ts +15 -0
- package/src/integrations/googlesheets.ts +143 -71
- package/src/integrations/utils.ts +16 -4
|
@@ -12,7 +12,7 @@ import * as exporters from "../view/exporters"
|
|
|
12
12
|
import { apiFileReturn } from "../../../utilities/fileSystem"
|
|
13
13
|
import {
|
|
14
14
|
Operation,
|
|
15
|
-
|
|
15
|
+
UserCtx,
|
|
16
16
|
Row,
|
|
17
17
|
PaginationJson,
|
|
18
18
|
Table,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
SortJson,
|
|
22
22
|
} from "@budibase/types"
|
|
23
23
|
import sdk from "../../../sdk"
|
|
24
|
+
import * as utils from "./utils"
|
|
24
25
|
|
|
25
26
|
const { cleanExportRows } = require("./utils")
|
|
26
27
|
|
|
@@ -49,12 +50,19 @@ export async function handleRequest(
|
|
|
49
50
|
)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
export async function patch(ctx:
|
|
53
|
+
export async function patch(ctx: UserCtx) {
|
|
53
54
|
const inputs = ctx.request.body
|
|
54
55
|
const tableId = ctx.params.tableId
|
|
55
56
|
const id = inputs._id
|
|
56
57
|
// don't save the ID to db
|
|
57
58
|
delete inputs._id
|
|
59
|
+
const validateResult = await utils.validate({
|
|
60
|
+
row: inputs,
|
|
61
|
+
tableId,
|
|
62
|
+
})
|
|
63
|
+
if (!validateResult.valid) {
|
|
64
|
+
throw { validation: validateResult.errors }
|
|
65
|
+
}
|
|
58
66
|
return handleRequest(Operation.UPDATE, tableId, {
|
|
59
67
|
id: breakRowIdField(id),
|
|
60
68
|
row: inputs,
|
|
@@ -62,16 +70,23 @@ export async function patch(ctx: BBContext) {
|
|
|
62
70
|
})
|
|
63
71
|
}
|
|
64
72
|
|
|
65
|
-
export async function save(ctx:
|
|
73
|
+
export async function save(ctx: UserCtx) {
|
|
66
74
|
const inputs = ctx.request.body
|
|
67
75
|
const tableId = ctx.params.tableId
|
|
76
|
+
const validateResult = await utils.validate({
|
|
77
|
+
row: inputs,
|
|
78
|
+
tableId,
|
|
79
|
+
})
|
|
80
|
+
if (!validateResult.valid) {
|
|
81
|
+
throw { validation: validateResult.errors }
|
|
82
|
+
}
|
|
68
83
|
return handleRequest(Operation.CREATE, tableId, {
|
|
69
84
|
row: inputs,
|
|
70
85
|
includeSqlRelationships: IncludeRelationship.EXCLUDE,
|
|
71
86
|
})
|
|
72
87
|
}
|
|
73
88
|
|
|
74
|
-
export async function fetchView(ctx:
|
|
89
|
+
export async function fetchView(ctx: UserCtx) {
|
|
75
90
|
// there are no views in external datasources, shouldn't ever be called
|
|
76
91
|
// for now just fetch
|
|
77
92
|
const split = ctx.params.viewName.split("all_")
|
|
@@ -79,14 +94,14 @@ export async function fetchView(ctx: BBContext) {
|
|
|
79
94
|
return fetch(ctx)
|
|
80
95
|
}
|
|
81
96
|
|
|
82
|
-
export async function fetch(ctx:
|
|
97
|
+
export async function fetch(ctx: UserCtx) {
|
|
83
98
|
const tableId = ctx.params.tableId
|
|
84
99
|
return handleRequest(Operation.READ, tableId, {
|
|
85
100
|
includeSqlRelationships: IncludeRelationship.INCLUDE,
|
|
86
101
|
})
|
|
87
102
|
}
|
|
88
103
|
|
|
89
|
-
export async function find(ctx:
|
|
104
|
+
export async function find(ctx: UserCtx) {
|
|
90
105
|
const id = ctx.params.rowId
|
|
91
106
|
const tableId = ctx.params.tableId
|
|
92
107
|
const response = (await handleRequest(Operation.READ, tableId, {
|
|
@@ -96,7 +111,7 @@ export async function find(ctx: BBContext) {
|
|
|
96
111
|
return response ? response[0] : response
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
export async function destroy(ctx:
|
|
114
|
+
export async function destroy(ctx: UserCtx) {
|
|
100
115
|
const tableId = ctx.params.tableId
|
|
101
116
|
const id = ctx.request.body._id
|
|
102
117
|
const { row } = (await handleRequest(Operation.DELETE, tableId, {
|
|
@@ -106,7 +121,7 @@ export async function destroy(ctx: BBContext) {
|
|
|
106
121
|
return { response: { ok: true }, row }
|
|
107
122
|
}
|
|
108
123
|
|
|
109
|
-
export async function bulkDestroy(ctx:
|
|
124
|
+
export async function bulkDestroy(ctx: UserCtx) {
|
|
110
125
|
const { rows } = ctx.request.body
|
|
111
126
|
const tableId = ctx.params.tableId
|
|
112
127
|
let promises: Promise<Row[] | { row: Row; table: Table }>[] = []
|
|
@@ -122,7 +137,7 @@ export async function bulkDestroy(ctx: BBContext) {
|
|
|
122
137
|
return { response: { ok: true }, rows: responses.map(resp => resp.row) }
|
|
123
138
|
}
|
|
124
139
|
|
|
125
|
-
export async function search(ctx:
|
|
140
|
+
export async function search(ctx: UserCtx) {
|
|
126
141
|
const tableId = ctx.params.tableId
|
|
127
142
|
const { paginate, query, ...params } = ctx.request.body
|
|
128
143
|
let { bookmark, limit } = params
|
|
@@ -185,12 +200,7 @@ export async function search(ctx: BBContext) {
|
|
|
185
200
|
}
|
|
186
201
|
}
|
|
187
202
|
|
|
188
|
-
export async function
|
|
189
|
-
// can't validate external right now - maybe in future
|
|
190
|
-
return { valid: true }
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export async function exportRows(ctx: BBContext) {
|
|
203
|
+
export async function exportRows(ctx: UserCtx) {
|
|
194
204
|
const { datasourceId, tableName } = breakExternalTableId(ctx.params.tableId)
|
|
195
205
|
const format = ctx.query.format
|
|
196
206
|
const { columns } = ctx.request.body
|
|
@@ -244,7 +254,7 @@ export async function exportRows(ctx: BBContext) {
|
|
|
244
254
|
return apiFileReturn(exporter(headers, exportRows))
|
|
245
255
|
}
|
|
246
256
|
|
|
247
|
-
export async function fetchEnrichedRow(ctx:
|
|
257
|
+
export async function fetchEnrichedRow(ctx: UserCtx) {
|
|
248
258
|
const id = ctx.params.rowId
|
|
249
259
|
const tableId = ctx.params.tableId
|
|
250
260
|
const { datasourceId, tableName } = breakExternalTableId(tableId)
|
|
@@ -2,6 +2,8 @@ import { quotas } from "@budibase/pro"
|
|
|
2
2
|
import * as internal from "./internal"
|
|
3
3
|
import * as external from "./external"
|
|
4
4
|
import { isExternalTable } from "../../../integrations/utils"
|
|
5
|
+
import { Ctx } from "@budibase/types"
|
|
6
|
+
import * as utils from "./utils"
|
|
5
7
|
|
|
6
8
|
function pickApi(tableId: any) {
|
|
7
9
|
if (isExternalTable(tableId)) {
|
|
@@ -129,9 +131,12 @@ export async function search(ctx: any) {
|
|
|
129
131
|
})
|
|
130
132
|
}
|
|
131
133
|
|
|
132
|
-
export async function validate(ctx:
|
|
134
|
+
export async function validate(ctx: Ctx) {
|
|
133
135
|
const tableId = getTableId(ctx)
|
|
134
|
-
ctx.body = await
|
|
136
|
+
ctx.body = await utils.validate({
|
|
137
|
+
row: ctx.request.body,
|
|
138
|
+
tableId,
|
|
139
|
+
})
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
export async function fetchEnrichedRow(ctx: any) {
|
|
@@ -387,13 +387,6 @@ export async function search(ctx: Ctx) {
|
|
|
387
387
|
return response
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
export async function validate(ctx: Ctx) {
|
|
391
|
-
return utils.validate({
|
|
392
|
-
tableId: ctx.params.tableId,
|
|
393
|
-
row: ctx.request.body,
|
|
394
|
-
})
|
|
395
|
-
}
|
|
396
|
-
|
|
397
390
|
export async function exportRows(ctx: Ctx) {
|
|
398
391
|
const db = context.getAppDB()
|
|
399
392
|
const table = await db.get(ctx.params.tableId)
|
|
@@ -4,11 +4,11 @@ import { FieldTypes } from "../../../constants"
|
|
|
4
4
|
import { context } from "@budibase/backend-core"
|
|
5
5
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
|
6
6
|
import { Row, Table } from "@budibase/types"
|
|
7
|
-
const validateJs = require("validate.js")
|
|
8
|
-
const { cloneDeep } = require("lodash/fp")
|
|
9
7
|
import { Format } from "../view/exporters"
|
|
10
8
|
import { Ctx } from "@budibase/types"
|
|
11
9
|
import sdk from "../../../sdk"
|
|
10
|
+
const validateJs = require("validate.js")
|
|
11
|
+
const { cloneDeep } = require("lodash/fp")
|
|
12
12
|
|
|
13
13
|
validateJs.extend(validateJs.validators.datetime, {
|
|
14
14
|
parse: function (value: string) {
|
|
@@ -56,8 +56,7 @@ export async function validate({
|
|
|
56
56
|
}) {
|
|
57
57
|
let fetchedTable: Table
|
|
58
58
|
if (!table) {
|
|
59
|
-
|
|
60
|
-
fetchedTable = await db.get(tableId)
|
|
59
|
+
fetchedTable = await sdk.tables.getTable(tableId)
|
|
61
60
|
} else {
|
|
62
61
|
fetchedTable = table
|
|
63
62
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
generateJunctionTableName,
|
|
8
8
|
foreignKeyStructure,
|
|
9
9
|
hasTypeChanged,
|
|
10
|
+
setStaticSchemas,
|
|
10
11
|
} from "./utils"
|
|
11
12
|
import { FieldTypes } from "../../../constants"
|
|
12
13
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
Operation,
|
|
21
22
|
RenameColumn,
|
|
22
23
|
FieldSchema,
|
|
23
|
-
|
|
24
|
+
UserCtx,
|
|
24
25
|
TableRequest,
|
|
25
26
|
RelationshipTypes,
|
|
26
27
|
} from "@budibase/types"
|
|
@@ -194,20 +195,20 @@ function isRelationshipSetup(column: FieldSchema) {
|
|
|
194
195
|
return column.foreignKey || column.through
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
export async function save(ctx:
|
|
198
|
-
const
|
|
199
|
-
const renamed =
|
|
198
|
+
export async function save(ctx: UserCtx) {
|
|
199
|
+
const inputs: TableRequest = ctx.request.body
|
|
200
|
+
const renamed = inputs?._rename
|
|
200
201
|
// can't do this right now
|
|
201
|
-
delete
|
|
202
|
+
delete inputs.rows
|
|
202
203
|
const datasourceId = getDatasourceId(ctx.request.body)!
|
|
203
204
|
// table doesn't exist already, note that it is created
|
|
204
|
-
if (!
|
|
205
|
-
|
|
205
|
+
if (!inputs._id) {
|
|
206
|
+
inputs.created = true
|
|
206
207
|
}
|
|
207
208
|
let tableToSave: TableRequest = {
|
|
208
209
|
type: "table",
|
|
209
|
-
_id: buildExternalTableId(datasourceId,
|
|
210
|
-
...
|
|
210
|
+
_id: buildExternalTableId(datasourceId, inputs.name),
|
|
211
|
+
...inputs,
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
let oldTable
|
|
@@ -224,6 +225,10 @@ export async function save(ctx: BBContext) {
|
|
|
224
225
|
if (!datasource.entities) {
|
|
225
226
|
datasource.entities = {}
|
|
226
227
|
}
|
|
228
|
+
|
|
229
|
+
// GSheets is a specific case - only ever has a static primary key
|
|
230
|
+
tableToSave = setStaticSchemas(datasource, tableToSave)
|
|
231
|
+
|
|
227
232
|
const oldTables = cloneDeep(datasource.entities)
|
|
228
233
|
const tables: Record<string, Table> = datasource.entities
|
|
229
234
|
|
|
@@ -246,7 +251,7 @@ export async function save(ctx: BBContext) {
|
|
|
246
251
|
const junctionTable = generateManyLinkSchema(
|
|
247
252
|
datasource,
|
|
248
253
|
schema,
|
|
249
|
-
|
|
254
|
+
tableToSave,
|
|
250
255
|
relatedTable
|
|
251
256
|
)
|
|
252
257
|
if (tables[junctionTable.name]) {
|
|
@@ -256,10 +261,12 @@ export async function save(ctx: BBContext) {
|
|
|
256
261
|
extraTablesToUpdate.push(junctionTable)
|
|
257
262
|
} else {
|
|
258
263
|
const fkTable =
|
|
259
|
-
relationType === RelationshipTypes.ONE_TO_MANY
|
|
264
|
+
relationType === RelationshipTypes.ONE_TO_MANY
|
|
265
|
+
? tableToSave
|
|
266
|
+
: relatedTable
|
|
260
267
|
const foreignKey = generateLinkSchema(
|
|
261
268
|
schema,
|
|
262
|
-
|
|
269
|
+
tableToSave,
|
|
263
270
|
relatedTable,
|
|
264
271
|
relationType
|
|
265
272
|
)
|
|
@@ -271,11 +278,11 @@ export async function save(ctx: BBContext) {
|
|
|
271
278
|
fkTable.constrained.push(foreignKey)
|
|
272
279
|
}
|
|
273
280
|
// foreign key is in other table, need to save it to external
|
|
274
|
-
if (fkTable._id !==
|
|
281
|
+
if (fkTable._id !== tableToSave._id) {
|
|
275
282
|
extraTablesToUpdate.push(fkTable)
|
|
276
283
|
}
|
|
277
284
|
}
|
|
278
|
-
generateRelatedSchema(schema, relatedTable,
|
|
285
|
+
generateRelatedSchema(schema, relatedTable, tableToSave, relatedColumnName)
|
|
279
286
|
schema.main = true
|
|
280
287
|
}
|
|
281
288
|
|
|
@@ -313,7 +320,7 @@ export async function save(ctx: BBContext) {
|
|
|
313
320
|
return tableToSave
|
|
314
321
|
}
|
|
315
322
|
|
|
316
|
-
export async function destroy(ctx:
|
|
323
|
+
export async function destroy(ctx: UserCtx) {
|
|
317
324
|
const tableToDelete: TableRequest = await sdk.tables.getTable(
|
|
318
325
|
ctx.params.tableId
|
|
319
326
|
)
|
|
@@ -339,7 +346,7 @@ export async function destroy(ctx: BBContext) {
|
|
|
339
346
|
return tableToDelete
|
|
340
347
|
}
|
|
341
348
|
|
|
342
|
-
export async function bulkImport(ctx:
|
|
349
|
+
export async function bulkImport(ctx: UserCtx) {
|
|
343
350
|
const table = await sdk.tables.getTable(ctx.params.tableId)
|
|
344
351
|
const { rows }: { rows: unknown } = ctx.request.body
|
|
345
352
|
const schema: unknown = table.schema
|
|
@@ -348,7 +355,7 @@ export async function bulkImport(ctx: BBContext) {
|
|
|
348
355
|
ctx.throw(400, "Provided data import information is invalid.")
|
|
349
356
|
}
|
|
350
357
|
|
|
351
|
-
const parsedRows =
|
|
358
|
+
const parsedRows = parse(rows, schema)
|
|
352
359
|
await handleRequest(Operation.BULK_CREATE, table._id!, {
|
|
353
360
|
rows: parsedRows,
|
|
354
361
|
})
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { isExternalTable, isSQL } from "../../../integrations/utils"
|
|
9
9
|
import { getDatasourceParams } from "../../../db/utils"
|
|
10
10
|
import { context, events } from "@budibase/backend-core"
|
|
11
|
-
import { Table,
|
|
11
|
+
import { Table, UserCtx } from "@budibase/types"
|
|
12
12
|
import sdk from "../../../sdk"
|
|
13
13
|
import csv from "csvtojson"
|
|
14
14
|
|
|
@@ -25,7 +25,7 @@ function pickApi({ tableId, table }: { tableId?: string; table?: Table }) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// covers both internal and external
|
|
28
|
-
export async function fetch(ctx:
|
|
28
|
+
export async function fetch(ctx: UserCtx) {
|
|
29
29
|
const db = context.getAppDB()
|
|
30
30
|
|
|
31
31
|
const internal = await sdk.tables.getAllInternalTables()
|
|
@@ -53,12 +53,12 @@ export async function fetch(ctx: BBContext) {
|
|
|
53
53
|
ctx.body = [...internal, ...external]
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export async function find(ctx:
|
|
56
|
+
export async function find(ctx: UserCtx) {
|
|
57
57
|
const tableId = ctx.params.tableId
|
|
58
58
|
ctx.body = await sdk.tables.getTable(tableId)
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export async function save(ctx:
|
|
61
|
+
export async function save(ctx: UserCtx) {
|
|
62
62
|
const appId = ctx.appId
|
|
63
63
|
const table = ctx.request.body
|
|
64
64
|
const isImport = table.rows
|
|
@@ -79,7 +79,7 @@ export async function save(ctx: BBContext) {
|
|
|
79
79
|
ctx.body = savedTable
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
export async function destroy(ctx:
|
|
82
|
+
export async function destroy(ctx: UserCtx) {
|
|
83
83
|
const appId = ctx.appId
|
|
84
84
|
const tableId = ctx.params.tableId
|
|
85
85
|
const deletedTable = await pickApi({ tableId }).destroy(ctx)
|
|
@@ -91,7 +91,7 @@ export async function destroy(ctx: BBContext) {
|
|
|
91
91
|
ctx.body = { message: `Table ${tableId} deleted.` }
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
export async function bulkImport(ctx:
|
|
94
|
+
export async function bulkImport(ctx: UserCtx) {
|
|
95
95
|
const tableId = ctx.params.tableId
|
|
96
96
|
await pickApi({ tableId }).bulkImport(ctx)
|
|
97
97
|
// right now we don't trigger anything for bulk import because it
|
|
@@ -101,7 +101,7 @@ export async function bulkImport(ctx: BBContext) {
|
|
|
101
101
|
ctx.body = { message: `Bulk rows created.` }
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
export async function csvToJson(ctx:
|
|
104
|
+
export async function csvToJson(ctx: UserCtx) {
|
|
105
105
|
const { csvString } = ctx.request.body
|
|
106
106
|
|
|
107
107
|
const result = await csv().fromString(csvString)
|
|
@@ -110,7 +110,7 @@ export async function csvToJson(ctx: BBContext) {
|
|
|
110
110
|
ctx.body = result
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
export async function validateNewTableImport(ctx:
|
|
113
|
+
export async function validateNewTableImport(ctx: UserCtx) {
|
|
114
114
|
const { rows, schema }: { rows: unknown; schema: unknown } = ctx.request.body
|
|
115
115
|
|
|
116
116
|
if (isRows(rows) && isSchema(schema)) {
|
|
@@ -121,7 +121,7 @@ export async function validateNewTableImport(ctx: BBContext) {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export async function validateExistingTableImport(ctx:
|
|
124
|
+
export async function validateExistingTableImport(ctx: UserCtx) {
|
|
125
125
|
const { rows, tableId }: { rows: unknown; tableId: unknown } =
|
|
126
126
|
ctx.request.body
|
|
127
127
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { parse, isSchema, isRows } from "../../../utilities/schema"
|
|
2
2
|
import { getRowParams, generateRowID, InternalTables } from "../../../db/utils"
|
|
3
3
|
import { isEqual } from "lodash"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AutoFieldSubTypes,
|
|
6
|
+
FieldTypes,
|
|
7
|
+
GOOGLE_SHEETS_PRIMARY_KEY,
|
|
8
|
+
} from "../../../constants"
|
|
5
9
|
import {
|
|
6
10
|
inputProcessing,
|
|
7
11
|
cleanupAttachments,
|
|
@@ -16,7 +20,7 @@ import viewTemplate from "../view/viewBuilder"
|
|
|
16
20
|
import { cloneDeep } from "lodash/fp"
|
|
17
21
|
import { quotas } from "@budibase/pro"
|
|
18
22
|
import { events, context } from "@budibase/backend-core"
|
|
19
|
-
import { Database } from "@budibase/types"
|
|
23
|
+
import { Database, Datasource, SourceName, Table } from "@budibase/types"
|
|
20
24
|
|
|
21
25
|
export async function clearColumns(table: any, columnNames: any) {
|
|
22
26
|
const db: Database = context.getAppDB()
|
|
@@ -392,5 +396,17 @@ export function hasTypeChanged(table: any, oldTable: any) {
|
|
|
392
396
|
return false
|
|
393
397
|
}
|
|
394
398
|
|
|
399
|
+
// used for external tables, some of them will have static schemas that need
|
|
400
|
+
// to be hard set
|
|
401
|
+
export function setStaticSchemas(datasource: Datasource, table: Table) {
|
|
402
|
+
// GSheets is a specific case - only ever has a static primary key
|
|
403
|
+
if (table && datasource.source === SourceName.GOOGLE_SHEETS) {
|
|
404
|
+
table.primary = [GOOGLE_SHEETS_PRIMARY_KEY]
|
|
405
|
+
// if there is an id column, remove it, should never exist in GSheets
|
|
406
|
+
delete table.schema?.id
|
|
407
|
+
}
|
|
408
|
+
return table
|
|
409
|
+
}
|
|
410
|
+
|
|
395
411
|
const _TableSaveFunctions = TableSaveFunctions
|
|
396
412
|
export { _TableSaveFunctions as TableSaveFunctions }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import appEndpoints from "./applications"
|
|
2
|
+
import metricEndpoints from "./metrics"
|
|
2
3
|
import queryEndpoints from "./queries"
|
|
3
4
|
import tableEndpoints from "./tables"
|
|
4
5
|
import rowEndpoints from "./rows"
|
|
@@ -12,7 +13,7 @@ import env from "../../../environment"
|
|
|
12
13
|
// below imports don't have declaration files
|
|
13
14
|
const Router = require("@koa/router")
|
|
14
15
|
const { RateLimit, Stores } = require("koa2-ratelimit")
|
|
15
|
-
import { redis, permissions } from "@budibase/backend-core"
|
|
16
|
+
import { middleware, redis, permissions } from "@budibase/backend-core"
|
|
16
17
|
const { PermissionType, PermissionLevel } = permissions
|
|
17
18
|
|
|
18
19
|
const PREFIX = "/api/public/v1"
|
|
@@ -91,6 +92,13 @@ function addToRouter(endpoints: any) {
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
function applyAdminRoutes(endpoints: any) {
|
|
96
|
+
addMiddleware(endpoints.read, middleware.builderOrAdmin)
|
|
97
|
+
addMiddleware(endpoints.write, middleware.builderOrAdmin)
|
|
98
|
+
addToRouter(endpoints.read)
|
|
99
|
+
addToRouter(endpoints.write)
|
|
100
|
+
}
|
|
101
|
+
|
|
94
102
|
function applyRoutes(
|
|
95
103
|
endpoints: any,
|
|
96
104
|
permType: string,
|
|
@@ -119,6 +127,7 @@ function applyRoutes(
|
|
|
119
127
|
addToRouter(endpoints.write)
|
|
120
128
|
}
|
|
121
129
|
|
|
130
|
+
applyAdminRoutes(metricEndpoints)
|
|
122
131
|
applyRoutes(appEndpoints, PermissionType.APP, "appId")
|
|
123
132
|
applyRoutes(tableEndpoints, PermissionType.TABLE, "tableId")
|
|
124
133
|
applyRoutes(userEndpoints, PermissionType.USER, "userId")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import controller from "../../controllers/public/metrics"
|
|
2
|
+
import Endpoint from "./utils/Endpoint"
|
|
3
|
+
|
|
4
|
+
const read = []
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @openapi
|
|
8
|
+
* /metrics:
|
|
9
|
+
* get:
|
|
10
|
+
* operationId: metricsGet
|
|
11
|
+
* summary: Retrieve Budibase tenant metrics
|
|
12
|
+
* description: Output metrics in OpenMetrics format compatible with Prometheus
|
|
13
|
+
* tags:
|
|
14
|
+
* - metrics
|
|
15
|
+
* responses:
|
|
16
|
+
* 200:
|
|
17
|
+
* description: Returns tenant metrics.
|
|
18
|
+
* content:
|
|
19
|
+
* text/plain:
|
|
20
|
+
* schema:
|
|
21
|
+
* type: string
|
|
22
|
+
* examples:
|
|
23
|
+
* metrics:
|
|
24
|
+
* $ref: '#/components/examples/metrics'
|
|
25
|
+
*/
|
|
26
|
+
read.push(new Endpoint("get", "/metrics", controller.fetch))
|
|
27
|
+
|
|
28
|
+
export default { read }
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const setup = require("../../tests/utilities")
|
|
2
|
+
|
|
3
|
+
jest.setTimeout(30000)
|
|
4
|
+
|
|
5
|
+
describe("/metrics", () => {
|
|
6
|
+
let request = setup.getRequest()
|
|
7
|
+
let config = setup.getConfig()
|
|
8
|
+
|
|
9
|
+
afterAll(setup.afterAll)
|
|
10
|
+
|
|
11
|
+
// For some reason this cannot be a beforeAll or the test "should be able to update the user" fail
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
await config.init()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe("get", () => {
|
|
17
|
+
it("returns a list of metrics", async () => {
|
|
18
|
+
const res = await request
|
|
19
|
+
.get(`/api/public/v1/metrics`)
|
|
20
|
+
.set(config.defaultHeaders())
|
|
21
|
+
.expect("Content-Type", /text\/plain/)
|
|
22
|
+
.expect(200)
|
|
23
|
+
expect(res.text).toContain("budibase_tenant_user_count")
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it("endpoint should not be publicly exposed", async () => {
|
|
27
|
+
await request
|
|
28
|
+
.get(`/api/public/v1/metrics`)
|
|
29
|
+
.set(config.publicHeaders())
|
|
30
|
+
.expect(403)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
})
|
package/src/constants/index.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface paths {
|
|
|
22
22
|
/** Based on application properties (currently only name) search for applications. */
|
|
23
23
|
post: operations["appSearch"];
|
|
24
24
|
};
|
|
25
|
+
"/metrics": {
|
|
26
|
+
/** Output metrics in OpenMetrics format compatible with Prometheus */
|
|
27
|
+
get: operations["metricsGet"];
|
|
28
|
+
};
|
|
25
29
|
"/queries/{queryId}": {
|
|
26
30
|
/** Queries which have been created within a Budibase app can be executed using this, */
|
|
27
31
|
post: operations["queryExecute"];
|
|
@@ -844,6 +848,17 @@ export interface operations {
|
|
|
844
848
|
};
|
|
845
849
|
};
|
|
846
850
|
};
|
|
851
|
+
/** Output metrics in OpenMetrics format compatible with Prometheus */
|
|
852
|
+
metricsGet: {
|
|
853
|
+
responses: {
|
|
854
|
+
/** Returns tenant metrics. */
|
|
855
|
+
200: {
|
|
856
|
+
content: {
|
|
857
|
+
"text/plain": string;
|
|
858
|
+
};
|
|
859
|
+
};
|
|
860
|
+
};
|
|
861
|
+
};
|
|
847
862
|
/** Queries which have been created within a Budibase app can be executed using this, */
|
|
848
863
|
queryExecute: {
|
|
849
864
|
parameters: {
|