@effect-app/infra 4.0.0-beta.12 → 4.0.0-beta.120
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/CHANGELOG.md +794 -0
- package/dist/CUPS.d.ts +3 -3
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +3 -3
- package/dist/Emailer/service.d.ts +3 -3
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/MainFiberSet.d.ts +2 -2
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +3 -3
- package/dist/Model/Repository/Registry.d.ts +20 -0
- package/dist/Model/Repository/Registry.d.ts.map +1 -0
- package/dist/Model/Repository/Registry.js +17 -0
- package/dist/Model/Repository/internal/internal.d.ts +3 -3
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +22 -16
- package/dist/Model/Repository/makeRepo.d.ts +5 -4
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +4 -1
- package/dist/Model/Repository/service.d.ts +5 -0
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +7 -6
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository.d.ts +1 -0
- package/dist/Model/Repository.d.ts.map +1 -1
- package/dist/Model/Repository.js +2 -1
- package/dist/Model/query/dsl.d.ts +9 -9
- package/dist/Model.d.ts +1 -0
- package/dist/Model.d.ts.map +1 -1
- package/dist/Model.js +2 -1
- package/dist/Operations.d.ts +2 -2
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +3 -3
- package/dist/OperationsRepo.d.ts +3 -3
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +2 -4
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +8 -6
- package/dist/QueueMaker/errors.d.ts +1 -1
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +3 -3
- package/dist/QueueMaker/sbqueue.js +3 -3
- package/dist/RequestContext.d.ts +22 -17
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +5 -5
- package/dist/RequestFiberSet.d.ts +2 -2
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +5 -5
- package/dist/Store/ContextMapContainer.d.ts +19 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +13 -3
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +136 -68
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +24 -21
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +26 -21
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +191 -0
- package/dist/Store/SQL/query.d.ts +38 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +367 -0
- package/dist/Store/SQL.d.ts +20 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +381 -0
- package/dist/Store/index.d.ts +4 -1
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +15 -3
- package/dist/Store/service.d.ts +16 -5
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +24 -6
- package/dist/adapters/ServiceBus.d.ts +6 -6
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +9 -9
- package/dist/adapters/cosmos-client.d.ts +2 -2
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +3 -3
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +2 -2
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +3 -3
- package/dist/adapters/mongo-client.d.ts +2 -2
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +3 -3
- package/dist/adapters/redis-client.d.ts +3 -3
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +3 -3
- package/dist/api/ContextProvider.d.ts +6 -6
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +6 -6
- package/dist/api/internal/auth.d.ts +1 -1
- package/dist/api/internal/events.d.ts +2 -2
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +11 -7
- package/dist/api/layerUtils.d.ts +5 -5
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +3 -3
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +35 -1
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +39 -1
- package/dist/api/routing.d.ts +1 -5
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +3 -2
- package/dist/api/setupRequest.d.ts +6 -3
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +11 -6
- package/dist/logger.d.ts.map +1 -1
- package/examples/query.ts +30 -26
- package/package.json +36 -18
- package/src/CUPS.ts +2 -2
- package/src/Emailer/service.ts +2 -2
- package/src/MainFiberSet.ts +2 -2
- package/src/Model/Repository/Registry.ts +33 -0
- package/src/Model/Repository/internal/internal.ts +76 -59
- package/src/Model/Repository/makeRepo.ts +7 -4
- package/src/Model/Repository/service.ts +6 -0
- package/src/Model/Repository.ts +1 -0
- package/src/Model.ts +1 -0
- package/src/Operations.ts +2 -2
- package/src/OperationsRepo.ts +2 -2
- package/src/QueueMaker/SQLQueue.ts +8 -7
- package/src/QueueMaker/memQueue.ts +2 -2
- package/src/QueueMaker/sbqueue.ts +2 -2
- package/src/RequestContext.ts +4 -4
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +41 -2
- package/src/Store/Cosmos.ts +350 -255
- package/src/Store/Disk.ts +37 -33
- package/src/Store/Memory.ts +29 -22
- package/src/Store/SQL/Pg.ts +321 -0
- package/src/Store/SQL/query.ts +409 -0
- package/src/Store/SQL.ts +674 -0
- package/src/Store/index.ts +17 -2
- package/src/Store/service.ts +31 -7
- package/src/adapters/ServiceBus.ts +8 -8
- package/src/adapters/cosmos-client.ts +2 -2
- package/src/adapters/memQueue.ts +2 -2
- package/src/adapters/mongo-client.ts +2 -2
- package/src/adapters/redis-client.ts +2 -2
- package/src/api/ContextProvider.ts +11 -11
- package/src/api/internal/events.ts +14 -9
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +43 -0
- package/src/api/routing.ts +3 -3
- package/src/api/setupRequest.ts +27 -7
- package/test/contextProvider.test.ts +11 -11
- package/test/controller.test.ts +12 -9
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/date-query.test.d.ts.map +1 -0
- package/test/dist/fixtures.d.ts +19 -9
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +11 -9
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/fixtures.ts +10 -8
- package/test/query.test.ts +182 -33
- package/test/rawQuery.test.ts +22 -18
- package/test/requires.test.ts +6 -5
- package/test/rpc-multi-middleware.test.ts +72 -3
- package/test/sql-store.test.ts +1064 -0
- package/test/validateSample.test.ts +12 -9
- package/tsconfig.json +0 -1
package/src/Store/Cosmos.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { InfraLogger } from "../logger.js"
|
|
|
9
9
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
10
10
|
import { type RawQuery } from "../Model/query.js"
|
|
11
11
|
import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
|
|
12
|
+
import { storeId } from "./Memory.js"
|
|
12
13
|
import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
13
14
|
|
|
14
15
|
const makeMapId =
|
|
@@ -46,11 +47,29 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
46
47
|
id: containerId,
|
|
47
48
|
uniqueKeyPolicy: config?.uniqueKeys
|
|
48
49
|
? { uniqueKeys: config.uniqueKeys }
|
|
49
|
-
: undefined
|
|
50
|
+
: undefined,
|
|
51
|
+
partitionKey: {
|
|
52
|
+
paths: ["/_partitionKey"],
|
|
53
|
+
version: 2 // support large partitionkeys so that the hash is not based on just the first 100 bytes!
|
|
54
|
+
}
|
|
50
55
|
}))
|
|
51
56
|
)
|
|
52
57
|
|
|
53
|
-
const
|
|
58
|
+
const basePartitionKey = config?.partitionValue() ?? "primary"
|
|
59
|
+
const nsPrefix = (ns: string) => ns === "primary" ? "" : `${ns}::`
|
|
60
|
+
const nsPartitionValue = (ns: string, e?: Encoded) => {
|
|
61
|
+
const base = config?.partitionValue(e) ?? "primary"
|
|
62
|
+
return `${nsPrefix(ns)}${base}`
|
|
63
|
+
}
|
|
64
|
+
const nsBasePartitionKey = (ns: string) => `${nsPrefix(ns)}${basePartitionKey}`
|
|
65
|
+
const resolveNamespace = !config?.allowNamespace
|
|
66
|
+
? Effect.succeed("primary")
|
|
67
|
+
: storeId.asEffect().pipe(Effect.map((namespace) => {
|
|
68
|
+
if (namespace !== "primary" && !config.allowNamespace!(namespace)) {
|
|
69
|
+
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
70
|
+
}
|
|
71
|
+
return namespace
|
|
72
|
+
}))
|
|
54
73
|
|
|
55
74
|
const defaultValues = config?.defaultValues ?? {}
|
|
56
75
|
const container = db.container(containerId)
|
|
@@ -60,7 +79,55 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
60
79
|
// then need to clean up the actual data.. perhaps first do with a config toggle to prescribe to it.
|
|
61
80
|
const importedMarkerId = containerId
|
|
62
81
|
|
|
63
|
-
const
|
|
82
|
+
const ctx = yield* Effect.context<R>()
|
|
83
|
+
const seedCache = new Map<string, Effect.Effect<void>>()
|
|
84
|
+
const makeSeedEffect = (ns: string) => {
|
|
85
|
+
const markerId = ns === "primary" ? importedMarkerId : `${importedMarkerId}::${ns}`
|
|
86
|
+
return Effect
|
|
87
|
+
.promise(() =>
|
|
88
|
+
container
|
|
89
|
+
.item(markerId, markerId)
|
|
90
|
+
.read<{ id: string }>()
|
|
91
|
+
.then(({ resource }) => Option.fromNullishOr(resource))
|
|
92
|
+
)
|
|
93
|
+
.pipe(
|
|
94
|
+
Effect.flatMap((marker) => {
|
|
95
|
+
if (Option.isSome(marker)) return Effect.void
|
|
96
|
+
return InfraLogger.logInfo(`Creating mock data for ${name} (namespace: ${ns})`).pipe(
|
|
97
|
+
Effect.andThen(seed!),
|
|
98
|
+
Effect.flatMap((m) =>
|
|
99
|
+
Effect.flatMapOption(
|
|
100
|
+
Effect.succeed(toNonEmptyArray([...m])),
|
|
101
|
+
(a) => bulkSetInternal(a, ns).pipe(Effect.orDie)
|
|
102
|
+
)
|
|
103
|
+
),
|
|
104
|
+
Effect.andThen(
|
|
105
|
+
Effect.promise(() =>
|
|
106
|
+
container.items.create({
|
|
107
|
+
_partitionKey: markerId,
|
|
108
|
+
id: markerId,
|
|
109
|
+
ttl: -1
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
),
|
|
113
|
+
Effect.provide(ctx),
|
|
114
|
+
Effect.orDie
|
|
115
|
+
)
|
|
116
|
+
}),
|
|
117
|
+
Effect.withLogSpan(`Cosmos.seedCheck ${name} in ${ns} [effect-app/infra/Store]`),
|
|
118
|
+
Effect.withSpan("Cosmos.seed [effect-app/infra/Store]", { attributes: { name, namespace: ns } })
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
122
|
+
if (!seed) return
|
|
123
|
+
let cached = seedCache.get(ns)
|
|
124
|
+
if (!cached) {
|
|
125
|
+
cached = yield* Effect.cached(Effect.uninterruptible(makeSeedEffect(ns)))
|
|
126
|
+
seedCache.set(ns, cached)
|
|
127
|
+
}
|
|
128
|
+
yield* cached
|
|
129
|
+
})
|
|
130
|
+
const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
|
|
64
131
|
Effect
|
|
65
132
|
.gen(function*() {
|
|
66
133
|
// TODO: disable batching if need atomicity
|
|
@@ -77,7 +144,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
77
144
|
resourceBody: {
|
|
78
145
|
...Struct.omit(x, ["_etag", idKey]),
|
|
79
146
|
id: x[idKey],
|
|
80
|
-
_partitionKey:
|
|
147
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
81
148
|
}
|
|
82
149
|
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
83
150
|
// partitionKey: config?.partitionValue(x)
|
|
@@ -89,7 +156,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
89
156
|
resourceBody: {
|
|
90
157
|
...Struct.omit(x, ["_etag", idKey]),
|
|
91
158
|
id: x[idKey],
|
|
92
|
-
_partitionKey:
|
|
159
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
93
160
|
},
|
|
94
161
|
ifMatch: eTag
|
|
95
162
|
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
@@ -108,7 +175,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
108
175
|
.promise(() => bulk(batch.map(([, op]) => op)))
|
|
109
176
|
.pipe(
|
|
110
177
|
Effect
|
|
111
|
-
.delay(Duration.millis(i === 0 ? 0 :
|
|
178
|
+
.delay(Duration.millis(i === 0 ? 0 : 150)),
|
|
112
179
|
Effect
|
|
113
180
|
.flatMap((responses) =>
|
|
114
181
|
Effect.gen(function*() {
|
|
@@ -161,139 +228,180 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
161
228
|
|
|
162
229
|
return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
|
|
163
230
|
})
|
|
164
|
-
.pipe(
|
|
165
|
-
|
|
166
|
-
|
|
231
|
+
.pipe(
|
|
232
|
+
Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
|
|
233
|
+
attributes: { "repository.container_id": containerId, "repository.model_name": name, namespace: ns }
|
|
234
|
+
}, { captureStackTrace: false })
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
238
|
+
resolveNamespace.pipe(Effect.flatMap((ns) => bulkSetInternal(items, ns)))
|
|
167
239
|
|
|
168
240
|
const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
|
|
169
|
-
return
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
(
|
|
173
|
-
[
|
|
174
|
-
x
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
241
|
+
return resolveNamespace
|
|
242
|
+
.pipe(Effect.flatMap((ns) =>
|
|
243
|
+
Effect
|
|
244
|
+
.suspend(() => {
|
|
245
|
+
const batch = [...items].map(
|
|
246
|
+
(x) =>
|
|
247
|
+
[
|
|
248
|
+
x,
|
|
249
|
+
Option.match(Option.fromNullishOr(x._etag), {
|
|
250
|
+
onNone: () => ({
|
|
251
|
+
operationType: "Create" as const,
|
|
252
|
+
resourceBody: {
|
|
253
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
254
|
+
id: x[idKey],
|
|
255
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
256
|
+
}
|
|
257
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
258
|
+
// partitionKey: config?.partitionValue(x)
|
|
259
|
+
}),
|
|
260
|
+
onSome: (eTag) => ({
|
|
261
|
+
operationType: "Replace" as const,
|
|
262
|
+
id: x[idKey],
|
|
263
|
+
resourceBody: {
|
|
264
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
265
|
+
id: x[idKey],
|
|
266
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
267
|
+
},
|
|
268
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
269
|
+
// partitionKey: config?.partitionValue(x)
|
|
270
|
+
ifMatch: eTag
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
] as const
|
|
274
|
+
)
|
|
201
275
|
|
|
202
|
-
|
|
276
|
+
const ex = batch.map(([, c]) => c)
|
|
203
277
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
278
|
+
return Effect
|
|
279
|
+
.promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
|
|
280
|
+
.pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
|
|
281
|
+
const result = x.result ?? []
|
|
282
|
+
const firstFailed = result.find(
|
|
283
|
+
(x: any) => x.statusCode > 299 || x.statusCode < 200
|
|
284
|
+
)
|
|
285
|
+
if (firstFailed) {
|
|
286
|
+
const code = firstFailed.statusCode ?? 0
|
|
287
|
+
if (code === 412 || code === 404 || code === 409) {
|
|
288
|
+
return yield* new OptimisticConcurrencyException({ type: name, id: "batch", code })
|
|
289
|
+
}
|
|
216
290
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
291
|
+
return yield* Effect.die(
|
|
292
|
+
new CosmosDbOperationError("not able to update record: " + code)
|
|
293
|
+
)
|
|
294
|
+
}
|
|
221
295
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
296
|
+
return batch.map(([e], i) => ({
|
|
297
|
+
...e,
|
|
298
|
+
_etag: result[i]?.eTag
|
|
299
|
+
})) as unknown as NonEmptyReadonlyArray<Encoded>
|
|
300
|
+
})))
|
|
301
|
+
})
|
|
302
|
+
.pipe(Effect
|
|
303
|
+
.withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
|
|
304
|
+
attributes: {
|
|
305
|
+
"repository.container_id": containerId,
|
|
306
|
+
"repository.model_name": name,
|
|
307
|
+
namespace: ns
|
|
308
|
+
}
|
|
309
|
+
}, { captureStackTrace: false }))
|
|
310
|
+
))
|
|
232
311
|
}
|
|
233
312
|
|
|
234
313
|
const s: Store<IdKey, Encoded> = {
|
|
314
|
+
seedNamespace: (ns) => seedNamespace(ns),
|
|
315
|
+
|
|
235
316
|
queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
|
|
236
317
|
Effect
|
|
237
|
-
.sync(() => query.cosmos({ name }))
|
|
318
|
+
.all({ q: Effect.sync(() => query.cosmos({ name })), ns: resolveNamespace })
|
|
238
319
|
.pipe(
|
|
239
|
-
Effect.tap((q) => logQuery(q)),
|
|
240
|
-
Effect.flatMap((q) =>
|
|
241
|
-
Effect
|
|
320
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
321
|
+
Effect.flatMap(({ ns, q }) =>
|
|
322
|
+
Effect
|
|
323
|
+
.promise(() =>
|
|
324
|
+
container
|
|
325
|
+
.items
|
|
326
|
+
.query<Out>(q, { partitionKey: nsBasePartitionKey(ns) })
|
|
327
|
+
.fetchAll()
|
|
328
|
+
.then(({ resources }) =>
|
|
329
|
+
resources.map(
|
|
330
|
+
(_) => ({ ...defaultValues, ...mapReverseId(_ as any) }) as Out
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
.pipe(
|
|
335
|
+
Effect.withSpan("Cosmos.queryRaw [effect-app/infra/Store]", {
|
|
336
|
+
attributes: {
|
|
337
|
+
"repository.container_id": containerId,
|
|
338
|
+
"repository.model_name": name,
|
|
339
|
+
namespace: ns
|
|
340
|
+
}
|
|
341
|
+
}, { captureStackTrace: false })
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
),
|
|
345
|
+
batchRemove: (ids, partitionKey?: string) =>
|
|
346
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
347
|
+
Effect
|
|
348
|
+
.promise(() =>
|
|
349
|
+
execBatch(
|
|
350
|
+
mutable(ids.map((id) =>
|
|
351
|
+
dropUndefinedT({
|
|
352
|
+
operationType: "Delete" as const,
|
|
353
|
+
id
|
|
354
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
355
|
+
// partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
|
|
356
|
+
})
|
|
357
|
+
)),
|
|
358
|
+
partitionKey ?? nsBasePartitionKey(ns)
|
|
359
|
+
)
|
|
360
|
+
)
|
|
361
|
+
.pipe(
|
|
362
|
+
Effect.withSpan("Cosmos.batchRemove [effect-app/infra/Store]", {
|
|
363
|
+
attributes: {
|
|
364
|
+
"repository.container_id": containerId,
|
|
365
|
+
"repository.model_name": name,
|
|
366
|
+
namespace: ns
|
|
367
|
+
}
|
|
368
|
+
}, { captureStackTrace: false })
|
|
369
|
+
)
|
|
370
|
+
)),
|
|
371
|
+
all: Effect
|
|
372
|
+
.all({
|
|
373
|
+
q: Effect.sync(() => ({
|
|
374
|
+
query: `SELECT * FROM ${name}`,
|
|
375
|
+
parameters: []
|
|
376
|
+
})),
|
|
377
|
+
ns: resolveNamespace
|
|
378
|
+
})
|
|
379
|
+
.pipe(
|
|
380
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
381
|
+
Effect.flatMap(({ ns, q }) =>
|
|
382
|
+
Effect
|
|
383
|
+
.promise(() =>
|
|
242
384
|
container
|
|
243
385
|
.items
|
|
244
|
-
.query<
|
|
386
|
+
.query<PMCosmos>(q, { partitionKey: nsBasePartitionKey(ns) })
|
|
245
387
|
.fetchAll()
|
|
246
388
|
.then(({ resources }) =>
|
|
247
389
|
resources.map(
|
|
248
|
-
(_) => ({ ...defaultValues, ...mapReverseId(_
|
|
390
|
+
(_) => ({ ...defaultValues, ...mapReverseId(_) })
|
|
249
391
|
)
|
|
250
392
|
)
|
|
251
393
|
)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
mutable(ids.map((id) =>
|
|
262
|
-
dropUndefinedT({
|
|
263
|
-
operationType: "Delete" as const,
|
|
264
|
-
id
|
|
265
|
-
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
266
|
-
// partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
|
|
267
|
-
})
|
|
268
|
-
)),
|
|
269
|
-
partitionKey ?? mainPartitionKey
|
|
394
|
+
.pipe(
|
|
395
|
+
Effect.withSpan("Cosmos.all [effect-app/infra/Store]", {
|
|
396
|
+
attributes: {
|
|
397
|
+
"repository.container_id": containerId,
|
|
398
|
+
"repository.model_name": name,
|
|
399
|
+
namespace: ns
|
|
400
|
+
}
|
|
401
|
+
}, { captureStackTrace: false })
|
|
402
|
+
)
|
|
270
403
|
)
|
|
271
404
|
),
|
|
272
|
-
all: Effect
|
|
273
|
-
.sync(() => ({
|
|
274
|
-
query: `SELECT * FROM ${name}`,
|
|
275
|
-
parameters: []
|
|
276
|
-
}))
|
|
277
|
-
.pipe(
|
|
278
|
-
Effect.tap((q) => logQuery(q)),
|
|
279
|
-
Effect.flatMap((q) =>
|
|
280
|
-
Effect.promise(() =>
|
|
281
|
-
container
|
|
282
|
-
.items
|
|
283
|
-
.query<PMCosmos>(q, { partitionKey: mainPartitionKey })
|
|
284
|
-
.fetchAll()
|
|
285
|
-
.then(({ resources }) =>
|
|
286
|
-
resources.map(
|
|
287
|
-
(_) => ({ ...defaultValues, ...mapReverseId(_) })
|
|
288
|
-
)
|
|
289
|
-
)
|
|
290
|
-
)
|
|
291
|
-
),
|
|
292
|
-
Effect
|
|
293
|
-
.withSpan("Cosmos.all [effect-app/infra/Store]", {
|
|
294
|
-
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
295
|
-
}, { captureStackTrace: false })
|
|
296
|
-
),
|
|
297
405
|
/**
|
|
298
406
|
* May return duplicate results for "join_find", when matching more than once.
|
|
299
407
|
*/
|
|
@@ -305,166 +413,153 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
305
413
|
const filter = f.filter
|
|
306
414
|
type M = U extends undefined ? Encoded : Pick<Encoded, U>
|
|
307
415
|
return Effect
|
|
308
|
-
.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
416
|
+
.all({
|
|
417
|
+
q: Effect.sync(() =>
|
|
418
|
+
buildWhereCosmosQuery3(
|
|
419
|
+
idKey,
|
|
420
|
+
filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
|
|
421
|
+
name,
|
|
422
|
+
defaultValues,
|
|
423
|
+
f.select as
|
|
424
|
+
| NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>
|
|
425
|
+
| undefined,
|
|
426
|
+
f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
|
|
427
|
+
skip,
|
|
428
|
+
limit
|
|
429
|
+
)
|
|
430
|
+
),
|
|
431
|
+
ns: resolveNamespace
|
|
432
|
+
})
|
|
320
433
|
.pipe(
|
|
321
|
-
Effect.tap((q) => logQuery(q)),
|
|
434
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
322
435
|
Effect
|
|
323
|
-
.flatMap((q) =>
|
|
324
|
-
Effect
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
({
|
|
436
|
+
.flatMap(({ ns, q }) =>
|
|
437
|
+
Effect
|
|
438
|
+
.promise(() =>
|
|
439
|
+
f.select
|
|
440
|
+
? container
|
|
441
|
+
.items
|
|
442
|
+
.query<M>(q, { partitionKey: nsBasePartitionKey(ns) })
|
|
443
|
+
.fetchAll()
|
|
444
|
+
.then(({ resources }) =>
|
|
445
|
+
resources.map((_) => ({
|
|
333
446
|
...pipe(
|
|
334
447
|
defaultValues,
|
|
335
|
-
Struct.pick(f.select!.filter((_) => typeof _ === "string"))
|
|
448
|
+
Struct.pick(f.select!.filter((_) => typeof _ === "string") as never[])
|
|
336
449
|
),
|
|
337
450
|
...mapReverseId(_ as any)
|
|
338
|
-
})
|
|
451
|
+
}))
|
|
339
452
|
)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
453
|
+
: container
|
|
454
|
+
.items
|
|
455
|
+
.query<{ f: M }>(q, { partitionKey: nsBasePartitionKey(ns) })
|
|
456
|
+
.fetchAll()
|
|
457
|
+
.then(({ resources }) =>
|
|
458
|
+
resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
|
|
459
|
+
)
|
|
460
|
+
)
|
|
461
|
+
.pipe(
|
|
462
|
+
Effect.withSpan("Cosmos.filter [effect-app/infra/Store]", {
|
|
463
|
+
attributes: {
|
|
464
|
+
"repository.container_id": containerId,
|
|
465
|
+
"repository.model_name": name,
|
|
466
|
+
namespace: ns
|
|
467
|
+
}
|
|
468
|
+
}, { captureStackTrace: false })
|
|
469
|
+
)
|
|
349
470
|
)
|
|
350
471
|
)
|
|
351
|
-
.pipe(
|
|
352
|
-
Effect.withSpan("Cosmos.filter [effect-app/infra/Store]", {
|
|
353
|
-
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
354
|
-
}, { captureStackTrace: false })
|
|
355
|
-
)
|
|
356
472
|
},
|
|
357
473
|
find: (id) =>
|
|
358
|
-
Effect
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
.pipe(Effect
|
|
368
|
-
.withSpan("Cosmos.find [effect-app/infra/Store]", {
|
|
369
|
-
attributes: {
|
|
370
|
-
"repository.container_id": containerId,
|
|
371
|
-
"repository.model_name": name,
|
|
372
|
-
partitionValue: config?.partitionValue({ [idKey]: id } as Encoded),
|
|
373
|
-
id
|
|
374
|
-
}
|
|
375
|
-
}, { captureStackTrace: false })),
|
|
376
|
-
set: (e) =>
|
|
377
|
-
Option
|
|
378
|
-
.match(
|
|
379
|
-
Option
|
|
380
|
-
.fromNullishOr(e._etag),
|
|
381
|
-
{
|
|
382
|
-
onNone: () =>
|
|
383
|
-
Effect.promise(() =>
|
|
384
|
-
container.items.create({
|
|
385
|
-
...mapId(e),
|
|
386
|
-
_partitionKey: config?.partitionValue(e)
|
|
387
|
-
})
|
|
388
|
-
),
|
|
389
|
-
onSome: (eTag) =>
|
|
390
|
-
Effect.promise(() =>
|
|
391
|
-
container.item(e[idKey], config?.partitionValue(e)).replace(
|
|
392
|
-
{ ...mapId(e), _partitionKey: config?.partitionValue(e) },
|
|
393
|
-
{
|
|
394
|
-
accessCondition: {
|
|
395
|
-
type: "IfMatch",
|
|
396
|
-
condition: eTag
|
|
397
|
-
}
|
|
398
|
-
}
|
|
474
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
475
|
+
Effect
|
|
476
|
+
.promise(() =>
|
|
477
|
+
container
|
|
478
|
+
.item(id, nsPartitionValue(ns, { [idKey]: id } as Encoded))
|
|
479
|
+
.read<Encoded>()
|
|
480
|
+
.then(({ resource }) =>
|
|
481
|
+
Option.fromNullishOr(resource).pipe(
|
|
482
|
+
Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
|
|
399
483
|
)
|
|
400
484
|
)
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
Effect
|
|
405
|
-
.flatMap((x) => {
|
|
406
|
-
if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
|
|
407
|
-
return Effect.fail(
|
|
408
|
-
new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
|
|
409
|
-
)
|
|
410
|
-
}
|
|
411
|
-
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
412
|
-
return Effect.die(
|
|
413
|
-
new CosmosDbOperationError(
|
|
414
|
-
"not able to update record: " + x.statusCode
|
|
415
|
-
)
|
|
416
|
-
)
|
|
417
|
-
}
|
|
418
|
-
return Effect.sync(() => ({
|
|
419
|
-
...e,
|
|
420
|
-
_etag: x.etag
|
|
421
|
-
}))
|
|
422
|
-
}),
|
|
423
|
-
Effect
|
|
424
|
-
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
485
|
+
)
|
|
486
|
+
.pipe(Effect
|
|
487
|
+
.withSpan("Cosmos.find [effect-app/infra/Store]", {
|
|
425
488
|
attributes: {
|
|
426
489
|
"repository.container_id": containerId,
|
|
427
490
|
"repository.model_name": name,
|
|
428
|
-
|
|
491
|
+
partitionValue: nsPartitionValue(ns, { [idKey]: id } as Encoded),
|
|
492
|
+
namespace: ns,
|
|
493
|
+
id
|
|
429
494
|
}
|
|
430
|
-
}, { captureStackTrace: false })
|
|
431
|
-
|
|
495
|
+
}, { captureStackTrace: false }))
|
|
496
|
+
)),
|
|
497
|
+
set: (e) =>
|
|
498
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
499
|
+
Option
|
|
500
|
+
.match(
|
|
501
|
+
Option
|
|
502
|
+
.fromNullishOr(e._etag),
|
|
503
|
+
{
|
|
504
|
+
onNone: () =>
|
|
505
|
+
Effect.promise(() =>
|
|
506
|
+
container.items.create({
|
|
507
|
+
...mapId(e),
|
|
508
|
+
_partitionKey: nsPartitionValue(ns, e)
|
|
509
|
+
})
|
|
510
|
+
),
|
|
511
|
+
onSome: (eTag) =>
|
|
512
|
+
Effect.promise(() =>
|
|
513
|
+
container.item(e[idKey], nsPartitionValue(ns, e)).replace(
|
|
514
|
+
{ ...mapId(e), _partitionKey: nsPartitionValue(ns, e) },
|
|
515
|
+
{
|
|
516
|
+
accessCondition: {
|
|
517
|
+
type: "IfMatch",
|
|
518
|
+
condition: eTag
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
)
|
|
522
|
+
)
|
|
523
|
+
}
|
|
524
|
+
)
|
|
525
|
+
.pipe(
|
|
526
|
+
Effect
|
|
527
|
+
.flatMap((x) => {
|
|
528
|
+
if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
|
|
529
|
+
return Effect.fail(
|
|
530
|
+
new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
|
|
531
|
+
)
|
|
532
|
+
}
|
|
533
|
+
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
534
|
+
return Effect.die(
|
|
535
|
+
new CosmosDbOperationError(
|
|
536
|
+
"not able to update record: " + x.statusCode
|
|
537
|
+
)
|
|
538
|
+
)
|
|
539
|
+
}
|
|
540
|
+
return Effect.sync(() => ({
|
|
541
|
+
...e,
|
|
542
|
+
_etag: x.etag
|
|
543
|
+
}))
|
|
544
|
+
}),
|
|
545
|
+
Effect
|
|
546
|
+
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
547
|
+
attributes: {
|
|
548
|
+
"repository.container_id": containerId,
|
|
549
|
+
"repository.model_name": name,
|
|
550
|
+
namespace: ns,
|
|
551
|
+
id: e[idKey]
|
|
552
|
+
}
|
|
553
|
+
}, { captureStackTrace: false })
|
|
554
|
+
)
|
|
555
|
+
)),
|
|
432
556
|
batchSet,
|
|
433
557
|
bulkSet
|
|
434
558
|
}
|
|
435
559
|
|
|
436
|
-
//
|
|
437
|
-
|
|
438
|
-
container
|
|
439
|
-
.item(importedMarkerId, importedMarkerId)
|
|
440
|
-
.read<{ id: string }>()
|
|
441
|
-
.then(({ resource }) => Option.fromNullishOr(resource))
|
|
442
|
-
)
|
|
560
|
+
// Eagerly seed primary namespace on initialization
|
|
561
|
+
yield* seedNamespace("primary")
|
|
443
562
|
|
|
444
|
-
if (!Option.isSome(marker)) {
|
|
445
|
-
yield* InfraLogger.logInfo("Creating mock data for " + name)
|
|
446
|
-
if (seed) {
|
|
447
|
-
const m = yield* seed
|
|
448
|
-
yield* Effect.flatMapOption(
|
|
449
|
-
Effect.succeed(toNonEmptyArray([...m])),
|
|
450
|
-
(a) =>
|
|
451
|
-
s.bulkSet(a).pipe(
|
|
452
|
-
Effect.orDie,
|
|
453
|
-
Effect
|
|
454
|
-
// we delay extra here, so that initial creation between Companies/POs also have an interval between them.
|
|
455
|
-
.delay(Duration.millis(1100))
|
|
456
|
-
)
|
|
457
|
-
)
|
|
458
|
-
}
|
|
459
|
-
// Mark as imported
|
|
460
|
-
yield* Effect.promise(() =>
|
|
461
|
-
container.items.create({
|
|
462
|
-
_partitionKey: importedMarkerId,
|
|
463
|
-
id: importedMarkerId,
|
|
464
|
-
ttl: -1
|
|
465
|
-
})
|
|
466
|
-
)
|
|
467
|
-
}
|
|
468
563
|
return s
|
|
469
564
|
})
|
|
470
565
|
}
|