@budibase/backend-core 3.2.5 → 3.2.7

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.
Files changed (276) hide show
  1. package/dist/index.js +7 -1
  2. package/dist/index.js.map +2 -2
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +11 -4
  5. package/dist/plugins.js.meta.json +1 -1
  6. package/dist/src/environment.d.ts +1 -0
  7. package/dist/src/environment.js +6 -1
  8. package/dist/src/environment.js.map +1 -1
  9. package/package.json +11 -4
  10. package/src/accounts/accounts.ts +0 -82
  11. package/src/accounts/api.ts +0 -59
  12. package/src/accounts/index.ts +0 -1
  13. package/src/auth/auth.ts +0 -210
  14. package/src/auth/index.ts +0 -1
  15. package/src/auth/tests/auth.spec.ts +0 -14
  16. package/src/blacklist/blacklist.ts +0 -54
  17. package/src/blacklist/index.ts +0 -1
  18. package/src/blacklist/tests/blacklist.spec.ts +0 -46
  19. package/src/cache/appMetadata.ts +0 -88
  20. package/src/cache/base/index.ts +0 -150
  21. package/src/cache/docWritethrough.ts +0 -105
  22. package/src/cache/generic.ts +0 -33
  23. package/src/cache/index.ts +0 -8
  24. package/src/cache/invite.ts +0 -86
  25. package/src/cache/passwordReset.ts +0 -49
  26. package/src/cache/tests/docWritethrough.spec.ts +0 -296
  27. package/src/cache/tests/user.spec.ts +0 -145
  28. package/src/cache/tests/writethrough.spec.ts +0 -139
  29. package/src/cache/user.ts +0 -154
  30. package/src/cache/writethrough.ts +0 -133
  31. package/src/configs/configs.ts +0 -263
  32. package/src/configs/index.ts +0 -1
  33. package/src/configs/tests/configs.spec.ts +0 -184
  34. package/src/constants/db.ts +0 -75
  35. package/src/constants/index.ts +0 -2
  36. package/src/constants/misc.ts +0 -36
  37. package/src/context/Context.ts +0 -14
  38. package/src/context/identity.ts +0 -58
  39. package/src/context/index.ts +0 -3
  40. package/src/context/mainContext.ts +0 -422
  41. package/src/context/tests/index.spec.ts +0 -255
  42. package/src/context/types.ts +0 -26
  43. package/src/db/Replication.ts +0 -94
  44. package/src/db/couch/DatabaseImpl.ts +0 -511
  45. package/src/db/couch/connections.ts +0 -89
  46. package/src/db/couch/index.ts +0 -4
  47. package/src/db/couch/pouchDB.ts +0 -97
  48. package/src/db/couch/pouchDump.ts +0 -0
  49. package/src/db/couch/tests/DatabaseImpl.spec.ts +0 -118
  50. package/src/db/couch/utils.ts +0 -55
  51. package/src/db/db.ts +0 -34
  52. package/src/db/errors.ts +0 -14
  53. package/src/db/index.ts +0 -12
  54. package/src/db/instrumentation.ts +0 -199
  55. package/src/db/lucene.ts +0 -721
  56. package/src/db/searchIndexes/index.ts +0 -1
  57. package/src/db/searchIndexes/searchIndexes.ts +0 -62
  58. package/src/db/tests/DatabaseImpl.spec.ts +0 -55
  59. package/src/db/tests/connections.spec.ts +0 -22
  60. package/src/db/tests/index.spec.ts +0 -32
  61. package/src/db/tests/lucene.spec.ts +0 -400
  62. package/src/db/tests/pouch.spec.js +0 -62
  63. package/src/db/tests/utils.spec.ts +0 -63
  64. package/src/db/utils.ts +0 -208
  65. package/src/db/views.ts +0 -245
  66. package/src/docIds/conversions.ts +0 -60
  67. package/src/docIds/ids.ts +0 -126
  68. package/src/docIds/index.ts +0 -2
  69. package/src/docIds/newid.ts +0 -5
  70. package/src/docIds/params.ts +0 -189
  71. package/src/docUpdates/index.ts +0 -24
  72. package/src/environment.ts +0 -293
  73. package/src/errors/errors.ts +0 -119
  74. package/src/errors/index.ts +0 -1
  75. package/src/events/analytics.ts +0 -6
  76. package/src/events/asyncEvents/index.ts +0 -2
  77. package/src/events/asyncEvents/publisher.ts +0 -12
  78. package/src/events/asyncEvents/queue.ts +0 -22
  79. package/src/events/backfill.ts +0 -183
  80. package/src/events/documentId.ts +0 -56
  81. package/src/events/events.ts +0 -47
  82. package/src/events/identification.ts +0 -311
  83. package/src/events/index.ts +0 -15
  84. package/src/events/processors/AnalyticsProcessor.ts +0 -64
  85. package/src/events/processors/AuditLogsProcessor.ts +0 -92
  86. package/src/events/processors/LoggingProcessor.ts +0 -36
  87. package/src/events/processors/Processors.ts +0 -52
  88. package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -38
  89. package/src/events/processors/index.ts +0 -19
  90. package/src/events/processors/posthog/PosthogProcessor.ts +0 -118
  91. package/src/events/processors/posthog/index.ts +0 -3
  92. package/src/events/processors/posthog/rateLimiting.ts +0 -106
  93. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -164
  94. package/src/events/processors/types.ts +0 -1
  95. package/src/events/publishers/account.ts +0 -41
  96. package/src/events/publishers/ai.ts +0 -21
  97. package/src/events/publishers/app.ts +0 -168
  98. package/src/events/publishers/auditLog.ts +0 -26
  99. package/src/events/publishers/auth.ts +0 -73
  100. package/src/events/publishers/automation.ts +0 -110
  101. package/src/events/publishers/backfill.ts +0 -74
  102. package/src/events/publishers/backup.ts +0 -42
  103. package/src/events/publishers/datasource.ts +0 -48
  104. package/src/events/publishers/email.ts +0 -17
  105. package/src/events/publishers/environmentVariable.ts +0 -38
  106. package/src/events/publishers/group.ts +0 -99
  107. package/src/events/publishers/index.ts +0 -25
  108. package/src/events/publishers/installation.ts +0 -38
  109. package/src/events/publishers/layout.ts +0 -26
  110. package/src/events/publishers/license.ts +0 -84
  111. package/src/events/publishers/org.ts +0 -37
  112. package/src/events/publishers/plugin.ts +0 -47
  113. package/src/events/publishers/query.ts +0 -89
  114. package/src/events/publishers/role.ts +0 -62
  115. package/src/events/publishers/rows.ts +0 -29
  116. package/src/events/publishers/screen.ts +0 -36
  117. package/src/events/publishers/serve.ts +0 -43
  118. package/src/events/publishers/table.ts +0 -70
  119. package/src/events/publishers/user.ts +0 -202
  120. package/src/events/publishers/view.ts +0 -107
  121. package/src/features/features.ts +0 -277
  122. package/src/features/index.ts +0 -2
  123. package/src/features/tests/features.spec.ts +0 -267
  124. package/src/features/tests/utils.ts +0 -64
  125. package/src/helpers.ts +0 -9
  126. package/src/index.ts +0 -59
  127. package/src/installation.ts +0 -115
  128. package/src/logging/alerts.ts +0 -26
  129. package/src/logging/correlation/correlation.ts +0 -15
  130. package/src/logging/correlation/index.ts +0 -1
  131. package/src/logging/correlation/middleware.ts +0 -18
  132. package/src/logging/index.ts +0 -4
  133. package/src/logging/pino/logger.ts +0 -239
  134. package/src/logging/pino/middleware.ts +0 -48
  135. package/src/logging/system.ts +0 -81
  136. package/src/logging/tests/system.spec.ts +0 -61
  137. package/src/middleware/adminOnly.ts +0 -9
  138. package/src/middleware/auditLog.ts +0 -6
  139. package/src/middleware/authenticated.ts +0 -247
  140. package/src/middleware/builderOnly.ts +0 -21
  141. package/src/middleware/builderOrAdmin.ts +0 -21
  142. package/src/middleware/contentSecurityPolicy.ts +0 -113
  143. package/src/middleware/csrf.ts +0 -81
  144. package/src/middleware/errorHandling.ts +0 -43
  145. package/src/middleware/index.ts +0 -24
  146. package/src/middleware/internalApi.ts +0 -23
  147. package/src/middleware/ip.ts +0 -12
  148. package/src/middleware/joi-validator.ts +0 -58
  149. package/src/middleware/matchers.ts +0 -39
  150. package/src/middleware/passport/datasource/google.ts +0 -102
  151. package/src/middleware/passport/local.ts +0 -54
  152. package/src/middleware/passport/sso/google.ts +0 -77
  153. package/src/middleware/passport/sso/oidc.ts +0 -152
  154. package/src/middleware/passport/sso/sso.ts +0 -138
  155. package/src/middleware/passport/sso/tests/google.spec.ts +0 -68
  156. package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -144
  157. package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
  158. package/src/middleware/passport/utils.ts +0 -38
  159. package/src/middleware/querystringToBody.ts +0 -28
  160. package/src/middleware/tenancy.ts +0 -36
  161. package/src/middleware/tests/builder.spec.ts +0 -181
  162. package/src/middleware/tests/contentSecurityPolicy.spec.ts +0 -75
  163. package/src/middleware/tests/matchers.spec.ts +0 -100
  164. package/src/migrations/definitions.ts +0 -40
  165. package/src/migrations/index.ts +0 -2
  166. package/src/migrations/migrations.ts +0 -186
  167. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
  168. package/src/migrations/tests/migrations.spec.ts +0 -64
  169. package/src/objectStore/buckets/app.ts +0 -53
  170. package/src/objectStore/buckets/global.ts +0 -29
  171. package/src/objectStore/buckets/index.ts +0 -3
  172. package/src/objectStore/buckets/plugins.ts +0 -71
  173. package/src/objectStore/buckets/tests/app.spec.ts +0 -161
  174. package/src/objectStore/buckets/tests/global.spec.ts +0 -74
  175. package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
  176. package/src/objectStore/cloudfront.ts +0 -41
  177. package/src/objectStore/index.ts +0 -3
  178. package/src/objectStore/objectStore.ts +0 -585
  179. package/src/objectStore/utils.ts +0 -113
  180. package/src/platform/index.ts +0 -3
  181. package/src/platform/platformDb.ts +0 -6
  182. package/src/platform/tenants.ts +0 -101
  183. package/src/platform/tests/tenants.spec.ts +0 -26
  184. package/src/platform/users.ts +0 -129
  185. package/src/plugin/index.ts +0 -1
  186. package/src/plugin/tests/validation.spec.ts +0 -209
  187. package/src/plugin/utils.ts +0 -175
  188. package/src/queue/constants.ts +0 -8
  189. package/src/queue/inMemoryQueue.ts +0 -189
  190. package/src/queue/index.ts +0 -2
  191. package/src/queue/listeners.ts +0 -199
  192. package/src/queue/queue.ts +0 -84
  193. package/src/redis/index.ts +0 -6
  194. package/src/redis/init.ts +0 -118
  195. package/src/redis/redis.ts +0 -358
  196. package/src/redis/redlockImpl.ts +0 -155
  197. package/src/redis/tests/redis.spec.ts +0 -207
  198. package/src/redis/tests/redlockImpl.spec.ts +0 -105
  199. package/src/redis/utils.ts +0 -128
  200. package/src/security/auth.ts +0 -24
  201. package/src/security/encryption.ts +0 -185
  202. package/src/security/index.ts +0 -1
  203. package/src/security/permissions.ts +0 -166
  204. package/src/security/roles.ts +0 -655
  205. package/src/security/secrets.ts +0 -20
  206. package/src/security/sessions.ts +0 -123
  207. package/src/security/tests/auth.spec.ts +0 -45
  208. package/src/security/tests/encryption.spec.ts +0 -31
  209. package/src/security/tests/permissions.spec.ts +0 -146
  210. package/src/security/tests/secrets.spec.ts +0 -35
  211. package/src/security/tests/sessions.spec.ts +0 -12
  212. package/src/sql/designDoc.ts +0 -17
  213. package/src/sql/index.ts +0 -5
  214. package/src/sql/sql.ts +0 -1854
  215. package/src/sql/sqlTable.ts +0 -319
  216. package/src/sql/utils.ts +0 -193
  217. package/src/tenancy/db.ts +0 -6
  218. package/src/tenancy/index.ts +0 -2
  219. package/src/tenancy/tenancy.ts +0 -148
  220. package/src/tenancy/tests/tenancy.spec.ts +0 -184
  221. package/src/timers/index.ts +0 -1
  222. package/src/timers/timers.ts +0 -22
  223. package/src/users/db.ts +0 -582
  224. package/src/users/events.ts +0 -176
  225. package/src/users/index.ts +0 -4
  226. package/src/users/lookup.ts +0 -99
  227. package/src/users/test/db.spec.ts +0 -188
  228. package/src/users/test/utils.spec.ts +0 -67
  229. package/src/users/users.ts +0 -353
  230. package/src/users/utils.ts +0 -81
  231. package/src/utils/Duration.ts +0 -56
  232. package/src/utils/hashing.ts +0 -15
  233. package/src/utils/index.ts +0 -4
  234. package/src/utils/stringUtils.ts +0 -8
  235. package/src/utils/tests/Duration.spec.ts +0 -19
  236. package/src/utils/tests/utils.spec.ts +0 -204
  237. package/src/utils/utils.ts +0 -249
  238. package/tests/core/logging.ts +0 -34
  239. package/tests/core/users/users.spec.js +0 -53
  240. package/tests/core/utilities/index.ts +0 -7
  241. package/tests/core/utilities/jestUtils.ts +0 -33
  242. package/tests/core/utilities/mocks/alerts.ts +0 -4
  243. package/tests/core/utilities/mocks/date.ts +0 -3
  244. package/tests/core/utilities/mocks/events.ts +0 -132
  245. package/tests/core/utilities/mocks/index.ts +0 -9
  246. package/tests/core/utilities/mocks/licenses.ts +0 -119
  247. package/tests/core/utilities/queue.ts +0 -9
  248. package/tests/core/utilities/structures/Chance.ts +0 -20
  249. package/tests/core/utilities/structures/accounts.ts +0 -80
  250. package/tests/core/utilities/structures/apps.ts +0 -21
  251. package/tests/core/utilities/structures/common.ts +0 -7
  252. package/tests/core/utilities/structures/db.ts +0 -12
  253. package/tests/core/utilities/structures/documents/index.ts +0 -1
  254. package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
  255. package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
  256. package/tests/core/utilities/structures/generator.ts +0 -3
  257. package/tests/core/utilities/structures/index.ts +0 -15
  258. package/tests/core/utilities/structures/koa.ts +0 -16
  259. package/tests/core/utilities/structures/licenses.ts +0 -190
  260. package/tests/core/utilities/structures/plugins.ts +0 -19
  261. package/tests/core/utilities/structures/quotas.ts +0 -72
  262. package/tests/core/utilities/structures/scim.ts +0 -80
  263. package/tests/core/utilities/structures/sso.ts +0 -118
  264. package/tests/core/utilities/structures/tenants.ts +0 -5
  265. package/tests/core/utilities/structures/userGroups.ts +0 -10
  266. package/tests/core/utilities/structures/users.ts +0 -89
  267. package/tests/core/utilities/testContainerUtils.ts +0 -165
  268. package/tests/core/utilities/utils/index.ts +0 -2
  269. package/tests/core/utilities/utils/queue.ts +0 -27
  270. package/tests/core/utilities/utils/time.ts +0 -3
  271. package/tests/extra/DBTestConfiguration.ts +0 -36
  272. package/tests/extra/index.ts +0 -2
  273. package/tests/extra/testEnv.ts +0 -95
  274. package/tests/index.ts +0 -2
  275. package/tests/jestEnv.ts +0 -10
  276. package/tests/jestSetup.ts +0 -36
