@budibase/server 2.4.40 → 2.4.42-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/__mocks__/node-fetch.ts +6 -1
- package/builder/assets/index.3cb1022d.css +6 -0
- package/builder/assets/{index.07ed2ead.js → index.4b6c8c0e.js} +384 -383
- package/builder/index.html +7 -7
- package/dist/api/controllers/application.js +28 -24
- 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 +11 -1
- package/dist/api/controllers/row/internal.js +1 -10
- package/dist/api/controllers/row/utils.js +7 -7
- package/dist/api/controllers/static/index.js +84 -24
- package/dist/api/controllers/static/templates/BudibaseApp.svelte +33 -10
- 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/app.js +1 -0
- package/dist/constants/index.js +2 -1
- package/dist/integrations/googlesheets.js +125 -59
- package/dist/integrations/redis.js +1 -1
- package/dist/integrations/utils.js +17 -2
- package/dist/package.json +13 -12
- package/dist/sdk/users/utils.js +2 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/global.js +17 -7
- package/jest.config.ts +1 -0
- package/package.json +14 -13
- package/scripts/test.sh +3 -3
- 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/application.ts +20 -21
- 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 +12 -2
- package/src/api/controllers/row/internal.ts +0 -7
- package/src/api/controllers/row/utils.ts +7 -7
- package/src/api/controllers/static/index.ts +69 -26
- package/src/api/controllers/static/templates/BudibaseApp.svelte +33 -10
- 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/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap +48 -48
- 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/api/routes/tests/__snapshots__/datasource.spec.ts.snap +22 -22
- package/src/api/routes/tests/__snapshots__/view.spec.js.snap +5 -5
- package/src/api/routes/tests/appSync.spec.ts +31 -0
- package/src/api/routes/tests/internalSearch.spec.js +8 -7
- package/src/app.ts +2 -1
- package/src/automations/automationUtils.ts +1 -1
- package/src/automations/tests/automation.spec.ts +99 -0
- package/src/constants/index.ts +1 -0
- package/src/definitions/openapi.ts +15 -0
- package/src/integration-test/postgres.spec.ts +46 -52
- package/src/integrations/googlesheets.ts +143 -71
- package/src/integrations/redis.ts +1 -1
- package/src/integrations/tests/googlesheets.spec.ts +13 -13
- package/src/integrations/tests/redis.spec.ts +9 -5
- package/src/integrations/utils.ts +16 -4
- package/src/middleware/currentapp.ts +2 -2
- package/src/sdk/users/utils.ts +4 -1
- package/src/tests/jestEnv.ts +1 -0
- package/src/tests/jestSetup.ts +5 -1
- package/src/tests/utilities/TestConfiguration.ts +13 -0
- package/src/tests/utilities/structures.ts +13 -1
- package/src/utilities/global.ts +21 -9
- package/builder/assets/favicon.e7fc7733.png +0 -0
- package/builder/assets/index.b0e3aca6.css +0 -6
- package/src/automations/tests/automation.spec.js +0 -84
|
@@ -11,10 +11,12 @@ import {
|
|
|
11
11
|
} from "../../../utilities/fileSystem"
|
|
12
12
|
import env from "../../../environment"
|
|
13
13
|
import { DocumentType } from "../../../db/utils"
|
|
14
|
-
import { context, objectStore, utils } from "@budibase/backend-core"
|
|
14
|
+
import { context, objectStore, utils, configs } from "@budibase/backend-core"
|
|
15
15
|
import AWS from "aws-sdk"
|
|
16
16
|
import fs from "fs"
|
|
17
17
|
import sdk from "../../../sdk"
|
|
18
|
+
import * as pro from "@budibase/pro"
|
|
19
|
+
|
|
18
20
|
const send = require("koa-send")
|
|
19
21
|
|
|
20
22
|
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
|
@@ -98,33 +100,74 @@ export const deleteObjects = async function (ctx: any) {
|
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
export const serveApp = async function (ctx: any) {
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
103
|
+
//Public Settings
|
|
104
|
+
const { config } = await configs.getSettingsConfigDoc()
|
|
105
|
+
const branding = await pro.branding.getBrandingConfig(config)
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
"https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
|
|
111
|
-
title: appInfo.name,
|
|
112
|
-
production: env.isProd(),
|
|
113
|
-
appId,
|
|
114
|
-
clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version),
|
|
115
|
-
usedPlugins: plugins,
|
|
116
|
-
})
|
|
107
|
+
let db
|
|
108
|
+
try {
|
|
109
|
+
db = context.getAppDB({ skip_setup: true })
|
|
110
|
+
const appInfo = await db.get(DocumentType.APP_METADATA)
|
|
111
|
+
let appId = context.getAppId()
|
|
117
112
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
113
|
+
if (!env.isJest()) {
|
|
114
|
+
const App = require("./templates/BudibaseApp.svelte").default
|
|
115
|
+
const plugins = objectStore.enrichPluginURLs(appInfo.usedPlugins)
|
|
116
|
+
const { head, html, css } = App.render({
|
|
117
|
+
metaImage:
|
|
118
|
+
branding?.metaImageUrl ||
|
|
119
|
+
"https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
|
|
120
|
+
metaDescription: branding?.metaDescription || "",
|
|
121
|
+
metaTitle:
|
|
122
|
+
branding?.metaTitle || `${appInfo.name} - built with Budibase`,
|
|
123
|
+
title: appInfo.name,
|
|
124
|
+
production: env.isProd(),
|
|
125
|
+
appId,
|
|
126
|
+
clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version),
|
|
127
|
+
usedPlugins: plugins,
|
|
128
|
+
favicon:
|
|
129
|
+
branding.faviconUrl !== ""
|
|
130
|
+
? objectStore.getGlobalFileUrl("settings", "faviconUrl")
|
|
131
|
+
: "",
|
|
132
|
+
logo:
|
|
133
|
+
config?.logoUrl !== ""
|
|
134
|
+
? objectStore.getGlobalFileUrl("settings", "logoUrl")
|
|
135
|
+
: "",
|
|
136
|
+
})
|
|
137
|
+
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
|
138
|
+
ctx.body = await processString(appHbs, {
|
|
139
|
+
head,
|
|
140
|
+
body: html,
|
|
141
|
+
style: css.code,
|
|
142
|
+
appId,
|
|
143
|
+
})
|
|
144
|
+
} else {
|
|
145
|
+
// just return the app info for jest to assert on
|
|
146
|
+
ctx.body = appInfo
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (!env.isJest()) {
|
|
150
|
+
const App = require("./templates/BudibaseApp.svelte").default
|
|
151
|
+
const { head, html, css } = App.render({
|
|
152
|
+
title: branding?.metaTitle,
|
|
153
|
+
metaTitle: branding?.metaTitle,
|
|
154
|
+
metaImage:
|
|
155
|
+
branding?.metaImageUrl ||
|
|
156
|
+
"https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
|
|
157
|
+
metaDescription: branding?.metaDescription || "",
|
|
158
|
+
favicon:
|
|
159
|
+
branding.faviconUrl !== ""
|
|
160
|
+
? objectStore.getGlobalFileUrl("settings", "faviconUrl")
|
|
161
|
+
: "",
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
|
165
|
+
ctx.body = await processString(appHbs, {
|
|
166
|
+
head,
|
|
167
|
+
body: html,
|
|
168
|
+
style: css.code,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
128
171
|
}
|
|
129
172
|
}
|
|
130
173
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
export let title = ""
|
|
3
3
|
export let favicon = ""
|
|
4
|
+
|
|
4
5
|
export let metaImage = ""
|
|
6
|
+
export let metaTitle = ""
|
|
7
|
+
export let metaDescription = ""
|
|
5
8
|
|
|
6
9
|
export let clientLibPath
|
|
7
10
|
export let usedPlugins
|
|
@@ -13,18 +16,33 @@
|
|
|
13
16
|
name="viewport"
|
|
14
17
|
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
|
15
18
|
/>
|
|
19
|
+
|
|
20
|
+
<!-- Primary Meta Tags -->
|
|
21
|
+
<meta name="title" content={metaTitle} />
|
|
22
|
+
<meta name="description" content={metaDescription} />
|
|
23
|
+
|
|
16
24
|
<!-- Opengraph Meta Tags -->
|
|
17
|
-
<meta name="twitter:card" content="summary_large_image" />
|
|
18
|
-
<meta name="twitter:site" content="@budibase" />
|
|
19
|
-
<meta name="twitter:image" content={metaImage} />
|
|
20
|
-
<meta name="twitter:title" content="{title} - built with Budibase" />
|
|
21
25
|
<meta property="og:site_name" content="Budibase" />
|
|
22
26
|
<meta property="og:title" content="{title} - built with Budibase" />
|
|
27
|
+
<meta property="og:description" content={metaDescription} />
|
|
23
28
|
<meta property="og:type" content="website" />
|
|
24
29
|
<meta property="og:image" content={metaImage} />
|
|
25
30
|
|
|
31
|
+
<!-- Twitter -->
|
|
32
|
+
<meta property="twitter:card" content="summary_large_image" />
|
|
33
|
+
<meta property="twitter:site" content="@budibase" />
|
|
34
|
+
<meta property="twitter:image" content={metaImage} />
|
|
35
|
+
<meta property="twitter:image:alt" content="{title} - built with Budibase" />
|
|
36
|
+
<meta property="twitter:title" content="{title} - built with Budibase" />
|
|
37
|
+
<meta property="twitter:description" content={metaDescription} />
|
|
38
|
+
|
|
26
39
|
<title>{title}</title>
|
|
27
|
-
|
|
40
|
+
{#if favicon !== ""}
|
|
41
|
+
<link rel="icon" type="image/png" href={favicon} />
|
|
42
|
+
{:else}
|
|
43
|
+
<link rel="icon" type="image/png" href="https://i.imgur.com/Xhdt1YP.png" />
|
|
44
|
+
{/if}
|
|
45
|
+
|
|
28
46
|
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
|
29
47
|
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
|
30
48
|
<link
|
|
@@ -83,11 +101,16 @@
|
|
|
83
101
|
|
|
84
102
|
<body id="app">
|
|
85
103
|
<div id="error">
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
104
|
+
{#if clientLibPath}
|
|
105
|
+
<h1>There was an error loading your app</h1>
|
|
106
|
+
<h2>
|
|
107
|
+
The Budibase client library could not be loaded. Try republishing your
|
|
108
|
+
app.
|
|
109
|
+
</h2>
|
|
110
|
+
{:else}
|
|
111
|
+
<h2>We couldn't find that application</h2>
|
|
112
|
+
<p />
|
|
113
|
+
{/if}
|
|
91
114
|
</div>
|
|
92
115
|
<script type="application/javascript">
|
|
93
116
|
window.INIT_TIME = Date.now()
|
|
@@ -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,48 +1,48 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`viewBuilder Calculate and filter creates a view with the calculation statistics and filter schema 1`] = `
|
|
4
|
-
|
|
4
|
+
{
|
|
5
5
|
"map": "function (doc) {
|
|
6
|
-
if ((doc.tableId ===
|
|
7
|
-
doc[
|
|
8
|
-
doc[
|
|
9
|
-
doc[
|
|
10
|
-
(Array.isArray(doc[
|
|
11
|
-
)) && (doc[
|
|
12
|
-
emit(doc[
|
|
6
|
+
if ((doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && !(
|
|
7
|
+
doc["myField"] === undefined ||
|
|
8
|
+
doc["myField"] === null ||
|
|
9
|
+
doc["myField"] === "" ||
|
|
10
|
+
(Array.isArray(doc["myField"]) && doc["myField"].length === 0)
|
|
11
|
+
)) && (doc["age"] > 17)) {
|
|
12
|
+
emit(doc["_id"], doc["myField"]);
|
|
13
13
|
}
|
|
14
14
|
}",
|
|
15
|
-
"meta":
|
|
15
|
+
"meta": {
|
|
16
16
|
"calculation": "stats",
|
|
17
17
|
"field": "myField",
|
|
18
|
-
"filters":
|
|
19
|
-
|
|
18
|
+
"filters": [
|
|
19
|
+
{
|
|
20
20
|
"condition": "MT",
|
|
21
21
|
"key": "age",
|
|
22
22
|
"value": 17,
|
|
23
23
|
},
|
|
24
24
|
],
|
|
25
25
|
"groupBy": undefined,
|
|
26
|
-
"schema":
|
|
27
|
-
"avg":
|
|
26
|
+
"schema": {
|
|
27
|
+
"avg": {
|
|
28
28
|
"type": "number",
|
|
29
29
|
},
|
|
30
|
-
"count":
|
|
30
|
+
"count": {
|
|
31
31
|
"type": "number",
|
|
32
32
|
},
|
|
33
|
-
"field":
|
|
33
|
+
"field": {
|
|
34
34
|
"type": "string",
|
|
35
35
|
},
|
|
36
|
-
"max":
|
|
36
|
+
"max": {
|
|
37
37
|
"type": "number",
|
|
38
38
|
},
|
|
39
|
-
"min":
|
|
39
|
+
"min": {
|
|
40
40
|
"type": "number",
|
|
41
41
|
},
|
|
42
|
-
"sum":
|
|
42
|
+
"sum": {
|
|
43
43
|
"type": "number",
|
|
44
44
|
},
|
|
45
|
-
"sumsqr":
|
|
45
|
+
"sumsqr": {
|
|
46
46
|
"type": "number",
|
|
47
47
|
},
|
|
48
48
|
},
|
|
@@ -53,42 +53,42 @@ Object {
|
|
|
53
53
|
`;
|
|
54
54
|
|
|
55
55
|
exports[`viewBuilder Calculate creates a view with the calculation statistics schema 1`] = `
|
|
56
|
-
|
|
56
|
+
{
|
|
57
57
|
"map": "function (doc) {
|
|
58
|
-
if ((doc.tableId ===
|
|
59
|
-
doc[
|
|
60
|
-
doc[
|
|
61
|
-
doc[
|
|
62
|
-
(Array.isArray(doc[
|
|
58
|
+
if ((doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && !(
|
|
59
|
+
doc["myField"] === undefined ||
|
|
60
|
+
doc["myField"] === null ||
|
|
61
|
+
doc["myField"] === "" ||
|
|
62
|
+
(Array.isArray(doc["myField"]) && doc["myField"].length === 0)
|
|
63
63
|
)) ) {
|
|
64
|
-
emit(doc[
|
|
64
|
+
emit(doc["_id"], doc["myField"]);
|
|
65
65
|
}
|
|
66
66
|
}",
|
|
67
|
-
"meta":
|
|
67
|
+
"meta": {
|
|
68
68
|
"calculation": "stats",
|
|
69
69
|
"field": "myField",
|
|
70
|
-
"filters":
|
|
70
|
+
"filters": [],
|
|
71
71
|
"groupBy": undefined,
|
|
72
|
-
"schema":
|
|
73
|
-
"avg":
|
|
72
|
+
"schema": {
|
|
73
|
+
"avg": {
|
|
74
74
|
"type": "number",
|
|
75
75
|
},
|
|
76
|
-
"count":
|
|
76
|
+
"count": {
|
|
77
77
|
"type": "number",
|
|
78
78
|
},
|
|
79
|
-
"field":
|
|
79
|
+
"field": {
|
|
80
80
|
"type": "string",
|
|
81
81
|
},
|
|
82
|
-
"max":
|
|
82
|
+
"max": {
|
|
83
83
|
"type": "number",
|
|
84
84
|
},
|
|
85
|
-
"min":
|
|
85
|
+
"min": {
|
|
86
86
|
"type": "number",
|
|
87
87
|
},
|
|
88
|
-
"sum":
|
|
88
|
+
"sum": {
|
|
89
89
|
"type": "number",
|
|
90
90
|
},
|
|
91
|
-
"sumsqr":
|
|
91
|
+
"sumsqr": {
|
|
92
92
|
"type": "number",
|
|
93
93
|
},
|
|
94
94
|
},
|
|
@@ -99,22 +99,22 @@ Object {
|
|
|
99
99
|
`;
|
|
100
100
|
|
|
101
101
|
exports[`viewBuilder Filter creates a view with multiple filters and conjunctions 1`] = `
|
|
102
|
-
|
|
102
|
+
{
|
|
103
103
|
"map": "function (doc) {
|
|
104
|
-
if (doc.tableId ===
|
|
105
|
-
emit(doc[
|
|
104
|
+
if (doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && (doc["Name"] === "Test" || doc["Yes"] > "Value")) {
|
|
105
|
+
emit(doc["_id"], doc["undefined"]);
|
|
106
106
|
}
|
|
107
107
|
}",
|
|
108
|
-
"meta":
|
|
108
|
+
"meta": {
|
|
109
109
|
"calculation": undefined,
|
|
110
110
|
"field": undefined,
|
|
111
|
-
"filters":
|
|
112
|
-
|
|
111
|
+
"filters": [
|
|
112
|
+
{
|
|
113
113
|
"condition": "EQUALS",
|
|
114
114
|
"key": "Name",
|
|
115
115
|
"value": "Test",
|
|
116
116
|
},
|
|
117
|
-
|
|
117
|
+
{
|
|
118
118
|
"condition": "MT",
|
|
119
119
|
"conjunction": "OR",
|
|
120
120
|
"key": "Yes",
|
|
@@ -129,16 +129,16 @@ Object {
|
|
|
129
129
|
`;
|
|
130
130
|
|
|
131
131
|
exports[`viewBuilder Group By creates a view emitting the group by field 1`] = `
|
|
132
|
-
|
|
132
|
+
{
|
|
133
133
|
"map": "function (doc) {
|
|
134
|
-
if (doc.tableId ===
|
|
135
|
-
emit(doc[
|
|
134
|
+
if (doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" ) {
|
|
135
|
+
emit(doc["age"], doc["score"]);
|
|
136
136
|
}
|
|
137
137
|
}",
|
|
138
|
-
"meta":
|
|
138
|
+
"meta": {
|
|
139
139
|
"calculation": undefined,
|
|
140
140
|
"field": "score",
|
|
141
|
-
"filters":
|
|
141
|
+
"filters": [],
|
|
142
142
|
"groupBy": "age",
|
|
143
143
|
"schema": null,
|
|
144
144
|
"tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
|
|
@@ -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 }
|