@effect-app/infra 1.23.2 → 1.23.4
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 +20 -0
- package/_cjs/api/internal/events.cjs +2 -1
- package/_cjs/api/internal/events.cjs.map +1 -1
- package/_cjs/api/internal/middlewares.cjs +3 -2
- package/_cjs/api/internal/middlewares.cjs.map +1 -1
- package/_cjs/api/routing/defaultErrorHandler.cjs +3 -2
- package/_cjs/api/routing/defaultErrorHandler.cjs.map +1 -1
- package/_cjs/api/routing/makeRequestHandler.cjs +9 -8
- package/_cjs/api/routing/makeRequestHandler.cjs.map +1 -1
- package/_cjs/errorReporter.cjs +12 -11
- package/_cjs/errorReporter.cjs.map +1 -1
- package/_cjs/logger.cjs +9 -0
- package/_cjs/logger.cjs.map +1 -0
- package/_cjs/services/Emailer/Sendgrid.cjs +6 -5
- package/_cjs/services/Emailer/Sendgrid.cjs.map +1 -1
- package/_cjs/services/Emailer/fake.cjs +3 -2
- package/_cjs/services/Emailer/fake.cjs.map +1 -1
- package/_cjs/services/QueueMaker/SQLQueue.cjs +7 -6
- package/_cjs/services/QueueMaker/SQLQueue.cjs.map +1 -1
- package/_cjs/services/QueueMaker/memQueue.cjs +15 -14
- package/_cjs/services/QueueMaker/memQueue.cjs.map +1 -1
- package/_cjs/services/QueueMaker/sbqueue.cjs +15 -14
- package/_cjs/services/QueueMaker/sbqueue.cjs.map +1 -1
- package/_cjs/services/RepositoryBase.cjs +22 -22
- package/_cjs/services/RepositoryBase.cjs.map +1 -1
- package/_cjs/services/Store/Cosmos/query.cjs +2 -3
- package/_cjs/services/Store/Cosmos/query.cjs.map +1 -1
- package/_cjs/services/Store/Cosmos.cjs +24 -23
- package/_cjs/services/Store/Cosmos.cjs.map +1 -1
- package/_cjs/services/Store/Disk.cjs +6 -6
- package/_cjs/services/Store/Disk.cjs.map +1 -1
- package/_cjs/services/Store/Memory.cjs +7 -6
- package/_cjs/services/Store/Memory.cjs.map +1 -1
- package/_cjs/services/Store/utils.cjs +5 -5
- package/_cjs/services/Store/utils.cjs.map +1 -1
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +3 -2
- package/dist/api/internal/middlewares.d.ts.map +1 -1
- package/dist/api/internal/middlewares.js +4 -3
- package/dist/api/routing/defaultErrorHandler.d.ts.map +1 -1
- package/dist/api/routing/defaultErrorHandler.js +4 -3
- package/dist/api/routing/makeRequestHandler.d.ts.map +1 -1
- package/dist/api/routing/makeRequestHandler.js +10 -9
- package/dist/errorReporter.d.ts +1 -1
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +12 -11
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +3 -0
- package/dist/services/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/services/Emailer/Sendgrid.js +8 -7
- package/dist/services/Emailer/fake.d.ts.map +1 -1
- package/dist/services/Emailer/fake.js +5 -4
- package/dist/services/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/services/QueueMaker/SQLQueue.js +9 -8
- package/dist/services/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/services/QueueMaker/memQueue.js +17 -16
- package/dist/services/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/services/QueueMaker/sbqueue.js +17 -16
- package/dist/services/RepositoryBase.d.ts.map +1 -1
- package/dist/services/RepositoryBase.js +23 -23
- package/dist/services/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/services/Store/Cosmos/query.js +3 -4
- package/dist/services/Store/Cosmos.d.ts.map +1 -1
- package/dist/services/Store/Cosmos.js +24 -23
- package/dist/services/Store/Disk.d.ts.map +1 -1
- package/dist/services/Store/Disk.js +8 -8
- package/dist/services/Store/Memory.d.ts.map +1 -1
- package/dist/services/Store/Memory.js +8 -7
- package/dist/services/Store/utils.d.ts.map +1 -1
- package/dist/services/Store/utils.js +5 -5
- package/examples/query.ts +3 -3
- package/package.json +15 -5
- package/src/api/internal/events.ts +2 -1
- package/src/api/internal/middlewares.ts +3 -2
- package/src/api/routing/defaultErrorHandler.ts +3 -2
- package/src/api/routing/makeRequestHandler.ts +9 -8
- package/src/api/writeDocs.ts.bak +1 -1
- package/src/errorReporter.ts +31 -33
- package/src/logger.ts +3 -0
- package/src/services/Emailer/Sendgrid.ts +14 -12
- package/src/services/Emailer/fake.ts +4 -3
- package/src/services/QueueMaker/SQLQueue.ts +18 -19
- package/src/services/QueueMaker/memQueue.ts +33 -36
- package/src/services/QueueMaker/sbqueue.ts +38 -41
- package/src/services/RepositoryBase.ts +40 -43
- package/src/services/Store/Cosmos/query.ts +6 -5
- package/src/services/Store/Cosmos.ts +75 -90
- package/src/services/Store/Disk.ts +12 -14
- package/src/services/Store/Memory.ts +7 -6
- package/src/services/Store/utils.ts +10 -14
- package/test/query.test.ts +56 -61
- package/tsconfig.json +0 -1
|
@@ -365,8 +365,8 @@ export function makeRepo<
|
|
|
365
365
|
}
|
|
366
366
|
) {
|
|
367
367
|
return Effect
|
|
368
|
-
.gen(function*(
|
|
369
|
-
const rctx = yield*
|
|
368
|
+
.gen(function*() {
|
|
369
|
+
const rctx = yield* Effect.context<R>()
|
|
370
370
|
const encodeMany = flow(
|
|
371
371
|
S.encode(S.Array(schema)),
|
|
372
372
|
Effect.provide(rctx),
|
|
@@ -379,18 +379,18 @@ export function makeRepo<
|
|
|
379
379
|
Effect.withSpan("decodeMany", { captureStackTrace: false })
|
|
380
380
|
)
|
|
381
381
|
|
|
382
|
-
const store = yield*
|
|
383
|
-
const { get } = yield*
|
|
382
|
+
const store = yield* mkStore(args.makeInitial, args.config)
|
|
383
|
+
const { get } = yield* ContextMapContainer
|
|
384
384
|
const cms = Effect.andThen(get, (_) => ({
|
|
385
385
|
get: (id: string) => _.get(`${name}.${id}`),
|
|
386
386
|
set: (id: string, etag: string | undefined) => _.set(`${name}.${id}`, etag)
|
|
387
387
|
}))
|
|
388
388
|
|
|
389
|
-
const pubCfg = yield*
|
|
389
|
+
const pubCfg = yield* Effect.context<R2>()
|
|
390
390
|
const pub = "publishEvents" in args
|
|
391
391
|
? flow(args.publishEvents, Effect.provide(pubCfg))
|
|
392
392
|
: () => Effect.void
|
|
393
|
-
const changeFeed = yield*
|
|
393
|
+
const changeFeed = yield* PubSub.unbounded<[T[], "save" | "remove"]>()
|
|
394
394
|
|
|
395
395
|
const allE = cms
|
|
396
396
|
.pipe(Effect.flatMap((cm) => Effect.map(store.all, (_) => _.map((_) => mapReverse(_, cm.set)))))
|
|
@@ -431,8 +431,8 @@ export function makeRepo<
|
|
|
431
431
|
return Effect.flatMap(
|
|
432
432
|
store.find(id),
|
|
433
433
|
(item) =>
|
|
434
|
-
Effect.gen(function*(
|
|
435
|
-
const { set } = yield*
|
|
434
|
+
Effect.gen(function*() {
|
|
435
|
+
const { set } = yield* cms
|
|
436
436
|
return item.pipe(Option.map((_) => mapReverse(_, set)))
|
|
437
437
|
})
|
|
438
438
|
)
|
|
@@ -456,10 +456,10 @@ export function makeRepo<
|
|
|
456
456
|
Effect
|
|
457
457
|
.sync(() => toNonEmptyArray([...a])),
|
|
458
458
|
(a) =>
|
|
459
|
-
Effect.gen(function*(
|
|
460
|
-
const { get, set } = yield*
|
|
459
|
+
Effect.gen(function*() {
|
|
460
|
+
const { get, set } = yield* cms
|
|
461
461
|
const items = a.map((_) => mapToPersistenceModel(_, get))
|
|
462
|
-
const ret = yield*
|
|
462
|
+
const ret = yield* store.batchSet(items)
|
|
463
463
|
ret.forEach((_) => set(_.id, _._etag))
|
|
464
464
|
})
|
|
465
465
|
)
|
|
@@ -489,23 +489,21 @@ export function makeRepo<
|
|
|
489
489
|
}
|
|
490
490
|
|
|
491
491
|
function removeAndPublish(a: Iterable<T>, events: Iterable<Evt> = []) {
|
|
492
|
-
return Effect.gen(function*(
|
|
493
|
-
const { get, set } = yield*
|
|
492
|
+
return Effect.gen(function*() {
|
|
493
|
+
const { get, set } = yield* cms
|
|
494
494
|
const it = [...a]
|
|
495
|
-
const items = yield*
|
|
495
|
+
const items = yield* encodeMany(it).pipe(Effect.orDie)
|
|
496
496
|
// TODO: we should have a batchRemove on store so the adapter can actually batch...
|
|
497
497
|
for (const e of items) {
|
|
498
|
-
yield*
|
|
498
|
+
yield* store.remove(mapToPersistenceModel(e, get))
|
|
499
499
|
set(e.id, undefined)
|
|
500
500
|
}
|
|
501
|
-
yield*
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
.pipe((_) => Effect.flatMapOption(_, pub))
|
|
506
|
-
)
|
|
501
|
+
yield* Effect
|
|
502
|
+
.sync(() => toNonEmptyArray([...events]))
|
|
503
|
+
// TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
|
|
504
|
+
.pipe((_) => Effect.flatMapOption(_, pub))
|
|
507
505
|
|
|
508
|
-
yield*
|
|
506
|
+
yield* changeFeed.publish([it, "remove"])
|
|
509
507
|
})
|
|
510
508
|
}
|
|
511
509
|
|
|
@@ -691,28 +689,27 @@ export function makeStore<
|
|
|
691
689
|
partitionValue?: (a: Encoded) => string
|
|
692
690
|
}
|
|
693
691
|
) {
|
|
694
|
-
return Effect.gen(function*(
|
|
695
|
-
const { make } = yield*
|
|
696
|
-
|
|
697
|
-
const store = yield*
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
makeInitial
|
|
701
|
-
|
|
702
|
-
.
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
714
|
-
)
|
|
692
|
+
return Effect.gen(function*() {
|
|
693
|
+
const { make } = yield* StoreMaker
|
|
694
|
+
|
|
695
|
+
const store = yield* make<Encoded, string, R | RInitial, EInitial>(
|
|
696
|
+
pluralize(name),
|
|
697
|
+
makeInitial
|
|
698
|
+
? makeInitial
|
|
699
|
+
.pipe(
|
|
700
|
+
Effect.flatMap(Effect.forEach(encodeToEncoded())),
|
|
701
|
+
Effect.withSpan("Repository.makeInitial [effect-app/infra]", {
|
|
702
|
+
attributes: { "repository.model_name": name }
|
|
703
|
+
})
|
|
704
|
+
)
|
|
705
|
+
: undefined,
|
|
706
|
+
{
|
|
707
|
+
...config,
|
|
708
|
+
partitionValue: config?.partitionValue
|
|
709
|
+
?? ((_) => "primary") /*(isIntegrationEvent(r) ? r.companyId : r.id*/
|
|
710
|
+
}
|
|
715
711
|
)
|
|
712
|
+
|
|
716
713
|
return store
|
|
717
714
|
})
|
|
718
715
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Array, Effect, Equivalence, pipe } from "effect-app"
|
|
2
2
|
import type { NonEmptyReadonlyArray } from "effect-app"
|
|
3
3
|
import { assertUnreachable } from "effect-app/utils"
|
|
4
|
+
import { InfraLogger } from "../../../logger.js"
|
|
4
5
|
import type { FilterR, FilterResult } from "../filterApi/query.js"
|
|
5
6
|
import type { SupportedValues } from "../service.js"
|
|
6
7
|
|
|
@@ -11,7 +12,7 @@ export function logQuery(q: {
|
|
|
11
12
|
value: SupportedValues | readonly SupportedValues[]
|
|
12
13
|
}[]
|
|
13
14
|
}) {
|
|
14
|
-
return
|
|
15
|
+
return InfraLogger
|
|
15
16
|
.logDebug("cosmos query")
|
|
16
17
|
.pipe(Effect.annotateLogs({
|
|
17
18
|
query: q.query,
|
|
@@ -114,15 +115,15 @@ export function buildWhereCosmosQuery3(
|
|
|
114
115
|
s += ` AND ${statement(e, i++)}`
|
|
115
116
|
break
|
|
116
117
|
case "or-scope": {
|
|
117
|
-
|
|
118
|
+
++l
|
|
118
119
|
s += ` OR (\n${printN(l + 1)}${print(e.result)}\n${printN(l)})`
|
|
119
|
-
|
|
120
|
+
--l
|
|
120
121
|
break
|
|
121
122
|
}
|
|
122
123
|
case "and-scope": {
|
|
123
|
-
|
|
124
|
+
++l
|
|
124
125
|
s += ` AND (\n${printN(l + 1)}${print(e.result)}\n${printN(l)})`
|
|
125
|
-
|
|
126
|
+
--l
|
|
126
127
|
break
|
|
127
128
|
}
|
|
128
129
|
case "where-scope": {
|
|
@@ -6,6 +6,7 @@ import { Array, Chunk, Duration, Effect, Layer, Option, pipe, Secret, Struct } f
|
|
|
6
6
|
import type { NonEmptyReadonlyArray } from "effect-app"
|
|
7
7
|
import { dropUndefinedT } from "effect-app/utils"
|
|
8
8
|
import { OptimisticConcurrencyException } from "../../errors.js"
|
|
9
|
+
import { InfraLogger } from "../../logger.js"
|
|
9
10
|
import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
|
|
10
11
|
import { StoreMaker } from "./service.js"
|
|
11
12
|
import type { FilterArgs, PersistenceModelType, StorageConfig, Store, StoreConfig } from "./service.js"
|
|
@@ -15,27 +16,26 @@ class CosmosDbOperationError {
|
|
|
15
16
|
} // TODO: Retry operation when running into RU limit.
|
|
16
17
|
|
|
17
18
|
function makeCosmosStore({ prefix }: StorageConfig) {
|
|
18
|
-
return Effect.gen(function*(
|
|
19
|
-
const { db } = yield*
|
|
19
|
+
return Effect.gen(function*() {
|
|
20
|
+
const { db } = yield* CosmosClient
|
|
20
21
|
return {
|
|
21
22
|
make: <Id extends string, Encoded extends Record<string, any> & { id: Id }, R = never, E = never>(
|
|
22
23
|
name: string,
|
|
23
24
|
seed?: Effect<Iterable<Encoded>, E, R>,
|
|
24
25
|
config?: StoreConfig<Encoded>
|
|
25
26
|
) =>
|
|
26
|
-
Effect.gen(function*(
|
|
27
|
+
Effect.gen(function*() {
|
|
27
28
|
type PM = PersistenceModelType<Encoded>
|
|
28
29
|
const containerId = `${prefix}${name}`
|
|
29
|
-
yield*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}))
|
|
37
|
-
)
|
|
30
|
+
yield* Effect.promise(() =>
|
|
31
|
+
db.containers.createIfNotExists(dropUndefinedT({
|
|
32
|
+
id: containerId,
|
|
33
|
+
uniqueKeyPolicy: config?.uniqueKeys
|
|
34
|
+
? { uniqueKeys: config.uniqueKeys }
|
|
35
|
+
: undefined
|
|
36
|
+
}))
|
|
38
37
|
)
|
|
38
|
+
|
|
39
39
|
const defaultValues = config?.defaultValues ?? {}
|
|
40
40
|
const container = db.container(containerId)
|
|
41
41
|
const bulk = container.items.bulk.bind(container.items)
|
|
@@ -44,7 +44,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
44
44
|
|
|
45
45
|
const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
46
46
|
Effect
|
|
47
|
-
.gen(function*(
|
|
47
|
+
.gen(function*() {
|
|
48
48
|
// TODO: disable batching if need atomicity
|
|
49
49
|
// we delay and batch to keep low amount of RUs
|
|
50
50
|
const b = [...items].map(
|
|
@@ -77,51 +77,46 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
77
77
|
)
|
|
78
78
|
const batches = Chunk.toReadonlyArray(Array.chunk_(b, config?.maxBulkSize ?? 10))
|
|
79
79
|
|
|
80
|
-
const batchResult = yield*
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
new OptimisticConcurrencyException(
|
|
98
|
-
{ type: name, id: JSON.stringify(r.resourceBody?.["id"]) }
|
|
99
|
-
)
|
|
100
|
-
)
|
|
80
|
+
const batchResult = yield* Effect.forEach(
|
|
81
|
+
batches
|
|
82
|
+
.map((x, i) => [i, x] as const),
|
|
83
|
+
([i, batch]) =>
|
|
84
|
+
Effect
|
|
85
|
+
.promise(() => bulk(batch.map(([, op]) => op)))
|
|
86
|
+
.pipe(
|
|
87
|
+
Effect
|
|
88
|
+
.delay(Duration.millis(i === 0 ? 0 : 1100)),
|
|
89
|
+
Effect
|
|
90
|
+
.flatMap((responses) =>
|
|
91
|
+
Effect.gen(function*() {
|
|
92
|
+
const r = responses.find((x) => x.statusCode === 412 || x.statusCode === 404)
|
|
93
|
+
if (r) {
|
|
94
|
+
return yield* Effect.fail(
|
|
95
|
+
new OptimisticConcurrencyException(
|
|
96
|
+
{ type: name, id: JSON.stringify(r.resourceBody?.["id"]) }
|
|
101
97
|
)
|
|
102
|
-
}
|
|
103
|
-
const r2 = responses.find(
|
|
104
|
-
(x) => x.statusCode > 299 || x.statusCode < 200
|
|
105
98
|
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
99
|
+
}
|
|
100
|
+
const r2 = responses.find(
|
|
101
|
+
(x) => x.statusCode > 299 || x.statusCode < 200
|
|
102
|
+
)
|
|
103
|
+
if (r2) {
|
|
104
|
+
return yield* Effect.die(
|
|
105
|
+
new CosmosDbOperationError(
|
|
106
|
+
"not able to update record: " + r2.statusCode
|
|
113
107
|
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
})
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
return batch.map(([e], i) => ({
|
|
111
|
+
...e,
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
113
|
+
_etag: responses[i]!.eTag
|
|
114
|
+
}))
|
|
115
|
+
})
|
|
116
|
+
)
|
|
117
|
+
)
|
|
124
118
|
)
|
|
119
|
+
|
|
125
120
|
return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
|
|
126
121
|
})
|
|
127
122
|
.pipe(Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
|
|
@@ -162,7 +157,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
162
157
|
return Effect
|
|
163
158
|
.promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
|
|
164
159
|
.pipe(Effect.flatMap((x) =>
|
|
165
|
-
Effect.gen(function*(
|
|
160
|
+
Effect.gen(function*() {
|
|
166
161
|
const result = x.result ?? []
|
|
167
162
|
const firstFailed = result.find(
|
|
168
163
|
(x: any) => x.statusCode > 299 || x.statusCode < 200
|
|
@@ -170,15 +165,11 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
170
165
|
if (firstFailed) {
|
|
171
166
|
const code = firstFailed.statusCode ?? 0
|
|
172
167
|
if (code === 412 || code === 404) {
|
|
173
|
-
return yield*
|
|
174
|
-
new OptimisticConcurrencyException({ type: name, id: "batch" })
|
|
175
|
-
)
|
|
168
|
+
return yield* new OptimisticConcurrencyException({ type: name, id: "batch" })
|
|
176
169
|
}
|
|
177
170
|
|
|
178
|
-
return yield*
|
|
179
|
-
|
|
180
|
-
new CosmosDbOperationError("not able to update record: " + code)
|
|
181
|
-
)
|
|
171
|
+
return yield* Effect.die(
|
|
172
|
+
new CosmosDbOperationError("not able to update record: " + code)
|
|
182
173
|
)
|
|
183
174
|
}
|
|
184
175
|
|
|
@@ -347,41 +338,35 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
347
338
|
}
|
|
348
339
|
|
|
349
340
|
// handle mock data
|
|
350
|
-
const marker = yield*
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
.then(({ resource }) => Option.fromNullable(resource))
|
|
356
|
-
)
|
|
341
|
+
const marker = yield* Effect.promise(() =>
|
|
342
|
+
container
|
|
343
|
+
.item(importedMarkerId, importedMarkerId)
|
|
344
|
+
.read<{ id: string }>()
|
|
345
|
+
.then(({ resource }) => Option.fromNullable(resource))
|
|
357
346
|
)
|
|
358
347
|
|
|
359
348
|
if (!Option.isSome(marker)) {
|
|
360
|
-
|
|
349
|
+
yield* InfraLogger.logInfo("Creating mock data for " + name)
|
|
361
350
|
if (seed) {
|
|
362
|
-
const m = yield*
|
|
363
|
-
yield*
|
|
364
|
-
Effect.
|
|
365
|
-
|
|
366
|
-
(a)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
)
|
|
373
|
-
)
|
|
351
|
+
const m = yield* seed
|
|
352
|
+
yield* Effect.flatMapOption(
|
|
353
|
+
Effect.succeed(toNonEmptyArray([...m])),
|
|
354
|
+
(a) =>
|
|
355
|
+
s.bulkSet(a).pipe(
|
|
356
|
+
Effect.orDie,
|
|
357
|
+
Effect
|
|
358
|
+
// we delay extra here, so that initial creation between Companies/POs also have an interval between them.
|
|
359
|
+
.delay(Duration.millis(1100))
|
|
360
|
+
)
|
|
374
361
|
)
|
|
375
362
|
}
|
|
376
363
|
// Mark as imported
|
|
377
|
-
yield*
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
})
|
|
384
|
-
)
|
|
364
|
+
yield* Effect.promise(() =>
|
|
365
|
+
container.items.create({
|
|
366
|
+
_partitionKey: importedMarkerId,
|
|
367
|
+
id: importedMarkerId,
|
|
368
|
+
ttl: -1
|
|
369
|
+
})
|
|
385
370
|
)
|
|
386
371
|
}
|
|
387
372
|
return s
|
|
@@ -17,7 +17,7 @@ function makeDiskStoreInt<Id extends string, Encoded extends { id: Id }, R, E>(
|
|
|
17
17
|
defaultValues?: Partial<Encoded>
|
|
18
18
|
) {
|
|
19
19
|
type PM = PersistenceModelType<Encoded>
|
|
20
|
-
return Effect.gen(function*(
|
|
20
|
+
return Effect.gen(function*() {
|
|
21
21
|
if (namespace !== "primary") {
|
|
22
22
|
dir = dir + "/" + namespace
|
|
23
23
|
if (!fs.existsSync(dir)) {
|
|
@@ -68,18 +68,16 @@ function makeDiskStoreInt<Id extends string, Encoded extends { id: Id }, R, E>(
|
|
|
68
68
|
)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const store = yield*
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
defaultValues
|
|
79
|
-
)
|
|
71
|
+
const store = yield* makeMemoryStoreInt<Id, Encoded, R, E>(
|
|
72
|
+
name,
|
|
73
|
+
namespace,
|
|
74
|
+
!fs.existsSync(file)
|
|
75
|
+
? seed
|
|
76
|
+
: fsStore.get,
|
|
77
|
+
defaultValues
|
|
80
78
|
)
|
|
81
79
|
|
|
82
|
-
yield*
|
|
80
|
+
yield* store.all.pipe(Effect.flatMap(fsStore.setRaw))
|
|
83
81
|
|
|
84
82
|
const sem = Effect.unsafeMakeSemaphore(1)
|
|
85
83
|
const withPermit = sem.withPermits(1)
|
|
@@ -128,11 +126,11 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
128
126
|
seed?: Effect<Iterable<Encoded>, E, R>,
|
|
129
127
|
config?: StoreConfig<Encoded>
|
|
130
128
|
) =>
|
|
131
|
-
Effect.gen(function*(
|
|
129
|
+
Effect.gen(function*() {
|
|
132
130
|
const storesSem = Effect.unsafeMakeSemaphore(1)
|
|
133
|
-
const primary = yield*
|
|
131
|
+
const primary = yield* makeDiskStoreInt(prefix, "primary", dir, name, seed, config?.defaultValues)
|
|
134
132
|
const stores = new Map<string, Store<Encoded, Id>>([["primary", primary]])
|
|
135
|
-
const ctx = yield*
|
|
133
|
+
const ctx = yield* Effect.context<R>()
|
|
136
134
|
const getStore = !config?.allowNamespace
|
|
137
135
|
? Effect.succeed(primary)
|
|
138
136
|
: FiberRef.get(storeId).pipe(Effect.flatMap((namespace) => {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Array, Effect, FiberRef, flow, Option, Order, pipe, Ref, Struct } from "effect-app"
|
|
4
4
|
import type { NonEmptyReadonlyArray } from "effect-app"
|
|
5
5
|
import { get } from "effect-app/utils"
|
|
6
|
+
import { InfraLogger } from "../../logger.js"
|
|
6
7
|
import type { RequestContext } from "../../RequestContext.js"
|
|
7
8
|
import type { FilterArgs, PersistenceModelType, Store, StoreConfig } from "./service.js"
|
|
8
9
|
import { StoreMaker } from "./service.js"
|
|
@@ -62,7 +63,7 @@ export const storeId = FiberRef.unsafeMake("primary")
|
|
|
62
63
|
export const restoreFromRequestContext = (ctx: RequestContext) => FiberRef.set(storeId, ctx.namespace ?? "primary")
|
|
63
64
|
|
|
64
65
|
function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
|
|
65
|
-
return
|
|
66
|
+
return InfraLogger
|
|
66
67
|
.logDebug("mem query")
|
|
67
68
|
.pipe(Effect.annotateLogs({
|
|
68
69
|
filter: JSON.stringify(
|
|
@@ -85,9 +86,9 @@ export function makeMemoryStoreInt<Id extends string, Encoded extends { id: Id }
|
|
|
85
86
|
_defaultValues?: Partial<Encoded>
|
|
86
87
|
) {
|
|
87
88
|
type PM = PersistenceModelType<Encoded>
|
|
88
|
-
return Effect.gen(function*(
|
|
89
|
+
return Effect.gen(function*() {
|
|
89
90
|
const updateETag = makeUpdateETag(modelName)
|
|
90
|
-
const items_ = yield*
|
|
91
|
+
const items_ = yield* seed ?? Effect.sync(() => [])
|
|
91
92
|
const defaultValues = _defaultValues ?? {}
|
|
92
93
|
|
|
93
94
|
const items = new Map([...items_].map((_) => [_.id, { _etag: undefined, ...defaultValues, ..._ }] as const))
|
|
@@ -218,10 +219,10 @@ export const makeMemoryStore = () => ({
|
|
|
218
219
|
seed?: Effect<Iterable<Encoded>, E, R>,
|
|
219
220
|
config?: StoreConfig<Encoded>
|
|
220
221
|
) =>
|
|
221
|
-
Effect.gen(function*(
|
|
222
|
+
Effect.gen(function*() {
|
|
222
223
|
const storesSem = Effect.unsafeMakeSemaphore(1)
|
|
223
|
-
const primary = yield*
|
|
224
|
-
const ctx = yield*
|
|
224
|
+
const primary = yield* makeMemoryStoreInt<Id, Encoded, R, E>(modelName, "primary", seed, config?.defaultValues)
|
|
225
|
+
const ctx = yield* Effect.context<R>()
|
|
225
226
|
const stores = new Map([["primary", primary]])
|
|
226
227
|
const getStore = !config?.allowNamespace
|
|
227
228
|
? Effect.succeed(primary)
|
|
@@ -12,24 +12,20 @@ export const makeETag = <E extends PersistenceModelType<{}>>(
|
|
|
12
12
|
}) as any)
|
|
13
13
|
export const makeUpdateETag =
|
|
14
14
|
(type: string) => <E extends PersistenceModelType<{ id: string }>>(e: E, current: Option<E>) =>
|
|
15
|
-
Effect.gen(function*(
|
|
15
|
+
Effect.gen(function*() {
|
|
16
16
|
if (e._etag) {
|
|
17
|
-
yield*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
() => new OptimisticConcurrencyException({ type, id: e.id, current: "", found: e._etag })
|
|
21
|
-
)
|
|
17
|
+
yield* Effect.mapError(
|
|
18
|
+
current,
|
|
19
|
+
() => new OptimisticConcurrencyException({ type, id: e.id, current: "", found: e._etag })
|
|
22
20
|
)
|
|
23
21
|
}
|
|
24
22
|
if (Option.isSome(current) && current.value._etag !== e._etag) {
|
|
25
|
-
return yield*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
})
|
|
32
|
-
)
|
|
23
|
+
return yield* new OptimisticConcurrencyException({
|
|
24
|
+
type,
|
|
25
|
+
id: current.value.id,
|
|
26
|
+
current: current.value._etag,
|
|
27
|
+
found: e._etag
|
|
28
|
+
})
|
|
33
29
|
}
|
|
34
30
|
const newE = makeETag(e)
|
|
35
31
|
return newE
|