@budibase/backend-core 2.21.3 → 2.21.5

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 (91) hide show
  1. package/dist/index.js +301 -68
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +6 -5
  5. package/dist/plugins.js.meta.json +1 -1
  6. package/dist/src/cache/base/index.d.ts +33 -3
  7. package/dist/src/cache/base/index.js +60 -1
  8. package/dist/src/cache/base/index.js.map +1 -1
  9. package/dist/src/cache/docWritethrough.d.ts +21 -0
  10. package/dist/src/cache/docWritethrough.js +107 -0
  11. package/dist/src/cache/docWritethrough.js.map +1 -0
  12. package/dist/src/cache/generic.d.ts +3 -3
  13. package/dist/src/cache/generic.js.map +1 -1
  14. package/dist/src/cache/index.d.ts +1 -0
  15. package/dist/src/cache/index.js +2 -1
  16. package/dist/src/cache/index.js.map +1 -1
  17. package/dist/src/cache/user.js.map +1 -1
  18. package/dist/src/configs/configs.d.ts +1 -1
  19. package/dist/src/constants/db.d.ts +3 -0
  20. package/dist/src/constants/db.js +3 -0
  21. package/dist/src/constants/db.js.map +1 -1
  22. package/dist/src/context/mainContext.d.ts +1 -0
  23. package/dist/src/context/mainContext.js +13 -1
  24. package/dist/src/context/mainContext.js.map +1 -1
  25. package/dist/src/db/Replication.d.ts +13 -25
  26. package/dist/src/db/Replication.js +18 -33
  27. package/dist/src/db/Replication.js.map +1 -1
  28. package/dist/src/db/couch/DatabaseImpl.d.ts +3 -1
  29. package/dist/src/db/couch/DatabaseImpl.js +18 -1
  30. package/dist/src/db/couch/DatabaseImpl.js.map +1 -1
  31. package/dist/src/db/instrumentation.d.ts +1 -1
  32. package/dist/src/db/instrumentation.js +5 -2
  33. package/dist/src/db/instrumentation.js.map +1 -1
  34. package/dist/src/environment.d.ts +1 -0
  35. package/dist/src/environment.js +1 -1
  36. package/dist/src/environment.js.map +1 -1
  37. package/dist/src/events/analytics.d.ts +1 -1
  38. package/dist/src/index.d.ts +1 -0
  39. package/dist/src/queue/constants.d.ts +2 -1
  40. package/dist/src/queue/constants.js +1 -0
  41. package/dist/src/queue/constants.js.map +1 -1
  42. package/dist/src/queue/inMemoryQueue.d.ts +23 -13
  43. package/dist/src/queue/inMemoryQueue.js +83 -30
  44. package/dist/src/queue/inMemoryQueue.js.map +1 -1
  45. package/dist/src/queue/listeners.js +2 -0
  46. package/dist/src/queue/listeners.js.map +1 -1
  47. package/dist/src/queue/queue.d.ts +1 -0
  48. package/dist/src/queue/queue.js.map +1 -1
  49. package/dist/src/redis/init.d.ts +1 -0
  50. package/dist/src/redis/init.js +12 -2
  51. package/dist/src/redis/init.js.map +1 -1
  52. package/dist/src/redis/redis.d.ts +10 -5
  53. package/dist/src/redis/redis.js +52 -3
  54. package/dist/src/redis/redis.js.map +1 -1
  55. package/dist/src/redis/redlockImpl.js.map +1 -1
  56. package/dist/src/redis/utils.d.ts +2 -1
  57. package/dist/src/redis/utils.js +1 -0
  58. package/dist/src/redis/utils.js.map +1 -1
  59. package/dist/src/security/roles.d.ts +1 -1
  60. package/dist/src/security/roles.js +0 -3
  61. package/dist/src/security/roles.js.map +1 -1
  62. package/dist/tests/core/utilities/structures/accounts.js +1 -1
  63. package/dist/tests/core/utilities/structures/accounts.js.map +1 -1
  64. package/dist/tests/core/utilities/structures/scim.js +1 -1
  65. package/dist/tests/core/utilities/structures/scim.js.map +1 -1
  66. package/package.json +6 -5
  67. package/src/cache/base/index.ts +62 -4
  68. package/src/cache/docWritethrough.ts +97 -0
  69. package/src/cache/generic.ts +3 -2
  70. package/src/cache/index.ts +1 -0
  71. package/src/cache/tests/docWritethrough.spec.ts +293 -0
  72. package/src/cache/user.ts +2 -2
  73. package/src/constants/db.ts +3 -0
  74. package/src/context/mainContext.ts +11 -0
  75. package/src/db/Replication.ts +27 -40
  76. package/src/db/couch/DatabaseImpl.ts +18 -1
  77. package/src/db/instrumentation.ts +5 -2
  78. package/src/db/tests/DatabaseImpl.spec.ts +55 -0
  79. package/src/environment.ts +1 -0
  80. package/src/queue/constants.ts +1 -0
  81. package/src/queue/inMemoryQueue.ts +79 -24
  82. package/src/queue/listeners.ts +2 -0
  83. package/src/queue/queue.ts +2 -0
  84. package/src/redis/init.ts +12 -1
  85. package/src/redis/redis.ts +63 -9
  86. package/src/redis/redlockImpl.ts +1 -1
  87. package/src/redis/tests/redis.spec.ts +214 -0
  88. package/src/redis/utils.ts +1 -0
  89. package/src/security/roles.ts +1 -4
  90. package/tests/core/utilities/structures/accounts.ts +1 -1
  91. package/tests/core/utilities/structures/scim.ts +1 -1