@@ -1,358 +0,0 @@
1
- import env from "../environment"
2
- import Redis, { Cluster } from "ioredis"
3
- // mock-redis doesn't have any typing
4
- let MockRedis: any | undefined
5
- if (env.MOCK_REDIS) {
6
- try {
7
- // ioredis mock is all in memory
8
- MockRedis = require("ioredis-mock")
9
- } catch (err) {
10
- console.log("Mock redis unavailable")
11
- }
12
- }
13
- import {
14
- addDbPrefix,
15
- removeDbPrefix,
16
- getRedisOptions,
17
- SEPARATOR,
18
- SelectableDatabase,
19
- getRedisConnectionDetails,
20
- } from "./utils"
21
- import { logAlert } from "../logging"
22
- import * as timers from "../timers"
23
-
24
- const RETRY_PERIOD_MS = 2000
25
- const STARTUP_TIMEOUT_MS = 5000
26
- const CLUSTERED = env.REDIS_CLUSTERED
27
- const DEFAULT_SELECT_DB = SelectableDatabase.DEFAULT
28
-
29
- // for testing just generate the client once
30
- let CLOSED = false
31
- const CLIENTS: Record<number, Redis> = {}
32
- let CONNECTED = false
33
-
34
- // mock redis always connected
35
- if (env.MOCK_REDIS) {
36
- CONNECTED = true
37
- }
38
-
39
- function pickClient(selectDb: number) {
40
- return CLIENTS[selectDb]
41
- }
42
-
43
- function connectionError(timeout: NodeJS.Timeout, err: Error | string) {
44
- // manually shut down, ignore errors
45
- if (CLOSED) {
46
- return
47
- }
48
- CLOSED = true
49
- // always clear this on error
50
- clearTimeout(timeout)
51
- CONNECTED = false
52
- logAlert("Redis connection failed", err)
53
- setTimeout(() => {
54
- init()
55
- }, RETRY_PERIOD_MS)
56
- }
57
-
58
- /**
59
- * Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise
60
- * will return the ioredis client which will be ready to use.
61
- */
62
- function init(selectDb = DEFAULT_SELECT_DB) {
63
- const RedisCore = env.MOCK_REDIS && MockRedis ? MockRedis : Redis
64
- let timeout: NodeJS.Timeout
65
- CLOSED = false
66
- let client = pickClient(selectDb)
67
- // already connected, ignore
68
- if (client && CONNECTED) {
69
- return
70
- }
71
- // testing uses a single in memory client
72
- if (env.MOCK_REDIS) {
73
- CLIENTS[selectDb] = new RedisCore(getRedisOptions())
74
- }
75
- // start the timer - only allowed 5 seconds to connect
76
- timeout = setTimeout(() => {
77
- if (!CONNECTED) {
78
- connectionError(timeout, "Did not successfully connect in timeout")
79
- }
80
- }, STARTUP_TIMEOUT_MS)
81
-
82
- // disconnect any lingering client
83
- if (client) {
84
- client.disconnect()
85
- }
86
- const { host, port } = getRedisConnectionDetails()
87
- const opts = getRedisOptions()
88
-
89
- if (CLUSTERED) {
90
- client = new RedisCore.Cluster([{ host, port }], opts)
91
- } else {
92
- client = new RedisCore(opts)
93
- }
94
- // attach handlers
95
- client.on("end", (err: Error) => {
96
- if (env.isTest()) {
97
- // don't try to re-connect in test env
98
- // allow the process to exit
99
- return
100
- }
101
- connectionError(timeout, err)
102
- })
103
- client.on("error", (err: Error) => {
104
- connectionError(timeout, err)
105
- })
106
- client.on("connect", () => {
107
- console.log(`Connected to Redis DB: ${selectDb}`)
108
- clearTimeout(timeout)
109
- CONNECTED = true
110
- })
111
- CLIENTS[selectDb] = client
112
- }
113
-
114
- export function closeAll() {
115
- Object.values(CLIENTS).forEach(client => client.disconnect())
116
- }
117
-
118
- function waitForConnection(selectDb: number = DEFAULT_SELECT_DB) {
119
- return new Promise(resolve => {
120
- if (pickClient(selectDb) == null) {
121
- init()
122
- } else if (CONNECTED) {
123
- resolve("")
124
- return
125
- }
126
- // check if the connection is ready
127
- const interval = timers.set(() => {
128
- if (CONNECTED) {
129
- timers.clear(interval)
130
- resolve("")
131
- }
132
- }, 500)
133
- })
134
- }
135
-
136
- /**
137
- * Utility function, takes a redis stream and converts it to a promisified response -
138
- * this can only be done with redis streams because they will have an end.
139
- * @param stream A redis stream, specifically as this type of stream will have an end.
140
- * @param client The client to use for further lookups.
141
- * @return The final output of the stream
142
- */
143
- function promisifyStream(stream: any, client: RedisWrapper) {
144
- return new Promise((resolve, reject) => {
145
- const outputKeys = new Set()
146
- stream.on("data", (keys: string[]) => {
147
- keys.forEach(key => {
148
- outputKeys.add(key)
149
- })
150
- })
151
- stream.on("error", (err: Error) => {
152
- reject(err)
153
- })
154
- stream.on("end", async () => {
155
- const keysArray: string[] = Array.from(outputKeys) as string[]
156
- try {
157
- let getPromises = []
158
- for (let key of keysArray) {
159
- getPromises.push(client.get(key))
160
- }
161
- const jsonArray = await Promise.all(getPromises)
162
- resolve(
163
- keysArray.map(key => ({
164
- key: removeDbPrefix(key),
165
- value: JSON.parse(jsonArray.shift()),
166
- }))
167
- )
168
- } catch (err) {
169
- reject(err)
170
- }
171
- })
172
- })
173
- }
174
-
175
- class RedisWrapper {
176
- _db: string
177
- _select: number
178
-
179
- constructor(db: string, selectDb: number | null = null) {
180
- this._db = db
181
- this._select = selectDb || DEFAULT_SELECT_DB
182
- }
183
-
184
- getClient() {
185
- return pickClient(this._select)
186
- }
187
-
188
- async init() {
189
- CLOSED = false
190
- init(this._select)
191
- await waitForConnection(this._select)
192
- if (this._select && !env.isTest()) {
193
- this.getClient().select(this._select)
194
- }
195
- return this
196
- }
197
-
198
- async finish() {
199
- CLOSED = true
200
- this.getClient().disconnect()
201
- }
202
-
203
- async scan(key = ""): Promise<any> {
204
- const db = this._db
205
- key = `${db}${SEPARATOR}${key}`
206
- let stream
207
- if (CLUSTERED) {
208
- let node = (this.getClient() as never as Cluster).nodes("master")
209
- stream = node[0].scanStream({ match: key + "*", count: 100 })
210
- } else {
211
- stream = (this.getClient() as Redis).scanStream({
212
- match: key + "*",
213
- count: 100,
214
- })
215
- }
216
- return promisifyStream(stream, this.getClient() as any)
217
- }
218
-
219
- async keys(pattern: string) {
220
- const db = this._db
221
- return this.getClient().keys(addDbPrefix(db, pattern))
222
- }
223
-
224
- async exists(key: string) {
225
- const db = this._db
226
- return await this.getClient().exists(addDbPrefix(db, key))
227
- }
228
-
229
- async get(key: string) {
230
- const db = this._db
231
- const response = await this.getClient().get(addDbPrefix(db, key))
232
- // overwrite the prefixed key
233
- // @ts-ignore
234
- if (response != null && response.key) {
235
- // @ts-ignore
236
- response.key = key
237
- }
238
- // if its not an object just return the response
239
- try {
240
- return JSON.parse(response!)
241
- } catch (err) {
242
- return response
243
- }
244
- }
245
-
246
- async bulkGet<T>(keys: string[]) {
247
- const db = this._db
248
- if (keys.length === 0) {
249
- return {}
250
- }
251
- const prefixedKeys = keys.map(key => addDbPrefix(db, key))
252
- let response = await this.getClient().mget(prefixedKeys)
253
- if (Array.isArray(response)) {
254
- let final: Record<string, T> = {}
255
- let count = 0
256
- for (let result of response) {
257
- if (result) {
258
- let parsed
259
- try {
260
- parsed = JSON.parse(result)
261
- } catch (err) {
262
- parsed = result
263
- }
264
- final[keys[count]] = parsed
265
- }
266
- count++
267
- }
268
- return final
269
- } else {
270
- throw new Error(`Invalid response: ${response}`)
271
- }
272
- }
273
-
274
- async store(key: string, value: any, expirySeconds: number | null = null) {
275
- const db = this._db
276
- if (typeof value === "object") {
277
- value = JSON.stringify(value)
278
- }
279
- const prefixedKey = addDbPrefix(db, key)
280
- await this.getClient().set(prefixedKey, value)
281
- if (expirySeconds) {
282
- await this.getClient().expire(prefixedKey, expirySeconds)
283
- }
284
- }
285
-
286
- async bulkStore(
287
- data: Record<string, any>,
288
- expirySeconds: number | null = null
289
- ) {
290
- const client = this.getClient()
291
-
292
- const dataToStore = Object.entries(data).reduce((acc, [key, value]) => {
293
- acc[addDbPrefix(this._db, key)] =
294
- typeof value === "object" ? JSON.stringify(value) : value
295
- return acc
296
- }, {} as Record<string, any>)
297
-
298
- const pipeline = client.pipeline()
299
- pipeline.mset(dataToStore)
300
-
301
- if (expirySeconds !== null) {
302
- for (const key of Object.keys(dataToStore)) {
303
- pipeline.expire(key, expirySeconds)
304
- }
305
- }
306
-
307
- await pipeline.exec()
308
- }
309
-
310
- async getTTL(key: string) {
311
- const db = this._db
312
- const prefixedKey = addDbPrefix(db, key)
313
- return this.getClient().ttl(prefixedKey)
314
- }
315
-
316
- async setExpiry(key: string, expirySeconds: number) {
317
- const db = this._db
318
- const prefixedKey = addDbPrefix(db, key)
319
- await this.getClient().expire(prefixedKey, expirySeconds)
320
- }
321
-
322
- async delete(key: string) {
323
- const db = this._db
324
- await this.getClient().del(addDbPrefix(db, key))
325
- }
326
-
327
- async bulkDelete(keys: string[]) {
328
- const db = this._db
329
- await this.getClient().del(keys.map(key => addDbPrefix(db, key)))
330
- }
331
-
332
- async clear() {
333
- let items = await this.scan()
334
- await Promise.all(items.map((obj: any) => this.delete(obj.key)))
335
- }
336
-
337
- async increment(key: string) {
338
- const result = await this.getClient().incr(addDbPrefix(this._db, key))
339
- if (isNaN(result)) {
340
- throw new Error(`Redis ${key} does not contain a number`)
341
- }
342
- return result
343
- }
344
-
345
- async deleteIfValue(key: string, value: any) {
346
- const client = this.getClient()
347
-
348
- const luaScript = `
349
- if redis.call('GET', KEYS[1]) == ARGV[1] then
350
- redis.call('DEL', KEYS[1])
351
- end
352
- `
353
-
354
- await client.eval(luaScript, 1, addDbPrefix(this._db, key), value)
355
- }
356
- }
357
-
358
- export default RedisWrapper
@@ -1,155 +0,0 @@
1
- import Redlock from "redlock"
2
- import { getLockClient } from "./init"
3
- import { LockOptions, LockType } from "@budibase/types"
4
- import * as context from "../context"
5
- import { utils } from "@budibase/shared-core"
6
- import { Duration } from "../utils"
7
-
8
- async function getClient(
9
- type: LockType,
10
- opts?: Redlock.Options
11
- ): Promise<Redlock> {
12
- if (type === LockType.CUSTOM) {
13
- return newRedlock(opts)
14
- }
15
-
16
- switch (type) {
17
- case LockType.TRY_ONCE: {
18
- return newRedlock(OPTIONS.TRY_ONCE)
19
- }
20
- case LockType.TRY_TWICE: {
21
- return newRedlock(OPTIONS.TRY_TWICE)
22
- }
23
- case LockType.DEFAULT: {
24
- return newRedlock(OPTIONS.DEFAULT)
25
- }
26
- case LockType.DELAY_500: {
27
- return newRedlock(OPTIONS.DELAY_500)
28
- }
29
- case LockType.AUTO_EXTEND: {
30
- return newRedlock(OPTIONS.AUTO_EXTEND)
31
- }
32
- default: {
33
- throw utils.unreachable(type)
34
- }
35
- }
36
- }
37
-
38
- const OPTIONS: Record<keyof typeof LockType, Redlock.Options> = {
39
- TRY_ONCE: {
40
- // immediately throws an error if the lock is already held
41
- retryCount: 0,
42
- },
43
- TRY_TWICE: {
44
- retryCount: 1,
45
- },
46
- DEFAULT: {
47
- // the expected clock drift; for more details
48
- // see http://redis.io/topics/distlock
49
- driftFactor: 0.01, // multiplied by lock ttl to determine drift time
50
-
51
- // the max number of times Redlock will attempt
52
- // to lock a resource before erroring
53
- retryCount: 10,
54
-
55
- // the time in ms between attempts
56
- retryDelay: 200, // time in ms
57
-
58
- // the max time in ms randomly added to retries
59
- // to improve performance under high contention
60
- // see https://www.awsarchitectureblog.com/2015/03/backoff.html
61
- retryJitter: 100, // time in ms
62
- },
63
- DELAY_500: {
64
- retryDelay: 500,
65
- },
66
- CUSTOM: {},
67
- AUTO_EXTEND: {
68
- retryCount: -1,
69
- },
70
- }
71
-
72
- export async function newRedlock(opts: Redlock.Options = {}) {
73
- const options = { ...OPTIONS.DEFAULT, ...opts }
74
- const redisWrapper = await getLockClient()
75
- const client = redisWrapper.getClient() as any
76
- return new Redlock([client], options)
77
- }
78
-
79
- type SuccessfulRedlockExecution<T> = {
80
- executed: true
81
- result: T
82
- }
83
- type UnsuccessfulRedlockExecution = {
84
- executed: false
85
- }
86
-
87
- type RedlockExecution<T> =
88
- | SuccessfulRedlockExecution<T>
89
- | UnsuccessfulRedlockExecution
90
-
91
- function getLockName(opts: LockOptions) {
92
- // determine lock name
93
- // by default use the tenantId for uniqueness, unless using a system lock
94
- const prefix = opts.systemLock ? "system" : context.getTenantId()
95
- let name: string = `lock:${prefix}_${opts.name}`
96
- // add additional unique name if required
97
- if (opts.resource) {
98
- name = name + `_${opts.resource}`
99
- }
100
- return name
101
- }
102
-
103
- export const AUTO_EXTEND_POLLING_MS = Duration.fromSeconds(10).toMs()
104
-
105
- export async function doWithLock<T>(
106
- opts: LockOptions,
107
- task: () => Promise<T>
108
- ): Promise<RedlockExecution<T>> {
109
- const redlock = await getClient(opts.type, opts.customOptions)
110
- let lock: Redlock.Lock | undefined
111
- let timeout
112
- try {
113
- const name = getLockName(opts)
114
-
115
- const ttl =
116
- opts.type === LockType.AUTO_EXTEND ? AUTO_EXTEND_POLLING_MS : opts.ttl
117
-
118
- // create the lock
119
- lock = await redlock.lock(name, ttl)
120
-
121
- if (opts.type === LockType.AUTO_EXTEND) {
122
- // We keep extending the lock while the task is running
123
- const extendInIntervals = (): void => {
124
- timeout = setTimeout(async () => {
125
- lock = await lock!.extend(ttl, () => opts.onExtend && opts.onExtend())
126
-
127
- extendInIntervals()
128
- }, ttl / 2)
129
- }
130
-
131
- extendInIntervals()
132
- }
133
-
134
- // perform locked task
135
- // need to await to ensure completion before unlocking
136
- const result = await task()
137
- return { executed: true, result }
138
- } catch (e: any) {
139
- // lock limit exceeded
140
- if (e.name === "LockError") {
141
- if (opts.type === LockType.TRY_ONCE) {
142
- // don't throw for try-once locks, they will always error
143
- // due to retry count (0) exceeded
144
- return { executed: false }
145
- } else {
146
- throw e
147
- }
148
- } else {
149
- throw e
150
- }
151
- } finally {
152
- clearTimeout(timeout)
153
- await lock?.unlock()
154
- }
155
- }
@@ -1,207 +0,0 @@
1
- import { GenericContainer, StartedTestContainer } from "testcontainers"
2
- import { generator, structures } from "../../../tests"
3
- import RedisWrapper, { closeAll } from "../redis"
4
- import env from "../../environment"
5
- import { randomUUID } from "crypto"
6
-
7
- jest.setTimeout(30000)
8
-
9
- describe("redis", () => {
10
- let redis: RedisWrapper
11
- let container: StartedTestContainer
12
-
13
- beforeAll(async () => {
14
- const container = await new GenericContainer("redis")
15
- .withExposedPorts(6379)
16
- .start()
17
-
18
- env._set(
19
- "REDIS_URL",
20
- `${container.getHost()}:${container.getMappedPort(6379)}`
21
- )
22
- env._set("MOCK_REDIS", 0)
23
- env._set("REDIS_PASSWORD", 0)
24
- })
25
-
26
- afterAll(() => {
27
- container?.stop()
28
- closeAll()
29
- })
30
-
31
- beforeEach(async () => {
32
- redis = new RedisWrapper(structures.db.id())
33
- await redis.init()
34
- })
35
-
36
- describe("store", () => {
37
- it("a basic value can be persisted", async () => {
38
- const key = structures.uuid()
39
- const value = generator.word()
40
-
41
- await redis.store(key, value)
42
-
43
- expect(await redis.get(key)).toEqual(value)
44
- })
45
-
46
- it("objects can be persisted", async () => {
47
- const key = structures.uuid()
48
- const value = { [generator.word()]: generator.word() }
49
-
50
- await redis.store(key, value)
51
-
52
- expect(await redis.get(key)).toEqual(value)
53
- })
54
- })
55
-
56
- describe("bulkStore", () => {
57
- function createRandomObject(
58
- keyLength: number,
59
- valueGenerator: () => any = () => randomUUID()
60
- ) {
61
- return generator
62
- .unique(() => randomUUID(), keyLength)
63
- .reduce((acc, key) => {
64
- acc[key] = valueGenerator()
65
- return acc
66
- }, {} as Record<string, string>)
67
- }
68
-
69
- it("a basic object can be persisted", async () => {
70
- const data = createRandomObject(10)
71
-
72
- await redis.bulkStore(data)
73
-
74
- for (const [key, value] of Object.entries(data)) {
75
- expect(await redis.get(key)).toEqual(value)
76
- }
77
-
78
- expect(await redis.keys("*")).toHaveLength(10)
79
- })
80
-
81
- it("a complex object can be persisted", async () => {
82
- const data = {
83
- ...createRandomObject(10, () => createRandomObject(5)),
84
- ...createRandomObject(5),
85
- }
86
-
87
- await redis.bulkStore(data)
88
-
89
- for (const [key, value] of Object.entries(data)) {
90
- expect(await redis.get(key)).toEqual(value)
91
- }
92
-
93
- expect(await redis.keys("*")).toHaveLength(15)
94
- })
95
-
96
- it("no TTL is set by default", async () => {
97
- const data = createRandomObject(10)
98
-
99
- await redis.bulkStore(data)
100
-
101
- for (const [key, value] of Object.entries(data)) {
102
- expect(await redis.get(key)).toEqual(value)
103
- expect(await redis.getTTL(key)).toEqual(-1)
104
- }
105
- })
106
-
107
- it("a bulk store can be persisted with TTL", async () => {
108
- const ttl = 500
109
- const data = createRandomObject(8)
110
-
111
- await redis.bulkStore(data, ttl)
112
-
113
- for (const [key, value] of Object.entries(data)) {
114
- expect(await redis.get(key)).toEqual(value)
115
- expect(await redis.getTTL(key)).toEqual(ttl)
116
- }
117
-
118
- expect(await redis.keys("*")).toHaveLength(8)
119
- })
120
-
121
- it("setting a TTL of -1 will not persist the key", async () => {
122
- const ttl = -1
123
- const data = createRandomObject(5)
124
-
125
- await redis.bulkStore(data, ttl)
126
-
127
- for (const key of Object.keys(data)) {
128
- expect(await redis.get(key)).toBe(null)
129
- }
130
-
131
- expect(await redis.keys("*")).toHaveLength(0)
132
- })
133
- })
134
-
135
- describe("increment", () => {
136
- it("can increment on a new key", async () => {
137
- const key = structures.uuid()
138
- const result = await redis.increment(key)
139
- expect(result).toBe(1)
140
- })
141
-
142
- it("can increment multiple times", async () => {
143
- const key = structures.uuid()
144
- const results = [
145
- await redis.increment(key),
146
- await redis.increment(key),
147
- await redis.increment(key),
148
- await redis.increment(key),
149
- await redis.increment(key),
150
- ]
151
- expect(results).toEqual([1, 2, 3, 4, 5])
152
- })
153
-
154
- it("can increment multiple times in parallel", async () => {
155
- const key = structures.uuid()
156
- const results = await Promise.all(
157
- Array.from({ length: 100 }).map(() => redis.increment(key))
158
- )
159
- expect(results).toHaveLength(100)
160
- expect(results).toEqual(Array.from({ length: 100 }).map((_, i) => i + 1))
161
- })
162
-
163
- it("can increment existing set keys", async () => {
164
- const key = structures.uuid()
165
- await redis.store(key, 70)
166
- await redis.increment(key)
167
-
168
- const result = await redis.increment(key)
169
- expect(result).toBe(72)
170
- })
171
-
172
- it.each([
173
- generator.word(),
174
- generator.bool(),
175
- { [generator.word()]: generator.word() },
176
- ])("cannot increment if the store value is not a number", async value => {
177
- const key = structures.uuid()
178
- await redis.store(key, value)
179
-
180
- await expect(redis.increment(key)).rejects.toThrow(
181
- "ERR value is not an integer or out of range"
182
- )
183
- })
184
- })
185
-
186
- describe("deleteIfValue", () => {
187
- it("can delete if the value matches", async () => {
188
- const key = structures.uuid()
189
- const value = generator.word()
190
- await redis.store(key, value)
191
-
192
- await redis.deleteIfValue(key, value)
193
-
194
- expect(await redis.get(key)).toBeNull()
195
- })
196
-
197
- it("will not delete if the value does not matches", async () => {
198
- const key = structures.uuid()
199
- const value = generator.word()
200
- await redis.store(key, value)
201
-
202
- await redis.deleteIfValue(key, generator.word())
203
-
204
- expect(await redis.get(key)).toEqual(value)
205
- })
206
- })
207
- })