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