@@ -0,0 +1,293 @@
1
+ import tk from "timekeeper"
2
+
3
+ import _ from "lodash"
4
+ import { DBTestConfiguration, generator, structures } from "../../../tests"
5
+ import { getDB } from "../../db"
6
+
7
+ import {
8
+ DocWritethrough,
9
+ docWritethroughProcessorQueue,
10
+ init,
11
+ } from "../docWritethrough"
12
+
13
+ import InMemoryQueue from "../../queue/inMemoryQueue"
14
+
15
+ const initialTime = Date.now()
16
+
17
+ async function waitForQueueCompletion() {
18
+ const queue: InMemoryQueue = docWritethroughProcessorQueue as never
19
+ await queue.waitForCompletion()
20
+ }
21
+
22
+ describe("docWritethrough", () => {
23
+ beforeAll(() => {
24
+ init()
25
+ })
26
+
27
+ const config = new DBTestConfiguration()
28
+
29
+ const db = getDB(structures.db.id())
30
+ let documentId: string
31
+ let docWritethrough: DocWritethrough
32
+
33
+ describe("patch", () => {
34
+ function generatePatchObject(fieldCount: number) {
35
+ const keys = generator.unique(() => generator.word(), fieldCount)
36
+ return keys.reduce((acc, c) => {
37
+ acc[c] = generator.word()
38
+ return acc
39
+ }, {} as Record<string, any>)
40
+ }
41
+
42
+ beforeEach(async () => {
43
+ jest.clearAllMocks()
44
+ documentId = structures.uuid()
45
+ docWritethrough = new DocWritethrough(db, documentId)
46
+ })
47
+
48
+ it("patching will not persist until the messages are persisted", async () => {
49
+ await config.doInTenant(async () => {
50
+ await docWritethrough.patch(generatePatchObject(2))
51
+ await docWritethrough.patch(generatePatchObject(2))
52
+
53
+ expect(await db.exists(documentId)).toBe(false)
54
+ })
55
+ })
56
+
57
+ it("patching will persist when the messages are persisted", async () => {
58
+ await config.doInTenant(async () => {
59
+ const patch1 = generatePatchObject(2)
60
+ const patch2 = generatePatchObject(2)
61
+ await docWritethrough.patch(patch1)
62
+ await docWritethrough.patch(patch2)
63
+
64
+ await waitForQueueCompletion()
65
+
66
+ // This will not be persisted
67
+ const patch3 = generatePatchObject(3)
68
+ await docWritethrough.patch(patch3)
69
+
70
+ expect(await db.get(documentId)).toEqual({
71
+ _id: documentId,
72
+ ...patch1,
73
+ ...patch2,
74
+ _rev: expect.stringMatching(/2-.+/),
75
+ createdAt: new Date(initialTime).toISOString(),
76
+ updatedAt: new Date(initialTime).toISOString(),
77
+ })
78
+ })
79
+ })
80
+
81
+ it("patching will persist keeping the previous data", async () => {
82
+ await config.doInTenant(async () => {
83
+ const patch1 = generatePatchObject(2)
84
+ const patch2 = generatePatchObject(2)
85
+ await docWritethrough.patch(patch1)
86
+ await docWritethrough.patch(patch2)
87
+
88
+ await waitForQueueCompletion()
89
+
90
+ const patch3 = generatePatchObject(3)
91
+ await docWritethrough.patch(patch3)
92
+
93
+ await waitForQueueCompletion()
94
+
95
+ expect(await db.get(documentId)).toEqual(
96
+ expect.objectContaining({
97
+ _id: documentId,
98
+ ...patch1,
99
+ ...patch2,
100
+ ...patch3,
101
+ })
102
+ )
103
+ })
104
+ })
105
+
106
+ it("date audit fields are set correctly when persisting", async () => {
107
+ await config.doInTenant(async () => {
108
+ const patch1 = generatePatchObject(2)
109
+ const patch2 = generatePatchObject(2)
110
+ await docWritethrough.patch(patch1)
111
+ const date1 = new Date()
112
+ await waitForQueueCompletion()
113
+ await docWritethrough.patch(patch2)
114
+
115
+ tk.travel(Date.now() + 100)
116
+ const date2 = new Date()
117
+ await waitForQueueCompletion()
118
+
119
+ expect(date1).not.toEqual(date2)
120
+ expect(await db.get(documentId)).toEqual(
121
+ expect.objectContaining({
122
+ createdAt: date1.toISOString(),
123
+ updatedAt: date2.toISOString(),
124
+ })
125
+ )
126
+ })
127
+ })
128
+
129
+ it("concurrent patches will override keys", async () => {
130
+ await config.doInTenant(async () => {
131
+ const patch1 = generatePatchObject(2)
132
+ await docWritethrough.patch(patch1)
133
+ await waitForQueueCompletion()
134
+ const patch2 = generatePatchObject(1)
135
+ await docWritethrough.patch(patch2)
136
+
137
+ const keyToOverride = _.sample(Object.keys(patch1))!
138
+ expect(await db.get(documentId)).toEqual(
139
+ expect.objectContaining({
140
+ [keyToOverride]: patch1[keyToOverride],
141
+ })
142
+ )
143
+
144
+ await waitForQueueCompletion()
145
+
146
+ const patch3 = {
147
+ ...generatePatchObject(3),
148
+ [keyToOverride]: generator.word(),
149
+ }
150
+ await docWritethrough.patch(patch3)
151
+ await waitForQueueCompletion()
152
+
153
+ expect(await db.get(documentId)).toEqual(
154
+ expect.objectContaining({
155
+ ...patch1,
156
+ ...patch2,
157
+ ...patch3,
158
+ })
159
+ )
160
+ })
161
+ })
162
+
163
+ it("concurrent patches to different docWritethrough will not pollute each other", async () => {
164
+ await config.doInTenant(async () => {
165
+ const secondDocWritethrough = new DocWritethrough(
166
+ db,
167
+ structures.db.id()
168
+ )
169
+
170
+ const doc1Patch = generatePatchObject(2)
171
+ await docWritethrough.patch(doc1Patch)
172
+ const doc2Patch = generatePatchObject(1)
173
+ await secondDocWritethrough.patch(doc2Patch)
174
+
175
+ await waitForQueueCompletion()
176
+
177
+ const doc1Patch2 = generatePatchObject(3)
178
+ await docWritethrough.patch(doc1Patch2)
179
+ const doc2Patch2 = generatePatchObject(3)
180
+ await secondDocWritethrough.patch(doc2Patch2)
181
+ await waitForQueueCompletion()
182
+
183
+ expect(await db.get(docWritethrough.docId)).toEqual(
184
+ expect.objectContaining({
185
+ ...doc1Patch,
186
+ ...doc1Patch2,
187
+ })
188
+ )
189
+
190
+ expect(await db.get(secondDocWritethrough.docId)).toEqual(
191
+ expect.objectContaining({
192
+ ...doc2Patch,
193
+ ...doc2Patch2,
194
+ })
195
+ )
196
+ })
197
+ })
198
+
199
+ it("cached values are persisted only once", async () => {
200
+ await config.doInTenant(async () => {
201
+ const initialPatch = generatePatchObject(5)
202
+
203
+ await docWritethrough.patch(initialPatch)
204
+ await waitForQueueCompletion()
205
+
206
+ expect(await db.get(documentId)).toEqual(
207
+ expect.objectContaining(initialPatch)
208
+ )
209
+
210
+ await db.remove(await db.get(documentId))
211
+
212
+ await waitForQueueCompletion()
213
+ const extraPatch = generatePatchObject(5)
214
+ await docWritethrough.patch(extraPatch)
215
+ await waitForQueueCompletion()
216
+
217
+ expect(await db.get(documentId)).toEqual(
218
+ expect.objectContaining(extraPatch)
219
+ )
220
+ expect(await db.get(documentId)).not.toEqual(
221
+ expect.objectContaining(initialPatch)
222
+ )
223
+ })
224
+ })
225
+
226
+ it("concurrent calls will not cause conflicts", async () => {
227
+ async function parallelPatch(count: number) {
228
+ const patches = Array.from({ length: count }).map(() =>
229
+ generatePatchObject(1)
230
+ )
231
+ await Promise.all(patches.map(p => docWritethrough.patch(p)))
232
+
233
+ return patches.reduce((acc, c) => {
234
+ acc = { ...acc, ...c }
235
+ return acc
236
+ }, {})
237
+ }
238
+ const queueMessageSpy = jest.spyOn(docWritethroughProcessorQueue, "add")
239
+
240
+ await config.doInTenant(async () => {
241
+ let patches = await parallelPatch(5)
242
+ expect(queueMessageSpy).toBeCalledTimes(5)
243
+
244
+ await waitForQueueCompletion()
245
+ expect(await db.get(documentId)).toEqual(
246
+ expect.objectContaining(patches)
247
+ )
248
+
249
+ patches = { ...patches, ...(await parallelPatch(40)) }
250
+ expect(queueMessageSpy).toBeCalledTimes(45)
251
+
252
+ await waitForQueueCompletion()
253
+ expect(await db.get(documentId)).toEqual(
254
+ expect.objectContaining(patches)
255
+ )
256
+
257
+ patches = { ...patches, ...(await parallelPatch(10)) }
258
+ expect(queueMessageSpy).toBeCalledTimes(55)
259
+
260
+ await waitForQueueCompletion()
261
+ expect(await db.get(documentId)).toEqual(
262
+ expect.objectContaining(patches)
263
+ )
264
+ })
265
+ })
266
+
267
+ // This is not yet supported
268
+ it.skip("patches will execute in order", async () => {
269
+ let incrementalValue = 0
270
+ const keyToOverride = generator.word()
271
+ async function incrementalPatches(count: number) {
272
+ for (let i = 0; i < count; i++) {
273
+ await docWritethrough.patch({ [keyToOverride]: incrementalValue++ })
274
+ }
275
+ }
276
+
277
+ await config.doInTenant(async () => {
278
+ await incrementalPatches(5)
279
+
280
+ await waitForQueueCompletion()
281
+ expect(await db.get(documentId)).toEqual(
282
+ expect.objectContaining({ [keyToOverride]: 5 })
283
+ )
284
+
285
+ await incrementalPatches(40)
286
+ await waitForQueueCompletion()
287
+ expect(await db.get(documentId)).toEqual(
288
+ expect.objectContaining({ [keyToOverride]: 45 })
289
+ )
290
+ })
291
+ })
292
+ })
293
+ })
package/src/cache/user.ts CHANGED
@@ -6,7 +6,7 @@ import env from "../environment"
6
6
  import * as accounts from "../accounts"
