@budibase/server 1.0.26 → 1.0.27-alpha.11
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 +21 -0
- package/builder/assets/bb-spaceship.8f6e81e1.svg +227 -0
- package/builder/assets/{index.4fb3f887.css → index.0dce895b.css} +1 -1
- package/builder/assets/index.b98dd7ca.js +1348 -0
- package/builder/assets/{vendor.470042a4.js → vendor.f9faf6ab.js} +88 -88
- package/builder/index.html +3 -3
- package/coverage/clover.xml +1364 -1121
- package/coverage/coverage-final.json +37 -36
- package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/application.js.html +59 -59
- package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +22 -10
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +4 -4
- package/coverage/lcov-report/api/controllers/datasource.js.html +170 -68
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +39 -54
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +126 -0
- package/coverage/lcov-report/api/controllers/{query.js.html → query/index.js.html} +225 -114
- package/coverage/lcov-report/api/controllers/query/validation.js.html +206 -0
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +22 -22
- package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +38 -11
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +7 -4
- package/coverage/lcov-report/api/controllers/row/utils.js.html +29 -20
- package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +108 -9
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +18 -6
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +5 -5
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/application.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +9 -9
- package/coverage/lcov-report/api/routes/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +19 -85
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/row.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/static.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +1 -1
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +32 -2
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +1 -1
- package/coverage/lcov-report/automations/index.html +20 -20
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +31 -7
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +11 -11
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +5 -8
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +1 -1
- package/coverage/lcov-report/automations/utils.js.html +48 -15
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +22 -22
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/client.js.html +18 -18
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +15 -15
- package/coverage/lcov-report/db/index.js.html +3 -3
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +4 -4
- package/coverage/lcov-report/db/utils.js.html +107 -80
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +99 -84
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
- package/coverage/lcov-report/middleware/authorized.js.html +10 -10
- package/coverage/lcov-report/middleware/builder.js.html +8 -8
- package/coverage/lcov-report/middleware/currentapp.js.html +28 -28
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +11 -11
- package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +2 -2
- package/coverage/lcov-report/threads/automation.js.html +1 -1
- package/coverage/lcov-report/threads/index.html +31 -31
- package/coverage/lcov-report/threads/index.js.html +12 -12
- package/coverage/lcov-report/threads/query.js.html +396 -69
- package/coverage/lcov-report/threads/utils.js.html +358 -13
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +13 -13
- package/coverage/lcov-report/utilities/index.html +19 -19
- package/coverage/lcov-report/utilities/index.js.html +69 -36
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +19 -19
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +235 -10
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +2 -2
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +19 -19
- package/coverage/lcov.info +2294 -1874
- package/dist/api/controllers/automation.js +7 -5
- package/dist/api/controllers/datasource.js +29 -0
- package/dist/api/controllers/query/import/index.js +79 -0
- package/dist/api/controllers/query/import/sources/base/index.js +64 -0
- package/dist/api/controllers/query/import/sources/base/openapi.js +42 -0
- package/dist/api/controllers/query/import/sources/curl.js +96 -0
- package/dist/api/controllers/query/import/sources/openapi2.js +142 -0
- package/dist/api/controllers/query/import/sources/tests/openapi2/data/crud/crud.json +253 -0
- package/dist/api/controllers/query/import/sources/tests/openapi2/data/petstore/petstore.json +1054 -0
- package/dist/api/controllers/{query.js → query/index.js} +86 -52
- package/dist/api/controllers/query/validation.js +38 -0
- package/dist/api/controllers/row/internal.js +14 -6
- package/dist/api/controllers/row/internalSearch.js +2 -1
- package/dist/api/controllers/row/utils.js +13 -6
- package/dist/api/controllers/user.js +37 -1
- package/dist/api/controllers/webhook.js +4 -0
- package/dist/api/routes/query.js +5 -33
- package/dist/api/routes/user.js +3 -1
- package/dist/automations/steps/executeQuery.js +8 -1
- package/dist/automations/steps/outgoingWebhook.js +2 -3
- package/dist/automations/utils.js +10 -0
- package/dist/db/utils.js +8 -0
- package/dist/definitions/datasource.js +6 -1
- package/dist/integrations/base/sqlTable.js +1 -1
- package/dist/integrations/rest.js +207 -117
- package/dist/package.json +11 -4
- package/dist/tests/utilities/TestConfiguration.js +47 -0
- package/dist/threads/query.js +148 -40
- package/dist/threads/utils.js +105 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/fileSystem/utilities.js +2 -1
- package/dist/utilities/index.js +10 -0
- package/dist/utilities/rowProcessor/index.js +76 -0
- package/package.json +12 -5
- package/src/api/controllers/automation.js +8 -4
- package/src/api/controllers/datasource.js +34 -0
- package/src/api/controllers/query/import/index.ts +85 -0
- package/src/api/controllers/query/import/sources/base/index.ts +87 -0
- package/src/api/controllers/query/import/sources/base/openapi.ts +24 -0
- package/src/api/controllers/query/import/sources/curl.ts +102 -0
- package/src/api/controllers/query/import/sources/openapi2.ts +163 -0
- package/src/api/controllers/query/import/sources/tests/curl/curl.spec.js +103 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/delete.txt +1 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/empty-body.txt +2 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/get.txt +1 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/headers.txt +3 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/patch.txt +2 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/path.txt +1 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/post.txt +2 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/put.txt +2 -0
- package/src/api/controllers/query/import/sources/tests/curl/data/query.txt +1 -0
- package/src/api/controllers/query/import/sources/tests/openapi2/data/crud/crud.json +253 -0
- package/src/api/controllers/query/import/sources/tests/openapi2/data/crud/crud.yaml +160 -0
- package/src/api/controllers/query/import/sources/tests/openapi2/data/petstore/petstore.json +1054 -0
- package/src/api/controllers/query/import/sources/tests/openapi2/data/petstore/petstore.yaml +711 -0
- package/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js +238 -0
- package/src/api/controllers/query/import/tests/index.spec.js +109 -0
- package/src/api/controllers/query/index.js +195 -0
- package/src/api/controllers/query/validation.js +42 -0
- package/src/api/controllers/row/internal.js +14 -5
- package/src/api/controllers/row/internalSearch.js +2 -1
- package/src/api/controllers/row/utils.js +11 -8
- package/src/api/controllers/user.js +33 -0
- package/src/api/controllers/webhook.js +4 -0
- package/src/api/routes/query.js +13 -35
- package/src/api/routes/tests/datasource.spec.js +45 -0
- package/src/api/routes/tests/query.spec.js +69 -0
- package/src/api/routes/user.js +10 -0
- package/src/automations/steps/executeQuery.js +9 -1
- package/src/automations/steps/outgoingWebhook.js +2 -3
- package/src/automations/utils.js +11 -0
- package/src/db/utils.js +9 -0
- package/src/definitions/common.ts +2 -16
- package/src/definitions/datasource.ts +83 -1
- package/src/integrations/base/IntegrationBase.ts +4 -4
- package/src/integrations/base/sqlTable.ts +1 -1
- package/src/integrations/oracle.ts +1 -1
- package/src/integrations/rest.ts +230 -137
- package/src/integrations/tests/rest.spec.js +167 -19
- package/src/tests/utilities/TestConfiguration.js +57 -0
- package/src/threads/query.js +149 -40
- package/src/threads/utils.js +115 -0
- package/src/utilities/fileSystem/utilities.js +2 -0
- package/src/utilities/index.js +11 -0
- package/src/utilities/rowProcessor/index.js +75 -0
- package/builder/assets/index.8adef83f.js +0 -1316
- package/src/api/controllers/query.js +0 -158
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
jest.mock("node-fetch", () =>
|
|
2
2
|
jest.fn(() => ({
|
|
3
3
|
headers: {
|
|
4
|
+
raw: () => {
|
|
5
|
+
return { "content-type": ["application/json"] }
|
|
6
|
+
},
|
|
4
7
|
get: () => ["application/json"]
|
|
5
8
|
},
|
|
6
9
|
json: jest.fn(),
|
|
@@ -9,6 +12,12 @@ jest.mock("node-fetch", () =>
|
|
|
9
12
|
)
|
|
10
13
|
const fetch = require("node-fetch")
|
|
11
14
|
const RestIntegration = require("../rest")
|
|
15
|
+
const { AuthType } = require("../rest")
|
|
16
|
+
|
|
17
|
+
const HEADERS = {
|
|
18
|
+
"Accept": "application/json",
|
|
19
|
+
"Content-Type": "application/json"
|
|
20
|
+
}
|
|
12
21
|
|
|
13
22
|
class TestConfiguration {
|
|
14
23
|
constructor(config = {}) {
|
|
@@ -24,26 +33,24 @@ describe("REST Integration", () => {
|
|
|
24
33
|
config = new TestConfiguration({
|
|
25
34
|
url: BASE_URL,
|
|
26
35
|
})
|
|
36
|
+
jest.clearAllMocks()
|
|
27
37
|
})
|
|
28
38
|
|
|
29
39
|
it("calls the create method with the correct params", async () => {
|
|
30
40
|
const query = {
|
|
31
41
|
path: "api",
|
|
32
42
|
queryString: "test=1",
|
|
33
|
-
headers:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
json: {
|
|
43
|
+
headers: HEADERS,
|
|
44
|
+
bodyType: "json",
|
|
45
|
+
requestBody: JSON.stringify({
|
|
37
46
|
name: "test",
|
|
38
|
-
},
|
|
47
|
+
}),
|
|
39
48
|
}
|
|
40
49
|
const response = await config.integration.create(query)
|
|
41
50
|
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
|
|
42
51
|
method: "POST",
|
|
43
52
|
body: '{"name":"test"}',
|
|
44
|
-
headers:
|
|
45
|
-
Accept: "application/json",
|
|
46
|
-
},
|
|
53
|
+
headers: HEADERS,
|
|
47
54
|
})
|
|
48
55
|
})
|
|
49
56
|
|
|
@@ -60,6 +67,7 @@ describe("REST Integration", () => {
|
|
|
60
67
|
headers: {
|
|
61
68
|
Accept: "text/html",
|
|
62
69
|
},
|
|
70
|
+
method: "GET",
|
|
63
71
|
})
|
|
64
72
|
})
|
|
65
73
|
|
|
@@ -70,17 +78,16 @@ describe("REST Integration", () => {
|
|
|
70
78
|
headers: {
|
|
71
79
|
Accept: "application/json",
|
|
72
80
|
},
|
|
73
|
-
|
|
81
|
+
bodyType: "json",
|
|
82
|
+
requestBody: JSON.stringify({
|
|
74
83
|
name: "test",
|
|
75
|
-
},
|
|
84
|
+
}),
|
|
76
85
|
}
|
|
77
86
|
const response = await config.integration.update(query)
|
|
78
87
|
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
|
|
79
|
-
method: "
|
|
88
|
+
method: "PUT",
|
|
80
89
|
body: '{"name":"test"}',
|
|
81
|
-
headers:
|
|
82
|
-
Accept: "application/json",
|
|
83
|
-
},
|
|
90
|
+
headers: HEADERS,
|
|
84
91
|
})
|
|
85
92
|
})
|
|
86
93
|
|
|
@@ -91,16 +98,157 @@ describe("REST Integration", () => {
|
|
|
91
98
|
headers: {
|
|
92
99
|
Accept: "application/json",
|
|
93
100
|
},
|
|
94
|
-
|
|
101
|
+
bodyType: "json",
|
|
102
|
+
requestBody: JSON.stringify({
|
|
95
103
|
name: "test",
|
|
96
|
-
},
|
|
104
|
+
}),
|
|
97
105
|
}
|
|
98
106
|
const response = await config.integration.delete(query)
|
|
99
107
|
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
|
|
100
108
|
method: "DELETE",
|
|
101
|
-
headers:
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
headers: HEADERS,
|
|
110
|
+
body: '{"name":"test"}',
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe("request body", () => {
|
|
115
|
+
const input = { a: 1, b: 2 }
|
|
116
|
+
|
|
117
|
+
it("should allow no body", () => {
|
|
118
|
+
const output = config.integration.addBody("none", null, {})
|
|
119
|
+
expect(output.body).toBeUndefined()
|
|
120
|
+
expect(Object.keys(output.headers).length).toEqual(0)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it("should allow text body", () => {
|
|
124
|
+
const output = config.integration.addBody("text", "hello world", {})
|
|
125
|
+
expect(output.body).toEqual("hello world")
|
|
126
|
+
// gets added by fetch
|
|
127
|
+
expect(Object.keys(output.headers).length).toEqual(0)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it("should allow form data", () => {
|
|
131
|
+
const FormData = require("form-data")
|
|
132
|
+
const output = config.integration.addBody("form", input, {})
|
|
133
|
+
expect(output.body instanceof FormData).toEqual(true)
|
|
134
|
+
expect(output.body._valueLength).toEqual(2)
|
|
135
|
+
// gets added by fetch
|
|
136
|
+
expect(Object.keys(output.headers).length).toEqual(0)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("should allow encoded form data", () => {
|
|
140
|
+
const { URLSearchParams } = require("url")
|
|
141
|
+
const output = config.integration.addBody("encoded", input, {})
|
|
142
|
+
expect(output.body instanceof URLSearchParams).toEqual(true)
|
|
143
|
+
expect(output.body.toString()).toEqual("a=1&b=2")
|
|
144
|
+
// gets added by fetch
|
|
145
|
+
expect(Object.keys(output.headers).length).toEqual(0)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it("should allow JSON", () => {
|
|
149
|
+
const output = config.integration.addBody("json", input, {})
|
|
150
|
+
expect(output.body).toEqual(JSON.stringify(input))
|
|
151
|
+
expect(output.headers["Content-Type"]).toEqual("application/json")
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it("should allow XML", () => {
|
|
155
|
+
const output = config.integration.addBody("xml", input, {})
|
|
156
|
+
expect(output.body.includes("<a>1</a>")).toEqual(true)
|
|
157
|
+
expect(output.body.includes("<b>2</b>")).toEqual(true)
|
|
158
|
+
expect(output.headers["Content-Type"]).toEqual("application/xml")
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
describe("response", () => {
|
|
163
|
+
function buildInput(json, text, header) {
|
|
164
|
+
return {
|
|
165
|
+
status: 200,
|
|
166
|
+
json: json ? async () => json : undefined,
|
|
167
|
+
text: text ? async () => text : undefined,
|
|
168
|
+
headers: { get: key => key === "content-length" ? 100 : header, raw: () => ({ "content-type": header }) }
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
it("should be able to parse JSON response", async () => {
|
|
173
|
+
const input = buildInput({a: 1}, null, "application/json")
|
|
174
|
+
const output = await config.integration.parseResponse(input)
|
|
175
|
+
expect(output.data).toEqual({a: 1})
|
|
176
|
+
expect(output.info.code).toEqual(200)
|
|
177
|
+
expect(output.info.size).toEqual("100B")
|
|
178
|
+
expect(output.extra.raw).toEqual(JSON.stringify({a: 1}))
|
|
179
|
+
expect(output.extra.headers["content-type"]).toEqual("application/json")
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it("should be able to parse text response", async () => {
|
|
183
|
+
const text = "hello world"
|
|
184
|
+
const input = buildInput(null, text, "text/plain")
|
|
185
|
+
const output = await config.integration.parseResponse(input)
|
|
186
|
+
expect(output.data).toEqual(text)
|
|
187
|
+
expect(output.extra.raw).toEqual(text)
|
|
188
|
+
expect(output.extra.headers["content-type"]).toEqual("text/plain")
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it("should be able to parse XML response", async () => {
|
|
192
|
+
const text = "<root><a>1</a><b>2</b></root>"
|
|
193
|
+
const input = buildInput(null, text, "application/xml")
|
|
194
|
+
const output = await config.integration.parseResponse(input)
|
|
195
|
+
expect(output.data).toEqual({a: "1", b: "2"})
|
|
196
|
+
expect(output.extra.raw).toEqual(text)
|
|
197
|
+
expect(output.extra.headers["content-type"]).toEqual("application/xml")
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
describe("authentication", () => {
|
|
202
|
+
const basicAuth = {
|
|
203
|
+
_id: "c59c14bd1898a43baa08da68959b24686",
|
|
204
|
+
name: "basic-1",
|
|
205
|
+
type : AuthType.BASIC,
|
|
206
|
+
config : {
|
|
207
|
+
username: "user",
|
|
208
|
+
password: "password"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const bearerAuth = {
|
|
213
|
+
_id: "0d91d732f34e4befabeff50b392a8ff3",
|
|
214
|
+
name: "bearer-1",
|
|
215
|
+
type : AuthType.BEARER,
|
|
216
|
+
config : {
|
|
217
|
+
"token": "mytoken"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
beforeEach(() => {
|
|
222
|
+
config = new TestConfiguration({
|
|
223
|
+
url: BASE_URL,
|
|
224
|
+
authConfigs : [basicAuth, bearerAuth]
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it("adds basic auth", async () => {
|
|
229
|
+
const query = {
|
|
230
|
+
authConfigId: "c59c14bd1898a43baa08da68959b24686"
|
|
231
|
+
}
|
|
232
|
+
await config.integration.read(query)
|
|
233
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, {
|
|
234
|
+
method: "GET",
|
|
235
|
+
headers: {
|
|
236
|
+
Authorization: "Basic dXNlcjpwYXNzd29yZA=="
|
|
237
|
+
},
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it("adds bearer auth", async () => {
|
|
242
|
+
const query = {
|
|
243
|
+
authConfigId: "0d91d732f34e4befabeff50b392a8ff3"
|
|
244
|
+
}
|
|
245
|
+
await config.integration.read(query)
|
|
246
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, {
|
|
247
|
+
method: "GET",
|
|
248
|
+
headers: {
|
|
249
|
+
Authorization: "Bearer mytoken"
|
|
250
|
+
},
|
|
251
|
+
})
|
|
104
252
|
})
|
|
105
253
|
})
|
|
106
254
|
})
|
|
@@ -316,6 +316,63 @@ class TestConfiguration {
|
|
|
316
316
|
return this.datasource
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
async updateDatasource(datasource) {
|
|
320
|
+
const response = await this._req(
|
|
321
|
+
datasource,
|
|
322
|
+
{ datasourceId: datasource._id },
|
|
323
|
+
controllers.datasource.update
|
|
324
|
+
)
|
|
325
|
+
this.datasource = response.datasource
|
|
326
|
+
return this.datasource
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async restDatasource(cfg) {
|
|
330
|
+
return this.createDatasource({
|
|
331
|
+
datasource: {
|
|
332
|
+
...basicDatasource().datasource,
|
|
333
|
+
source: "REST",
|
|
334
|
+
config: cfg || {},
|
|
335
|
+
},
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async dynamicVariableDatasource() {
|
|
340
|
+
let datasource = await this.restDatasource()
|
|
341
|
+
const basedOnQuery = await this.createQuery({
|
|
342
|
+
...basicQuery(datasource._id),
|
|
343
|
+
fields: {
|
|
344
|
+
path: "www.google.com",
|
|
345
|
+
},
|
|
346
|
+
})
|
|
347
|
+
datasource = await this.updateDatasource({
|
|
348
|
+
...datasource,
|
|
349
|
+
config: {
|
|
350
|
+
dynamicVariables: [
|
|
351
|
+
{
|
|
352
|
+
queryId: basedOnQuery._id,
|
|
353
|
+
name: "variable3",
|
|
354
|
+
value: "{{ data.0.[value] }}",
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
},
|
|
358
|
+
})
|
|
359
|
+
return { datasource, query: basedOnQuery }
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async previewQuery(request, config, datasource, fields) {
|
|
363
|
+
return request
|
|
364
|
+
.post(`/api/queries/preview`)
|
|
365
|
+
.send({
|
|
366
|
+
datasourceId: datasource._id,
|
|
367
|
+
parameters: {},
|
|
368
|
+
fields,
|
|
369
|
+
queryVerb: "read",
|
|
370
|
+
})
|
|
371
|
+
.set(config.defaultHeaders())
|
|
372
|
+
.expect("Content-Type", /json/)
|
|
373
|
+
.expect(200)
|
|
374
|
+
}
|
|
375
|
+
|
|
319
376
|
async createQuery(config = null) {
|
|
320
377
|
if (!this.datasource && !config) {
|
|
321
378
|
throw "No data source created for query."
|
package/src/threads/query.js
CHANGED
|
@@ -1,60 +1,169 @@
|
|
|
1
|
-
require("./utils")
|
|
1
|
+
const threadUtils = require("./utils")
|
|
2
|
+
threadUtils.threadSetup()
|
|
2
3
|
const ScriptRunner = require("../utilities/scriptRunner")
|
|
3
4
|
const { integrations } = require("../integrations")
|
|
5
|
+
const { processStringSync } = require("@budibase/string-templates")
|
|
6
|
+
const CouchDB = require("../db")
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
class QueryRunner {
|
|
9
|
+
constructor(input, flags = { noRecursiveQuery: false }) {
|
|
10
|
+
this.appId = input.appId
|
|
11
|
+
this.datasource = input.datasource
|
|
12
|
+
this.queryVerb = input.queryVerb
|
|
13
|
+
this.fields = input.fields
|
|
14
|
+
this.parameters = input.parameters
|
|
15
|
+
this.transformer = input.transformer
|
|
16
|
+
this.queryId = input.queryId
|
|
17
|
+
this.noRecursiveQuery = flags.noRecursiveQuery
|
|
18
|
+
this.cachedVariables = []
|
|
19
|
+
// allows the response from a query to be stored throughout this
|
|
20
|
+
// execution so that if it needs to be re-used for another variable
|
|
21
|
+
// it can be
|
|
22
|
+
this.queryResponse = {}
|
|
23
|
+
this.hasRerun = false
|
|
12
24
|
}
|
|
13
|
-
return resp
|
|
14
|
-
}
|
|
15
25
|
|
|
16
|
-
async
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
async execute() {
|
|
27
|
+
let { datasource, fields, queryVerb, transformer } = this
|
|
28
|
+
// pre-query, make sure datasource variables are added to parameters
|
|
29
|
+
const parameters = await this.addDatasourceVariables()
|
|
30
|
+
const query = threadUtils.enrichQueryFields(fields, parameters)
|
|
31
|
+
const Integration = integrations[datasource.source]
|
|
32
|
+
if (!Integration) {
|
|
33
|
+
throw "Integration type does not exist."
|
|
34
|
+
}
|
|
35
|
+
const integration = new Integration(datasource.config)
|
|
22
36
|
|
|
23
|
-
|
|
37
|
+
let output = threadUtils.formatResponse(await integration[queryVerb](query))
|
|
38
|
+
let rows = output,
|
|
39
|
+
info = undefined,
|
|
40
|
+
extra = undefined
|
|
41
|
+
if (threadUtils.hasExtraData(output)) {
|
|
42
|
+
rows = output.data
|
|
43
|
+
info = output.info
|
|
44
|
+
extra = output.extra
|
|
45
|
+
}
|
|
24
46
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
// transform as required
|
|
48
|
+
if (transformer) {
|
|
49
|
+
const runner = new ScriptRunner(transformer, {
|
|
50
|
+
data: rows,
|
|
51
|
+
params: parameters,
|
|
52
|
+
})
|
|
53
|
+
rows = runner.execute()
|
|
54
|
+
}
|
|
30
55
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
56
|
+
// if the request fails we retry once, invalidating the cached value
|
|
57
|
+
if (
|
|
58
|
+
info &&
|
|
59
|
+
info.code >= 400 &&
|
|
60
|
+
this.cachedVariables.length > 0 &&
|
|
61
|
+
!this.hasRerun
|
|
62
|
+
) {
|
|
63
|
+
this.hasRerun = true
|
|
64
|
+
// invalidate the cache value
|
|
65
|
+
await threadUtils.invalidateDynamicVariables(this.cachedVariables)
|
|
66
|
+
return this.execute()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// needs to an array for next step
|
|
70
|
+
if (!Array.isArray(rows)) {
|
|
71
|
+
rows = [rows]
|
|
72
|
+
}
|
|
35
73
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
74
|
+
// map into JSON if just raw primitive here
|
|
75
|
+
if (rows.find(row => typeof row !== "object")) {
|
|
76
|
+
rows = rows.map(value => ({ value }))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// get all the potential fields in the schema
|
|
80
|
+
let keys = rows.flatMap(Object.keys)
|
|
81
|
+
|
|
82
|
+
if (integration.end) {
|
|
83
|
+
integration.end()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return { rows, keys, info, extra }
|
|
39
87
|
}
|
|
40
88
|
|
|
41
|
-
|
|
42
|
-
|
|
89
|
+
async runAnotherQuery(queryId, parameters) {
|
|
90
|
+
const db = new CouchDB(this.appId)
|
|
91
|
+
const query = await db.get(queryId)
|
|
92
|
+
const datasource = await db.get(query.datasourceId)
|
|
93
|
+
return new QueryRunner(
|
|
94
|
+
{
|
|
95
|
+
appId: this.appId,
|
|
96
|
+
datasource,
|
|
97
|
+
queryVerb: query.queryVerb,
|
|
98
|
+
fields: query.fields,
|
|
99
|
+
parameters,
|
|
100
|
+
transformer: query.transformer,
|
|
101
|
+
},
|
|
102
|
+
{ noRecursiveQuery: true }
|
|
103
|
+
).execute()
|
|
104
|
+
}
|
|
43
105
|
|
|
44
|
-
|
|
45
|
-
|
|
106
|
+
async getDynamicVariable(variable) {
|
|
107
|
+
let { parameters } = this
|
|
108
|
+
const queryId = variable.queryId,
|
|
109
|
+
name = variable.name
|
|
110
|
+
let value = await threadUtils.checkCacheForDynamicVariable(queryId, name)
|
|
111
|
+
if (!value) {
|
|
112
|
+
value = this.queryResponse[queryId]
|
|
113
|
+
? this.queryResponse[queryId]
|
|
114
|
+
: await this.runAnotherQuery(queryId, parameters)
|
|
115
|
+
// store incase this query is to be called again
|
|
116
|
+
this.queryResponse[queryId] = value
|
|
117
|
+
await threadUtils.storeDynamicVariable(queryId, name, value)
|
|
118
|
+
} else {
|
|
119
|
+
this.cachedVariables.push({ queryId, name })
|
|
120
|
+
}
|
|
121
|
+
return value
|
|
46
122
|
}
|
|
47
123
|
|
|
48
|
-
|
|
124
|
+
async addDatasourceVariables() {
|
|
125
|
+
let { datasource, parameters, fields } = this
|
|
126
|
+
if (!datasource || !datasource.config) {
|
|
127
|
+
return parameters
|
|
128
|
+
}
|
|
129
|
+
const staticVars = datasource.config.staticVariables || {}
|
|
130
|
+
const dynamicVars = datasource.config.dynamicVariables || []
|
|
131
|
+
for (let [key, value] of Object.entries(staticVars)) {
|
|
132
|
+
if (!parameters[key]) {
|
|
133
|
+
parameters[key] = value
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!this.noRecursiveQuery) {
|
|
137
|
+
// need to see if this uses any variables
|
|
138
|
+
const stringFields = JSON.stringify(fields)
|
|
139
|
+
const foundVars = dynamicVars.filter(variable => {
|
|
140
|
+
// don't allow a query to use its own dynamic variable (loop)
|
|
141
|
+
if (variable.queryId === this.queryId) {
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
// look for {{ variable }} but allow spaces between handlebars
|
|
145
|
+
const regex = new RegExp(`{{[ ]*${variable.name}[ ]*}}`)
|
|
146
|
+
return regex.test(stringFields)
|
|
147
|
+
})
|
|
148
|
+
const dynamics = foundVars.map(dynVar => this.getDynamicVariable(dynVar))
|
|
149
|
+
const responses = await Promise.all(dynamics)
|
|
150
|
+
for (let i = 0; i < foundVars.length; i++) {
|
|
151
|
+
const variable = foundVars[i]
|
|
152
|
+
parameters[variable.name] = processStringSync(variable.value, {
|
|
153
|
+
data: responses[i].rows,
|
|
154
|
+
info: responses[i].extra,
|
|
155
|
+
})
|
|
156
|
+
// make sure its known that this uses dynamic variables in case it fails
|
|
157
|
+
this.hasDynamicVariables = true
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return parameters
|
|
161
|
+
}
|
|
49
162
|
}
|
|
50
163
|
|
|
51
164
|
module.exports = (input, callback) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
input.queryVerb,
|
|
55
|
-
input.query,
|
|
56
|
-
input.transformer
|
|
57
|
-
)
|
|
165
|
+
const Runner = new QueryRunner(input)
|
|
166
|
+
Runner.execute()
|
|
58
167
|
.then(response => {
|
|
59
168
|
callback(null, response)
|
|
60
169
|
})
|
package/src/threads/utils.js
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
const env = require("../environment")
|
|
2
2
|
const CouchDB = require("../db")
|
|
3
3
|
const { init } = require("@budibase/auth")
|
|
4
|
+
const redis = require("@budibase/auth/redis")
|
|
5
|
+
const { SEPARATOR } = require("@budibase/auth/db")
|
|
6
|
+
const { processStringSync } = require("@budibase/string-templates")
|
|
7
|
+
|
|
8
|
+
const VARIABLE_TTL_SECONDS = 3600
|
|
9
|
+
let client
|
|
10
|
+
|
|
11
|
+
const IS_TRIPLE_BRACE = new RegExp(/^{{3}.*}{3}$/)
|
|
12
|
+
const IS_HANDLEBARS = new RegExp(/^{{2}.*}{2}$/)
|
|
13
|
+
|
|
14
|
+
async function getClient() {
|
|
15
|
+
if (!client) {
|
|
16
|
+
client = await new redis.Client(redis.utils.Databases.QUERY_VARS).init()
|
|
17
|
+
}
|
|
18
|
+
return client
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
process.on("exit", async () => {
|
|
22
|
+
if (client) await client.finish()
|
|
23
|
+
})
|
|
4
24
|
|
|
5
25
|
exports.threadSetup = () => {
|
|
6
26
|
// don't run this if not threading
|
|
@@ -11,3 +31,98 @@ exports.threadSetup = () => {
|
|
|
11
31
|
env.setInThread()
|
|
12
32
|
init(CouchDB)
|
|
13
33
|
}
|
|
34
|
+
|
|
35
|
+
function makeVariableKey(queryId, variable) {
|
|
36
|
+
return `${queryId}${SEPARATOR}${variable}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.checkCacheForDynamicVariable = async (queryId, variable) => {
|
|
40
|
+
const cache = await getClient()
|
|
41
|
+
return cache.get(makeVariableKey(queryId, variable))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
exports.invalidateDynamicVariables = async cachedVars => {
|
|
45
|
+
const cache = await getClient()
|
|
46
|
+
let promises = []
|
|
47
|
+
for (let variable of cachedVars) {
|
|
48
|
+
promises.push(
|
|
49
|
+
cache.delete(makeVariableKey(variable.queryId, variable.name))
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
await Promise.all(promises)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exports.storeDynamicVariable = async (queryId, variable, value) => {
|
|
56
|
+
const cache = await getClient()
|
|
57
|
+
await cache.store(
|
|
58
|
+
makeVariableKey(queryId, variable),
|
|
59
|
+
value,
|
|
60
|
+
VARIABLE_TTL_SECONDS
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
exports.formatResponse = resp => {
|
|
65
|
+
if (typeof resp === "string") {
|
|
66
|
+
try {
|
|
67
|
+
resp = JSON.parse(resp)
|
|
68
|
+
} catch (err) {
|
|
69
|
+
resp = { response: resp }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return resp
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
exports.hasExtraData = response => {
|
|
76
|
+
return (
|
|
77
|
+
typeof response === "object" &&
|
|
78
|
+
!Array.isArray(response) &&
|
|
79
|
+
response.data != null &&
|
|
80
|
+
response.info != null
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.enrichQueryFields = (fields, parameters = {}) => {
|
|
85
|
+
const enrichedQuery = {}
|
|
86
|
+
|
|
87
|
+
// enrich the fields with dynamic parameters
|
|
88
|
+
for (let key of Object.keys(fields)) {
|
|
89
|
+
if (fields[key] == null) {
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
if (typeof fields[key] === "object") {
|
|
93
|
+
// enrich nested fields object
|
|
94
|
+
enrichedQuery[key] = this.enrichQueryFields(fields[key], parameters)
|
|
95
|
+
} else if (typeof fields[key] === "string") {
|
|
96
|
+
// enrich string value as normal
|
|
97
|
+
let value = fields[key]
|
|
98
|
+
// add triple brace to avoid escaping e.g. '=' in cookie header
|
|
99
|
+
if (IS_HANDLEBARS.test(value) && !IS_TRIPLE_BRACE.test(value)) {
|
|
100
|
+
value = `{${value}}`
|
|
101
|
+
}
|
|
102
|
+
enrichedQuery[key] = processStringSync(value, parameters, {
|
|
103
|
+
noHelpers: true,
|
|
104
|
+
})
|
|
105
|
+
} else {
|
|
106
|
+
enrichedQuery[key] = fields[key]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
enrichedQuery.json ||
|
|
112
|
+
enrichedQuery.customData ||
|
|
113
|
+
enrichedQuery.requestBody
|
|
114
|
+
) {
|
|
115
|
+
try {
|
|
116
|
+
enrichedQuery.json = JSON.parse(
|
|
117
|
+
enrichedQuery.json ||
|
|
118
|
+
enrichedQuery.customData ||
|
|
119
|
+
enrichedQuery.requestBody
|
|
120
|
+
)
|
|
121
|
+
} catch (err) {
|
|
122
|
+
// no json found, ignore
|
|
123
|
+
}
|
|
124
|
+
delete enrichedQuery.customData
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return enrichedQuery
|
|
128
|
+
}
|
|
@@ -2,6 +2,7 @@ const {
|
|
|
2
2
|
ObjectStore,
|
|
3
3
|
makeSureBucketExists,
|
|
4
4
|
upload,
|
|
5
|
+
deleteFiles,
|
|
5
6
|
streamUpload,
|
|
6
7
|
retrieve,
|
|
7
8
|
retrieveToTmp,
|
|
@@ -28,3 +29,4 @@ exports.retrieveToTmp = retrieveToTmp
|
|
|
28
29
|
exports.deleteFolder = deleteFolder
|
|
29
30
|
exports.uploadDirectory = uploadDirectory
|
|
30
31
|
exports.downloadTarball = downloadTarball
|
|
32
|
+
exports.deleteFiles = deleteFiles
|
package/src/utilities/index.js
CHANGED
|
@@ -150,3 +150,14 @@ exports.doesDatabaseExist = async dbName => {
|
|
|
150
150
|
return false
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
exports.formatBytes = bytes => {
|
|
155
|
+
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
|
156
|
+
const byteIncrements = 1024
|
|
157
|
+
let unit = 0
|
|
158
|
+
let size = parseInt(bytes, 10) || 0
|
|
159
|
+
while (size >= byteIncrements && ++unit) {
|
|
160
|
+
size /= byteIncrements
|
|
161
|
+
}
|
|
162
|
+
return `${size.toFixed(size < 10 && unit > 0 ? 1 : 0)}${units[unit]}`
|
|
163
|
+
}
|