@budibase/server 2.19.2 → 2.19.4
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-bba11af5.js → index-7fe79d09.js} +180 -179
- package/builder/assets/{index-3cbbfc57.css → index-fb878e69.css} +1 -1
- package/builder/index.html +2 -2
- package/client/budibase-client.js +2 -2
- package/client/manifest.json +2 -0
- package/package.json +8 -8
- package/src/api/routes/tests/queries/mysql.spec.ts +239 -0
- package/src/api/routes/tests/queries/postgres.spec.ts +73 -0
- package/src/integrations/tests/utils/index.ts +2 -1
- package/src/integrations/tests/utils/mysql.ts +53 -0
- package/src/integrations/tests/utils/postgres.ts +1 -3
package/client/manifest.json
CHANGED
|
@@ -270,6 +270,7 @@
|
|
|
270
270
|
{
|
|
271
271
|
"type": "buttonConfiguration",
|
|
272
272
|
"key": "buttons",
|
|
273
|
+
"nested": true,
|
|
273
274
|
"defaultValue": [
|
|
274
275
|
{
|
|
275
276
|
"type": "cta",
|
|
@@ -4639,6 +4640,7 @@
|
|
|
4639
4640
|
"illegalChildren": ["section"],
|
|
4640
4641
|
"hasChildren": true,
|
|
4641
4642
|
"showEmptyState": false,
|
|
4643
|
+
"actions": ["ClearRowSelection"],
|
|
4642
4644
|
"size": {
|
|
4643
4645
|
"width": 600,
|
|
4644
4646
|
"height": 400
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.19.
|
|
4
|
+
"version": "2.19.4",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
"license": "GPL-3.0",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@apidevtools/swagger-parser": "10.0.3",
|
|
50
|
-
"@budibase/backend-core": "2.19.
|
|
51
|
-
"@budibase/client": "2.19.
|
|
52
|
-
"@budibase/pro": "2.19.
|
|
53
|
-
"@budibase/shared-core": "2.19.
|
|
54
|
-
"@budibase/string-templates": "2.19.
|
|
55
|
-
"@budibase/types": "2.19.
|
|
50
|
+
"@budibase/backend-core": "2.19.4",
|
|
51
|
+
"@budibase/client": "2.19.4",
|
|
52
|
+
"@budibase/pro": "2.19.4",
|
|
53
|
+
"@budibase/shared-core": "2.19.4",
|
|
54
|
+
"@budibase/string-templates": "2.19.4",
|
|
55
|
+
"@budibase/types": "2.19.4",
|
|
56
56
|
"@bull-board/api": "5.10.2",
|
|
57
57
|
"@bull-board/koa": "5.10.2",
|
|
58
58
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -193,5 +193,5 @@
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
},
|
|
196
|
-
"gitHead": "
|
|
196
|
+
"gitHead": "b92bbf340be02a66b0faa3f8e5115015d6cf56a6"
|
|
197
197
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Datasource, Query } from "@budibase/types"
|
|
2
|
+
import * as setup from "../utilities"
|
|
3
|
+
import { databaseTestProviders } from "../../../../integrations/tests/utils"
|
|
4
|
+
import mysql from "mysql2/promise"
|
|
5
|
+
|
|
6
|
+
jest.unmock("mysql2")
|
|
7
|
+
jest.unmock("mysql2/promise")
|
|
8
|
+
|
|
9
|
+
const createTableSQL = `
|
|
10
|
+
CREATE TABLE test_table (
|
|
11
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
12
|
+
name VARCHAR(50) NOT NULL
|
|
13
|
+
)
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
const insertSQL = `
|
|
17
|
+
INSERT INTO test_table (name) VALUES ('one'), ('two'), ('three'), ('four'), ('five')
|
|
18
|
+
`
|
|
19
|
+
|
|
20
|
+
const dropTableSQL = `
|
|
21
|
+
DROP TABLE test_table
|
|
22
|
+
`
|
|
23
|
+
|
|
24
|
+
describe("/queries", () => {
|
|
25
|
+
let config = setup.getConfig()
|
|
26
|
+
let datasource: Datasource
|
|
27
|
+
|
|
28
|
+
async function createQuery(query: Partial<Query>): Promise<Query> {
|
|
29
|
+
const defaultQuery: Query = {
|
|
30
|
+
datasourceId: datasource._id!,
|
|
31
|
+
name: "New Query",
|
|
32
|
+
parameters: [],
|
|
33
|
+
fields: {},
|
|
34
|
+
schema: {},
|
|
35
|
+
queryVerb: "read",
|
|
36
|
+
transformer: "return data",
|
|
37
|
+
readable: true,
|
|
38
|
+
}
|
|
39
|
+
return await config.api.query.create({ ...defaultQuery, ...query })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function withConnection(
|
|
43
|
+
callback: (client: mysql.Connection) => Promise<void>
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
const ds = await databaseTestProviders.mysql.datasource()
|
|
46
|
+
const con = await mysql.createConnection(ds.config!)
|
|
47
|
+
try {
|
|
48
|
+
await callback(con)
|
|
49
|
+
} finally {
|
|
50
|
+
con.end()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
afterAll(async () => {
|
|
55
|
+
await databaseTestProviders.mysql.stop()
|
|
56
|
+
setup.afterAll()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
beforeAll(async () => {
|
|
60
|
+
await config.init()
|
|
61
|
+
datasource = await config.api.datasource.create(
|
|
62
|
+
await databaseTestProviders.mysql.datasource()
|
|
63
|
+
)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
beforeEach(async () => {
|
|
67
|
+
await withConnection(async connection => {
|
|
68
|
+
const resp = await connection.query(createTableSQL)
|
|
69
|
+
await connection.query(insertSQL)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
afterEach(async () => {
|
|
74
|
+
await withConnection(async connection => {
|
|
75
|
+
await connection.query(dropTableSQL)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("should execute a query", async () => {
|
|
80
|
+
const query = await createQuery({
|
|
81
|
+
fields: {
|
|
82
|
+
sql: "SELECT * FROM test_table ORDER BY id",
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const result = await config.api.query.execute(query._id!)
|
|
87
|
+
|
|
88
|
+
expect(result.data).toEqual([
|
|
89
|
+
{
|
|
90
|
+
id: 1,
|
|
91
|
+
name: "one",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 2,
|
|
95
|
+
name: "two",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: 3,
|
|
99
|
+
name: "three",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 4,
|
|
103
|
+
name: "four",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 5,
|
|
107
|
+
name: "five",
|
|
108
|
+
},
|
|
109
|
+
])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it("should be able to transform a query", async () => {
|
|
113
|
+
const query = await createQuery({
|
|
114
|
+
fields: {
|
|
115
|
+
sql: "SELECT * FROM test_table WHERE id = 1",
|
|
116
|
+
},
|
|
117
|
+
transformer: `
|
|
118
|
+
data[0].id = data[0].id + 1;
|
|
119
|
+
return data;
|
|
120
|
+
`,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const result = await config.api.query.execute(query._id!)
|
|
124
|
+
|
|
125
|
+
expect(result.data).toEqual([
|
|
126
|
+
{
|
|
127
|
+
id: 2,
|
|
128
|
+
name: "one",
|
|
129
|
+
},
|
|
130
|
+
])
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("should be able to insert with bindings", async () => {
|
|
134
|
+
const query = await createQuery({
|
|
135
|
+
fields: {
|
|
136
|
+
sql: "INSERT INTO test_table (name) VALUES ({{ foo }})",
|
|
137
|
+
},
|
|
138
|
+
parameters: [
|
|
139
|
+
{
|
|
140
|
+
name: "foo",
|
|
141
|
+
default: "bar",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
queryVerb: "create",
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const result = await config.api.query.execute(query._id!, {
|
|
148
|
+
parameters: {
|
|
149
|
+
foo: "baz",
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
expect(result.data).toEqual([
|
|
154
|
+
{
|
|
155
|
+
created: true,
|
|
156
|
+
},
|
|
157
|
+
])
|
|
158
|
+
|
|
159
|
+
await withConnection(async connection => {
|
|
160
|
+
const [rows] = await connection.query(
|
|
161
|
+
"SELECT * FROM test_table WHERE name = 'baz'"
|
|
162
|
+
)
|
|
163
|
+
expect(rows).toHaveLength(1)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it("should be able to update rows", async () => {
|
|
168
|
+
const query = await createQuery({
|
|
169
|
+
fields: {
|
|
170
|
+
sql: "UPDATE test_table SET name = {{ name }} WHERE id = {{ id }}",
|
|
171
|
+
},
|
|
172
|
+
parameters: [
|
|
173
|
+
{
|
|
174
|
+
name: "id",
|
|
175
|
+
default: "",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "name",
|
|
179
|
+
default: "updated",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
queryVerb: "update",
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const result = await config.api.query.execute(query._id!, {
|
|
186
|
+
parameters: {
|
|
187
|
+
id: "1",
|
|
188
|
+
name: "foo",
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
expect(result.data).toEqual([
|
|
193
|
+
{
|
|
194
|
+
updated: true,
|
|
195
|
+
},
|
|
196
|
+
])
|
|
197
|
+
|
|
198
|
+
await withConnection(async connection => {
|
|
199
|
+
const [rows] = await connection.query(
|
|
200
|
+
"SELECT * FROM test_table WHERE id = 1"
|
|
201
|
+
)
|
|
202
|
+
expect(rows).toEqual([{ id: 1, name: "foo" }])
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it("should be able to delete rows", async () => {
|
|
207
|
+
const query = await createQuery({
|
|
208
|
+
fields: {
|
|
209
|
+
sql: "DELETE FROM test_table WHERE id = {{ id }}",
|
|
210
|
+
},
|
|
211
|
+
parameters: [
|
|
212
|
+
{
|
|
213
|
+
name: "id",
|
|
214
|
+
default: "",
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
queryVerb: "delete",
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
const result = await config.api.query.execute(query._id!, {
|
|
221
|
+
parameters: {
|
|
222
|
+
id: "1",
|
|
223
|
+
},
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
expect(result.data).toEqual([
|
|
227
|
+
{
|
|
228
|
+
deleted: true,
|
|
229
|
+
},
|
|
230
|
+
])
|
|
231
|
+
|
|
232
|
+
await withConnection(async connection => {
|
|
233
|
+
const [rows] = await connection.query(
|
|
234
|
+
"SELECT * FROM test_table WHERE id = 1"
|
|
235
|
+
)
|
|
236
|
+
expect(rows).toHaveLength(0)
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
})
|
|
@@ -167,4 +167,77 @@ describe("/queries", () => {
|
|
|
167
167
|
expect(rows).toHaveLength(1)
|
|
168
168
|
})
|
|
169
169
|
})
|
|
170
|
+
|
|
171
|
+
it("should be able to update rows", async () => {
|
|
172
|
+
const query = await createQuery({
|
|
173
|
+
fields: {
|
|
174
|
+
sql: "UPDATE test_table SET name = {{ name }} WHERE id = {{ id }}",
|
|
175
|
+
},
|
|
176
|
+
parameters: [
|
|
177
|
+
{
|
|
178
|
+
name: "id",
|
|
179
|
+
default: "",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "name",
|
|
183
|
+
default: "updated",
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
queryVerb: "update",
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const result = await config.api.query.execute(query._id!, {
|
|
190
|
+
parameters: {
|
|
191
|
+
id: "1",
|
|
192
|
+
name: "foo",
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
expect(result.data).toEqual([
|
|
197
|
+
{
|
|
198
|
+
updated: true,
|
|
199
|
+
},
|
|
200
|
+
])
|
|
201
|
+
|
|
202
|
+
await withClient(async client => {
|
|
203
|
+
const { rows } = await client.query(
|
|
204
|
+
"SELECT * FROM test_table WHERE id = 1"
|
|
205
|
+
)
|
|
206
|
+
expect(rows).toEqual([{ id: 1, name: "foo" }])
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it("should be able to delete rows", async () => {
|
|
211
|
+
const query = await createQuery({
|
|
212
|
+
fields: {
|
|
213
|
+
sql: "DELETE FROM test_table WHERE id = {{ id }}",
|
|
214
|
+
},
|
|
215
|
+
parameters: [
|
|
216
|
+
{
|
|
217
|
+
name: "id",
|
|
218
|
+
default: "",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
queryVerb: "delete",
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const result = await config.api.query.execute(query._id!, {
|
|
225
|
+
parameters: {
|
|
226
|
+
id: "1",
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
expect(result.data).toEqual([
|
|
231
|
+
{
|
|
232
|
+
deleted: true,
|
|
233
|
+
},
|
|
234
|
+
])
|
|
235
|
+
|
|
236
|
+
await withClient(async client => {
|
|
237
|
+
const { rows } = await client.query(
|
|
238
|
+
"SELECT * FROM test_table WHERE id = 1"
|
|
239
|
+
)
|
|
240
|
+
expect(rows).toHaveLength(0)
|
|
241
|
+
})
|
|
242
|
+
})
|
|
170
243
|
})
|
|
@@ -3,6 +3,7 @@ jest.unmock("pg")
|
|
|
3
3
|
import { Datasource } from "@budibase/types"
|
|
4
4
|
import * as postgres from "./postgres"
|
|
5
5
|
import * as mongodb from "./mongodb"
|
|
6
|
+
import * as mysql from "./mysql"
|
|
6
7
|
import { StartedTestContainer } from "testcontainers"
|
|
7
8
|
|
|
8
9
|
jest.setTimeout(30000)
|
|
@@ -13,4 +14,4 @@ export interface DatabaseProvider {
|
|
|
13
14
|
datasource(): Promise<Datasource>
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export const databaseTestProviders = { postgres, mongodb }
|
|
17
|
+
export const databaseTestProviders = { postgres, mongodb, mysql }
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Datasource, SourceName } from "@budibase/types"
|
|
2
|
+
import { GenericContainer, Wait, StartedTestContainer } from "testcontainers"
|
|
3
|
+
|
|
4
|
+
let container: StartedTestContainer | undefined
|
|
5
|
+
|
|
6
|
+
export async function start(): Promise<StartedTestContainer> {
|
|
7
|
+
return await new GenericContainer("mysql:8.3")
|
|
8
|
+
.withExposedPorts(3306)
|
|
9
|
+
.withEnvironment({ MYSQL_ROOT_PASSWORD: "password" })
|
|
10
|
+
.withWaitStrategy(
|
|
11
|
+
Wait.forSuccessfulCommand(
|
|
12
|
+
// Because MySQL first starts itself up, runs an init script, then restarts,
|
|
13
|
+
// it's possible for the mysqladmin ping to succeed early and then tests to
|
|
14
|
+
// run against a MySQL that's mid-restart and fail. To avoid this, we run
|
|
15
|
+
// the ping command three times with a small delay between each.
|
|
16
|
+
`
|
|
17
|
+
mysqladmin ping -h localhost -P 3306 -u root -ppassword && sleep 1 &&
|
|
18
|
+
mysqladmin ping -h localhost -P 3306 -u root -ppassword && sleep 1 &&
|
|
19
|
+
mysqladmin ping -h localhost -P 3306 -u root -ppassword && sleep 1 &&
|
|
20
|
+
mysqladmin ping -h localhost -P 3306 -u root -ppassword
|
|
21
|
+
`
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
.start()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function datasource(): Promise<Datasource> {
|
|
28
|
+
if (!container) {
|
|
29
|
+
container = await start()
|
|
30
|
+
}
|
|
31
|
+
const host = container.getHost()
|
|
32
|
+
const port = container.getMappedPort(3306)
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
type: "datasource_plus",
|
|
36
|
+
source: SourceName.MYSQL,
|
|
37
|
+
plus: true,
|
|
38
|
+
config: {
|
|
39
|
+
host,
|
|
40
|
+
port,
|
|
41
|
+
user: "root",
|
|
42
|
+
password: "password",
|
|
43
|
+
database: "mysql",
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function stop() {
|
|
49
|
+
if (container) {
|
|
50
|
+
await container.stop()
|
|
51
|
+
container = undefined
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -8,9 +8,7 @@ export async function start(): Promise<StartedTestContainer> {
|
|
|
8
8
|
.withExposedPorts(5432)
|
|
9
9
|
.withEnvironment({ POSTGRES_PASSWORD: "password" })
|
|
10
10
|
.withWaitStrategy(
|
|
11
|
-
Wait.forSuccessfulCommand(
|
|
12
|
-
"pg_isready -h localhost -p 5432"
|
|
13
|
-
).withStartupTimeout(10000)
|
|
11
|
+
Wait.forSuccessfulCommand("pg_isready -h localhost -p 5432")
|
|
14
12
|
)
|
|
15
13
|
.start()
|
|
16
14
|
}
|