7
7
  import { UserDB } from "../users"
8
8
  import { sdk } from "@budibase/shared-core"
9
- import { User } from "@budibase/types"
9
+ import { User, UserMetadata } from "@budibase/types"
10
10
 
11
11
  const EXPIRY_SECONDS = 3600
12
12
 
@@ -15,7 +15,7 @@ const EXPIRY_SECONDS = 3600
15
15
  */
16
16
  async function populateFromDB(userId: string, tenantId: string) {
17
17
  const db = tenancy.getTenantDB(tenantId)
18
- const user = await db.get<any>(userId)
18
+ const user = await db.get<UserMetadata>(userId)
19
19
  user.budibaseAccess = true
20
20
  if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
21
21
  const account = await accounts.getAccount(user.email)
@@ -57,6 +57,9 @@ export const StaticDatabases = {
57
57
  AUDIT_LOGS: {
58
58
  name: "audit-logs",
59
59
  },
60
+ SCIM_LOGS: {
61
+ name: "scim-logs",
62
+ },
60
63
  }
61
64
 
62
65
  export const APP_PREFIX = prefixed(DocumentType.APP)
@@ -35,6 +35,17 @@ export function getAuditLogDBName(tenantId?: string) {
35
35
  }
36
36
  }
37
37
 
38
+ export function getScimDBName(tenantId?: string) {
39
+ if (!tenantId) {
40
+ tenantId = getTenantId()
41
+ }
42
+ if (tenantId === DEFAULT_TENANT_ID) {
43
+ return StaticDatabases.SCIM_LOGS.name
44
+ } else {
45
+ return `${tenantId}${SEPARATOR}${StaticDatabases.SCIM_LOGS.name}`
46
+ }
47
+ }
48
+
38
49
  export function baseGlobalDBName(tenantId: string | undefined | null) {
39
50
  if (!tenantId || tenantId === DEFAULT_TENANT_ID) {
40
51
  return StaticDatabases.GLOBAL.name
@@ -1,66 +1,57 @@
1
+ import PouchDB from "pouchdb"
1
2
  import { getPouchDB, closePouchDB } from "./couch"
2
3
  import { DocumentType } from "../constants"
3
4
 
4
5
  class Replication {
5
- source: any
6
- target: any
7
- replication: any
6
+ source: PouchDB.Database
7
+ target: PouchDB.Database
8
8
 
9
- /**
10
- *
11
- * @param source - the DB you want to replicate or rollback to
12
- * @param target - the DB you want to replicate to, or rollback from
13
- */
14
- constructor({ source, target }: any) {
9
+ constructor({ source, target }: { source: string; target: string }) {
15
10
  this.source = getPouchDB(source)
16
11
  this.target = getPouchDB(target)
17
12
  }
18
13
 
19
- close() {
20
- return Promise.all([closePouchDB(this.source), closePouchDB(this.target)])
14
+ async close() {
15
+ await Promise.all([closePouchDB(this.source), closePouchDB(this.target)])
21
16
  }
22
17
 
23
- promisify(operation: any, opts = {}) {
24
- return new Promise(resolve => {
25
- operation(this.target, opts)
26
- .on("denied", function (err: any) {
18
+ replicate(opts: PouchDB.Replication.ReplicateOptions = {}) {
19
+ return new Promise<PouchDB.Replication.ReplicationResult<{}>>(resolve => {
20
+ this.source.replicate
21
+ .to(this.target, opts)
22
+ .on("denied", function (err) {
27
23
  // a document failed to replicate (e.g. due to permissions)
28
24
  throw new Error(`Denied: Document failed to replicate ${err}`)
29
25
  })
30
- .on("complete", function (info: any) {
26
+ .on("complete", function (info) {
31
27
  return resolve(info)
32
28
  })
33
- .on("error", function (err: any) {
29
+ .on("error", function (err) {
34
30
  throw new Error(`Replication Error: ${err}`)
35
31
  })
36
32
  })
37
33
  }
38
34
 
39
- /**
40
- * Two way replication operation, intended to be promise based.
41
- * @param opts - PouchDB replication options
42
- */
43
- sync(opts = {}) {
44
- this.replication = this.promisify(this.source.sync, opts)
45
- return this.replication
46
- }
35
+ appReplicateOpts(
36
+ opts: PouchDB.Replication.ReplicateOptions = {}
37
+ ): PouchDB.Replication.ReplicateOptions {
38
+ if (typeof opts.filter === "string") {
39
+ return opts
40
+ }
47
41
 
48
- /**
49
- * One way replication operation, intended to be promise based.
50
- * @param opts - PouchDB replication options
51
- */
52
- replicate(opts = {}) {
53
- this.replication = this.promisify(this.source.replicate.to, opts)
54
- return this.replication
55
- }
42
+ const filter = opts.filter
43
+ delete opts.filter
56
44
 
57
- appReplicateOpts() {
58
45
  return {
59
- filter: (doc: any) => {
46
+ ...opts,
47
+ filter: (doc: any, params: any) => {
60
48
  if (doc._id && doc._id.startsWith(DocumentType.AUTOMATION_LOG)) {
61
49
  return false
62
50
  }
63
- return doc._id !== DocumentType.APP_METADATA
51
+ if (doc._id === DocumentType.APP_METADATA) {
52
+ return false
53
+ }
54
+ return filter ? filter(doc, params) : true
64
55
  },
65
56
  }
66
57
  }
@@ -75,10 +66,6 @@ class Replication {
75
66
  // take the opportunity to remove deleted tombstones
76
67
  await this.replicate()
77
68
  }
78
-
79
- cancel() {
80
- this.replication.cancel()
81
- }
82
69
  }
83
70
 
84
71
  export default Replication
@@ -70,7 +70,15 @@ export class DatabaseImpl implements Database {
70
70
  DatabaseImpl.nano = buildNano(couchInfo)
71
71
  }
72
72
 
73
- async exists() {
73
+ exists(docId?: string) {
74
+ if (docId === undefined) {
75
+ return this.dbExists()
76
+ }
77
+
78
+ return this.docExists(docId)
79
+ }
80
+
81
+ private async dbExists() {
74
82
  const response = await directCouchUrlCall({
75
83
  url: `${this.couchInfo.url}/${this.name}`,
76
84
  method: "HEAD",
@@ -79,6 +87,15 @@ export class DatabaseImpl implements Database {
79
87
  return response.status === 200
80
88
  }
81
89
 
90
+ private async docExists(id: string): Promise<boolean> {
91
+ try {
92
+ await this.performCall(db => () => db.head(id))
93
+ return true
94
+ } catch {
95
+ return false
96
+ }
97
+ }
98
+
82
99
  private nano() {
83
100
  return this.instanceNano || DatabaseImpl.nano
84
101
  }
@@ -24,9 +24,12 @@ export class DDInstrumentedDatabase implements Database {
24
24
  return this.db.name
25
25
  }
26
26
 
27
- exists(): Promise<boolean> {
27
+ exists(docId?: string): Promise<boolean> {
28
28
  return tracer.trace("db.exists", span => {
29
- span?.addTags({ db_name: this.name })
29
+ span?.addTags({ db_name: this.name, doc_id: docId })
30
+ if (docId) {
31
+ return this.db.exists(docId)
32
+ }
30
33
  return this.db.exists()
31
34
  })
32
35
  }
@@ -0,0 +1,55 @@
1
+ import _ from "lodash"
2
+ import { AnyDocument } from "@budibase/types"
3
+ import { generator } from "../../../tests"
4
+ import { DatabaseImpl } from "../couch"
5
+ import { newid } from "../../utils"
6
+
7
+ describe("DatabaseImpl", () => {
8
+ const database = new DatabaseImpl(generator.word())
9
+ const documents: AnyDocument[] = []
10
+
11
+ beforeAll(async () => {
12
+ const docsToCreate = Array.from({ length: 10 }).map(() => ({
13
+ _id: newid(),
14
+ }))
15
+ const createdDocs = await database.bulkDocs(docsToCreate)
16
+
17
+ documents.push(...createdDocs.map((x: any) => ({ _id: x.id, _rev: x.rev })))
18
+ })
19
+
20
+ describe("document exists", () => {
21
+ it("can check existing docs by id", async () => {
22
+ const existingDoc = _.sample(documents)
23
+ const result = await database.exists(existingDoc!._id!)
24
+
25
+ expect(result).toBe(true)
26
+ })
27
+
28
+ it("can check non existing docs by id", async () => {
29
+ const result = await database.exists(newid())
30
+
31
+ expect(result).toBe(false)
32
+ })
33
+
34
+ it("can check an existing doc by id multiple times", async () => {
35
+ const existingDoc = _.sample(documents)
36
+ const id = existingDoc!._id!
37
+
38
+ const results = []
39
+ results.push(await database.exists(id))
40
+ results.push(await database.exists(id))
41
+ results.push(await database.exists(id))
42
+
43
+ expect(results).toEqual([true, true, true])
44
+ })
45
+
46
+ it("returns false after the doc is deleted", async () => {
47
+ const existingDoc = _.sample(documents)
48
+ const id = existingDoc!._id!
49
+ expect(await database.exists(id)).toBe(true)
50
+
51
+ await database.remove(existingDoc!)
52
+ expect(await database.exists(id)).toBe(false)
53
+ })
54
+ })
55
+ })
@@ -186,6 +186,7 @@ const environment = {
186
186
  environment[key] = value
187
187
  },
188
188
  ROLLING_LOG_MAX_SIZE: process.env.ROLLING_LOG_MAX_SIZE || "10M",
189
+ DISABLE_SCIM_CALLS: process.env.DISABLE_SCIM_CALLS,
189
190
  }
190
191
 
191
192
  // clean up any environment variable edge cases
@@ -4,4 +4,5 @@ export enum JobQueue {
4
4
  AUDIT_LOG = "auditLogQueue",
5
5
  SYSTEM_EVENT_QUEUE = "systemEventQueue",
6
6
  APP_MIGRATION = "appMigration",
7
+ DOC_WRITETHROUGH_QUEUE = "docWritethroughQueue",
7
8
  }