@effect-app/infra 3.10.0 → 4.0.0-beta.0
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 +11 -0
- package/_check.sh +3 -0
- package/dist/CUPS.d.ts +22 -12
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +28 -29
- package/dist/Emailer/Sendgrid.js +13 -12
- package/dist/Emailer/service.d.ts +3 -13
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/MainFiberSet.d.ts +18 -41
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +10 -10
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +13 -10
- package/dist/Model/Repository/internal/internal.d.ts +5 -5
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +52 -42
- package/dist/Model/Repository/legacy.d.ts +9 -9
- package/dist/Model/Repository/legacy.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +4 -4
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +1 -1
- package/dist/Model/Repository/service.d.ts +11 -11
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +17 -47
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository/validation.js +2 -2
- package/dist/Model/query/dsl.d.ts +22 -22
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/dsl.js +1 -1
- package/dist/Model/query/new-kid-interpreter.d.ts +1 -1
- package/dist/Model/query/new-kid-interpreter.js +7 -7
- package/dist/Operations.d.ts +22 -63
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +14 -14
- package/dist/OperationsRepo.d.ts +23 -7
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +4 -5
- package/dist/QueueMaker/SQLQueue.d.ts +6 -8
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +20 -24
- package/dist/QueueMaker/errors.js +1 -1
- package/dist/QueueMaker/memQueue.d.ts +2 -5
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +22 -26
- package/dist/QueueMaker/sbqueue.d.ts +2 -5
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +24 -28
- package/dist/RequestContext.d.ts +28 -41
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +4 -4
- package/dist/RequestFiberSet.d.ts +23 -50
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +14 -14
- package/dist/Store/ContextMapContainer.d.ts +4 -4
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +5 -5
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +21 -28
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +12 -16
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +25 -33
- package/dist/Store/index.js +2 -2
- package/dist/Store/service.d.ts +9 -34
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +4 -4
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +10 -2
- package/dist/adapters/SQL/Model.d.ts +106 -162
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +92 -130
- package/dist/adapters/ServiceBus.d.ts +13 -44
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +13 -15
- package/dist/adapters/cosmos-client.d.ts +7 -3
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +5 -4
- package/dist/adapters/logger.d.ts +1 -1
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +8 -21
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +4 -4
- package/dist/adapters/mongo-client.d.ts +6 -6
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +5 -4
- package/dist/adapters/redis-client.d.ts +14 -4
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +19 -18
- package/dist/api/ContextProvider.d.ts +10 -15
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +8 -8
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/codec.d.ts.map +1 -1
- package/dist/api/codec.js +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/auth.d.ts +3 -3
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +8 -8
- 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 +9 -9
- package/dist/api/internal/health.d.ts +1 -1
- package/dist/api/internal/health.d.ts.map +1 -1
- package/dist/api/internal/health.js +2 -2
- package/dist/api/layerUtils.d.ts +14 -14
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/middlewares.d.ts +0 -75
- package/dist/api/middlewares.d.ts.map +1 -1
- package/dist/api/middlewares.js +6 -51
- package/dist/api/reportError.js +4 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +6 -7
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +9 -13
- 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 +5 -4
- package/dist/api/routing/utils.d.ts +2 -2
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing/utils.js +10 -8
- package/dist/api/routing.d.ts +39 -37
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +17 -21
- package/dist/api/setupRequest.d.ts +4 -6
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +10 -9
- package/dist/arbs.d.ts +3 -3
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +2 -2
- package/dist/errorReporter.d.ts +1 -1
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +12 -12
- package/dist/fileUtil.d.ts +6 -6
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/logger/jsonLogger.d.ts.map +1 -1
- package/dist/logger/jsonLogger.js +19 -18
- package/dist/logger/logFmtLogger.d.ts.map +1 -1
- package/dist/logger/logFmtLogger.js +11 -13
- package/dist/logger/shared.d.ts +2 -2
- package/dist/logger/shared.d.ts.map +1 -1
- package/dist/logger/shared.js +7 -9
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.d.ts +2 -2
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +5 -5
- package/dist/test.d.ts +2 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +6 -24
- package/package.json +19 -22
- package/src/CUPS.ts +15 -14
- package/src/Emailer/Sendgrid.ts +15 -13
- package/src/Emailer/service.ts +3 -3
- package/src/MainFiberSet.ts +16 -12
- package/src/Model/Repository/ext.ts +18 -16
- package/src/Model/Repository/internal/internal.ts +80 -69
- package/src/Model/Repository/legacy.ts +9 -9
- package/src/Model/Repository/makeRepo.ts +5 -5
- package/src/Model/Repository/service.ts +12 -12
- package/src/Model/Repository/validation.ts +1 -1
- package/src/Model/query/dsl.ts +13 -13
- package/src/Model/query/new-kid-interpreter.ts +8 -8
- package/src/Operations.ts +17 -14
- package/src/OperationsRepo.ts +3 -4
- package/src/QueueMaker/SQLQueue.ts +86 -89
- package/src/QueueMaker/errors.ts +1 -1
- package/src/QueueMaker/memQueue.ts +90 -91
- package/src/QueueMaker/sbqueue.ts +90 -92
- package/src/RequestContext.ts +3 -3
- package/src/RequestFiberSet.ts +17 -15
- package/src/Store/ContextMapContainer.ts +4 -4
- package/src/Store/Cosmos.ts +20 -27
- package/src/Store/Disk.ts +13 -17
- package/src/Store/Memory.ts +28 -34
- package/src/Store/index.ts +1 -1
- package/src/Store/service.ts +4 -4
- package/src/Store/utils.ts +9 -5
- package/src/adapters/SQL/Model.ts +255 -268
- package/src/adapters/ServiceBus.ts +17 -20
- package/src/adapters/cosmos-client.ts +5 -5
- package/src/adapters/memQueue.ts +3 -3
- package/src/adapters/mongo-client.ts +5 -5
- package/src/adapters/redis-client.ts +25 -19
- package/src/api/ContextProvider.ts +24 -34
- package/src/api/codec.ts +1 -1
- package/src/api/internal/auth.ts +11 -13
- package/src/api/internal/events.ts +11 -11
- package/src/api/internal/health.ts +1 -1
- package/src/api/layerUtils.ts +20 -20
- package/src/api/middlewares.ts +0 -97
- package/src/api/reportError.ts +3 -3
- package/src/api/routing/middleware/RouterMiddleware.ts +5 -6
- package/src/api/routing/middleware/middleware.ts +13 -25
- package/src/api/routing/schema/jwt.ts +9 -7
- package/src/api/routing/utils.ts +12 -10
- package/src/api/routing.ts +77 -79
- package/src/api/setupRequest.ts +9 -8
- package/src/arbs.ts +3 -3
- package/src/errorReporter.ts +12 -12
- package/src/logger/jsonLogger.ts +18 -17
- package/src/logger/logFmtLogger.ts +10 -12
- package/src/logger/shared.ts +6 -8
- package/src/rateLimit.ts +7 -7
- package/src/test.ts +7 -29
- package/test/contextProvider.test.ts +77 -70
- package/test/controller.test.ts +51 -39
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/fixtures.d.ts +33 -81
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +9 -8
- 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/fixtures.ts +9 -7
- package/test/query.test.ts +49 -41
- package/test/rawQuery.test.ts +44 -40
- package/test/requires.test.ts +40 -31
- package/test/rpc-multi-middleware.test.ts +13 -14
- package/test/validateSample.test.ts +2 -2
- package/tsconfig.json +1 -27
- package/dist/api/internal/middlewares.d.ts +0 -15
- package/dist/api/internal/middlewares.d.ts.map +0 -1
- package/dist/api/internal/middlewares.js +0 -168
- package/src/api/internal/middlewares.ts +0 -279
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Cause, Tracer } from "effect"
|
|
2
2
|
import { Effect, Fiber, flow, S } from "effect-app"
|
|
3
|
+
import * as Q from "effect/Queue"
|
|
3
4
|
import { pretty } from "effect-app/utils"
|
|
4
5
|
import { MemQueue } from "../adapters/memQueue.js"
|
|
5
6
|
import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
|
|
6
7
|
import { InfraLogger } from "../logger.js"
|
|
7
8
|
import { reportNonInterruptedFailure, reportNonInterruptedFailureCause } from "./errors.js"
|
|
9
|
+
import type { NonEmptyReadonlyArray } from "effect-app"
|
|
8
10
|
import { type QueueBase, QueueMeta } from "./service.js"
|
|
9
11
|
|
|
10
12
|
export function makeMemQueue<
|
|
@@ -15,8 +17,8 @@ export function makeMemQueue<
|
|
|
15
17
|
>(
|
|
16
18
|
queueName: string,
|
|
17
19
|
queueDrainName: string,
|
|
18
|
-
schema: S.
|
|
19
|
-
drainSchema: S.
|
|
20
|
+
schema: S.Codec<Evt, EvtE>,
|
|
21
|
+
drainSchema: S.Codec<DrainEvt, DrainEvtE>
|
|
20
22
|
) {
|
|
21
23
|
return Effect.gen(function*() {
|
|
22
24
|
const mem = yield* MemQueue
|
|
@@ -25,108 +27,105 @@ export function makeMemQueue<
|
|
|
25
27
|
|
|
26
28
|
const wireSchema = S.Struct({ body: schema, meta: QueueMeta })
|
|
27
29
|
const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
|
|
28
|
-
const parseDrain = flow(S.
|
|
30
|
+
const parseDrain = flow(S.decodeUnknownEffect(drainW), Effect.orDie)
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
publish: (...messages) =>
|
|
32
|
-
|
|
33
|
-
.gen(function*() {
|
|
34
|
-
const requestContext = yield* getRequestContext
|
|
35
|
-
return yield* Effect
|
|
36
|
-
.forEach(messages, (m) =>
|
|
37
|
-
// we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
|
|
38
|
-
S.encode(wireSchema)({ body: m, meta: requestContext }).pipe(
|
|
39
|
-
Effect.orDie,
|
|
40
|
-
Effect
|
|
41
|
-
.andThen(JSON.stringify),
|
|
42
|
-
// .tap((msg) => info("Publishing Mem Message: " + utils.inspect(msg)))
|
|
43
|
-
Effect.flatMap((_) => q.offer(_))
|
|
44
|
-
), { discard: true })
|
|
45
|
-
})
|
|
32
|
+
const queue = {
|
|
33
|
+
publish: (...messages: NonEmptyReadonlyArray<Evt>) =>
|
|
34
|
+
getRequestContext
|
|
46
35
|
.pipe(
|
|
36
|
+
Effect.flatMap((requestContext) =>
|
|
37
|
+
Effect
|
|
38
|
+
.forEach(messages, (m) =>
|
|
39
|
+
// we JSON encode, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
|
|
40
|
+
S.encodeEffect(wireSchema)({ body: m, meta: requestContext }).pipe(
|
|
41
|
+
Effect.orDie,
|
|
42
|
+
Effect
|
|
43
|
+
.map(JSON.stringify),
|
|
44
|
+
// .tap((msg) => info("Publishing Mem Message: " + utils.inspect(msg)))
|
|
45
|
+
Effect.flatMap((_) => Q.offer(q, _))
|
|
46
|
+
), { discard: true })
|
|
47
|
+
),
|
|
47
48
|
Effect.withSpan("queue.publish: " + queueName, {
|
|
48
|
-
captureStackTrace: false,
|
|
49
49
|
kind: "producer",
|
|
50
50
|
attributes: { "message_tags": messages.map((_) => _._tag) }
|
|
51
|
-
})
|
|
51
|
+
}, { captureStackTrace: false })
|
|
52
52
|
),
|
|
53
53
|
drain: <DrainE, DrainR>(
|
|
54
54
|
handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
|
|
55
55
|
sessionId?: string
|
|
56
|
-
) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
.sync(() => JSON.parse(msg))
|
|
64
|
-
.pipe(
|
|
65
|
-
Effect.flatMap(parseDrain),
|
|
66
|
-
Effect.orDie,
|
|
67
|
-
Effect
|
|
68
|
-
.flatMap(({ body, meta }) => {
|
|
69
|
-
let effect = InfraLogger
|
|
70
|
-
.logDebug(`[${queueDrainName}] Processing incoming message`)
|
|
71
|
-
.pipe(
|
|
72
|
-
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
73
|
-
Effect.zipRight(handleEvent(body)),
|
|
74
|
-
silenceAndReportError,
|
|
75
|
-
(_) =>
|
|
76
|
-
setupRequestContextWithCustomSpan(
|
|
77
|
-
_,
|
|
78
|
-
meta,
|
|
79
|
-
`queue.drain: ${queueDrainName}.${body._tag}`,
|
|
80
|
-
{
|
|
81
|
-
captureStackTrace: false,
|
|
82
|
-
kind: "consumer",
|
|
83
|
-
attributes: {
|
|
84
|
-
"queue.name": queueDrainName,
|
|
85
|
-
"queue.sessionId": sessionId,
|
|
86
|
-
"queue.input": body
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
)
|
|
90
|
-
)
|
|
91
|
-
if (meta.span) {
|
|
92
|
-
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
93
|
-
}
|
|
94
|
-
return effect
|
|
95
|
-
})
|
|
96
|
-
)
|
|
97
|
-
return yield* qDrain
|
|
98
|
-
.take
|
|
56
|
+
) => {
|
|
57
|
+
const silenceAndReportError = reportNonInterruptedFailure({ name: "MemQueue.drain." + queueDrainName })
|
|
58
|
+
const reportError = reportNonInterruptedFailureCause({ name: "MemQueue.drain." + queueDrainName })
|
|
59
|
+
const processMessage = (msg: string) =>
|
|
60
|
+
// we JSON parse, because that is what the wire also does, and it reveals holes in e.g unknown encoders (Date->String)
|
|
61
|
+
Effect
|
|
62
|
+
.sync(() => JSON.parse(msg))
|
|
99
63
|
.pipe(
|
|
64
|
+
Effect.flatMap(parseDrain),
|
|
65
|
+
Effect.orDie,
|
|
100
66
|
Effect
|
|
101
|
-
.flatMap((
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
67
|
+
.flatMap(({ body, meta }) => {
|
|
68
|
+
let effect = InfraLogger
|
|
69
|
+
.logDebug(`[${queueDrainName}] Processing incoming message`)
|
|
70
|
+
.pipe(
|
|
71
|
+
Effect.annotateLogs({ body: pretty(body), meta: pretty(meta) }),
|
|
72
|
+
Effect.andThen(handleEvent(body)),
|
|
73
|
+
silenceAndReportError,
|
|
74
|
+
(_) =>
|
|
75
|
+
setupRequestContextWithCustomSpan(
|
|
76
|
+
_,
|
|
77
|
+
meta,
|
|
78
|
+
`queue.drain: ${queueDrainName}.${body._tag}`,
|
|
79
|
+
{
|
|
80
|
+
captureStackTrace: false,
|
|
81
|
+
kind: "consumer",
|
|
82
|
+
attributes: {
|
|
83
|
+
"queue.name": queueDrainName,
|
|
84
|
+
"queue.sessionId": sessionId,
|
|
85
|
+
"queue.input": body
|
|
86
|
+
}
|
|
87
|
+
}
|
|
114
88
|
)
|
|
115
|
-
: Effect.void
|
|
116
89
|
)
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
"queue.type": "mem",
|
|
123
|
-
"queue.name": queueDrainName,
|
|
124
|
-
"queue.sessionId": sessionId
|
|
125
|
-
}
|
|
126
|
-
}),
|
|
127
|
-
Effect.forever
|
|
90
|
+
if (meta.span) {
|
|
91
|
+
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
92
|
+
}
|
|
93
|
+
return effect
|
|
94
|
+
})
|
|
128
95
|
)
|
|
129
|
-
|
|
130
|
-
|
|
96
|
+
return Q.take(qDrain)
|
|
97
|
+
.pipe(
|
|
98
|
+
Effect
|
|
99
|
+
.flatMap((x) =>
|
|
100
|
+
processMessage(x).pipe(
|
|
101
|
+
Effect.uninterruptible,
|
|
102
|
+
Effect.forkChild,
|
|
103
|
+
Effect.flatMap(Fiber.join),
|
|
104
|
+
// normally a failed item would be returned to the queue and retried up to X times.
|
|
105
|
+
Effect.flatMap((_) =>
|
|
106
|
+
_._tag === "Failure" && !Cause.hasInterruptsOnly(_.cause)
|
|
107
|
+
? Q.offer(qDrain, x).pipe(
|
|
108
|
+
// TODO: retry count tracking and max retries.
|
|
109
|
+
Effect.delay("5 seconds"),
|
|
110
|
+
Effect.tapCause(reportError),
|
|
111
|
+
Effect.forkDetach
|
|
112
|
+
)
|
|
113
|
+
: Effect.void
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
silenceAndReportError,
|
|
118
|
+
Effect.withSpan(`queue.drain: ${queueDrainName}`, {
|
|
119
|
+
attributes: {
|
|
120
|
+
"queue.type": "mem",
|
|
121
|
+
"queue.name": queueDrainName,
|
|
122
|
+
"queue.sessionId": sessionId
|
|
123
|
+
}
|
|
124
|
+
}),
|
|
125
|
+
Effect.forever
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return queue as QueueBase<Evt, DrainEvt>
|
|
131
130
|
})
|
|
132
131
|
}
|
|
@@ -6,6 +6,7 @@ import { Receiver, Sender } from "../adapters/ServiceBus.js"
|
|
|
6
6
|
import { getRequestContext, setupRequestContextWithCustomSpan } from "../api/setupRequest.js"
|
|
7
7
|
import { InfraLogger } from "../logger.js"
|
|
8
8
|
import { reportNonInterruptedFailure, reportNonInterruptedFailureCause, reportQueueError } from "./errors.js"
|
|
9
|
+
import type { NonEmptyReadonlyArray } from "effect-app"
|
|
9
10
|
import { type QueueBase, QueueMeta } from "./service.js"
|
|
10
11
|
|
|
11
12
|
export function makeServiceBusQueue<
|
|
@@ -14,15 +15,15 @@ export function makeServiceBusQueue<
|
|
|
14
15
|
EvtE,
|
|
15
16
|
DrainEvtE
|
|
16
17
|
>(
|
|
17
|
-
schema: S.
|
|
18
|
-
drainSchema: S.
|
|
18
|
+
schema: S.Codec<Evt, EvtE>,
|
|
19
|
+
drainSchema: S.Codec<DrainEvt, DrainEvtE>
|
|
19
20
|
) {
|
|
20
21
|
const wireSchema = S.Struct({
|
|
21
22
|
body: schema,
|
|
22
23
|
meta: QueueMeta
|
|
23
24
|
})
|
|
24
25
|
const drainW = S.Struct({ body: drainSchema, meta: QueueMeta })
|
|
25
|
-
const parseDrain = flow(S.
|
|
26
|
+
const parseDrain = flow(S.decodeUnknownEffect(drainW), Effect.orDie)
|
|
26
27
|
|
|
27
28
|
return Effect.gen(function*() {
|
|
28
29
|
const sender = yield* Sender
|
|
@@ -34,103 +35,100 @@ export function makeServiceBusQueue<
|
|
|
34
35
|
// This will make sure that the host receives the error (MainFiberSet.join), who will then interrupt everything and commence a shutdown and restart of app
|
|
35
36
|
// const deferred = yield* Deferred.make<never, ServiceBusError | Error>()
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
const queue = {
|
|
38
39
|
drain: <DrainE, DrainR>(
|
|
39
40
|
handleEvent: (ks: DrainEvt) => Effect.Effect<void, DrainE, DrainR>,
|
|
40
41
|
sessionId?: string
|
|
41
|
-
) =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
42
|
+
) => {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
function processMessage(messageBody: any) {
|
|
45
|
+
return Effect
|
|
46
|
+
.sync(() => JSON.parse(messageBody))
|
|
47
|
+
.pipe(
|
|
48
|
+
Effect.flatMap((x) => parseDrain(x)),
|
|
49
|
+
Effect.orDie,
|
|
50
|
+
Effect
|
|
51
|
+
.flatMap(({ body, meta }) => {
|
|
52
|
+
let effect = InfraLogger
|
|
53
|
+
.logDebug(`[${receiver.name}] Processing incoming message`)
|
|
54
|
+
.pipe(
|
|
55
|
+
Effect.annotateLogs({
|
|
56
|
+
body: pretty(body),
|
|
57
|
+
meta: pretty(meta)
|
|
58
|
+
}),
|
|
59
|
+
Effect.andThen(handleEvent(body)),
|
|
60
|
+
Effect.orDie
|
|
61
|
+
)
|
|
62
|
+
// we silenceAndReportError here, so that the error is reported, and moves into the Exit.
|
|
63
|
+
.pipe(
|
|
64
|
+
silenceAndReportError,
|
|
65
|
+
(_) =>
|
|
66
|
+
setupRequestContextWithCustomSpan(
|
|
67
|
+
_,
|
|
68
|
+
meta,
|
|
69
|
+
`queue.drain: ${receiver.name}${sessionId ? `#${sessionId}` : ""}.${body._tag}`,
|
|
70
|
+
{
|
|
71
|
+
captureStackTrace: false,
|
|
72
|
+
kind: "consumer",
|
|
73
|
+
attributes: {
|
|
74
|
+
"queue.name": receiver.name,
|
|
75
|
+
"queue.sessionId": sessionId,
|
|
76
|
+
"queue.input": body
|
|
77
|
+
}
|
|
78
|
+
}
|
|
62
79
|
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"queue.sessionId": sessionId,
|
|
77
|
-
"queue.input": body
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
)
|
|
81
|
-
)
|
|
82
|
-
if (meta.span) {
|
|
83
|
-
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
84
|
-
}
|
|
85
|
-
return effect
|
|
86
|
-
}),
|
|
87
|
-
Effect
|
|
88
|
-
// we reportError here, so that we report the error only, and keep flowing
|
|
89
|
-
.tapErrorCause(reportError),
|
|
90
|
-
// we still need to flatten the Exit.
|
|
91
|
-
Effect.flatMap((_) => _)
|
|
92
|
-
)
|
|
93
|
-
}
|
|
80
|
+
)
|
|
81
|
+
if (meta.span) {
|
|
82
|
+
effect = Effect.withParentSpan(effect, Tracer.externalSpan(meta.span))
|
|
83
|
+
}
|
|
84
|
+
return effect
|
|
85
|
+
}),
|
|
86
|
+
Effect
|
|
87
|
+
// we reportError here, so that we report the error only, and keep flowing
|
|
88
|
+
.tapCause(reportError),
|
|
89
|
+
// we still need to flatten the Exit.
|
|
90
|
+
Effect.flatMap((_) => _)
|
|
91
|
+
)
|
|
92
|
+
}
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
})
|
|
94
|
+
return receiver
|
|
95
|
+
.subscribe({
|
|
96
|
+
processMessage: (x) => processMessage(x.body).pipe(Effect.uninterruptible),
|
|
97
|
+
processError: (err) => reportQueueError(Cause.fail(err.error))
|
|
98
|
+
// Deferred.completeWith(
|
|
99
|
+
// deferred,
|
|
100
|
+
// reportFatalQueueError(Cause.fail(err.error))
|
|
101
|
+
// .pipe(Effect.andThen(Effect.fail(err.error)))
|
|
102
|
+
// )
|
|
103
|
+
}, sessionId)
|
|
106
104
|
// .pipe(Effect.andThen(Deferred.await(deferred).pipe(Effect.orDie))),
|
|
107
105
|
.pipe(
|
|
108
106
|
Effect.andThen(Effect.never)
|
|
109
|
-
)
|
|
107
|
+
)
|
|
108
|
+
},
|
|
110
109
|
|
|
111
|
-
publish: (...messages) =>
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
captureStackTrace: false
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
} satisfies QueueBase<Evt, DrainEvt>
|
|
110
|
+
publish: (...messages: NonEmptyReadonlyArray<Evt>) =>
|
|
111
|
+
getRequestContext
|
|
112
|
+
.pipe(
|
|
113
|
+
Effect.flatMap((requestContext) =>
|
|
114
|
+
Effect.forEach(messages, (m) =>
|
|
115
|
+
S.encodeEffect(wireSchema)({
|
|
116
|
+
body: m,
|
|
117
|
+
meta: requestContext
|
|
118
|
+
}).pipe(Effect.orDie, Effect.map((encoded) => ({
|
|
119
|
+
body: JSON.stringify(encoded),
|
|
120
|
+
messageId: m.id, /* correllationid: requestId */
|
|
121
|
+
contentType: "application/json",
|
|
122
|
+
sessionId: "sessionId" in m ? m.sessionId as string : undefined as unknown as string // TODO: optional
|
|
123
|
+
}))))
|
|
124
|
+
.pipe(Effect.flatMap((msgs) => sender.sendMessages(msgs)))
|
|
125
|
+
),
|
|
126
|
+
Effect.withSpan("queue.publish: " + sender.name, {
|
|
127
|
+
kind: "producer",
|
|
128
|
+
attributes: { "message_tags": messages.map((_) => _._tag) }
|
|
129
|
+
}, { captureStackTrace: false })
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
return queue as QueueBase<Evt, DrainEvt>
|
|
135
133
|
})
|
|
136
134
|
}
|
package/src/RequestContext.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S, ServiceMap } from "effect-app"
|
|
2
2
|
import { UserProfileId } from "effect-app/ids"
|
|
3
3
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
4
4
|
|
|
5
5
|
export const Locale = S.Literal("en", "de")
|
|
6
6
|
export type Locale = typeof Locale.Type
|
|
7
7
|
|
|
8
|
-
export class LocaleRef extends
|
|
8
|
+
export class LocaleRef extends ServiceMap.Reference("Locale", { defaultValue: (): Locale => "en" }) {}
|
|
9
9
|
|
|
10
10
|
export class RequestContext extends S.ExtendedClass<
|
|
11
11
|
RequestContext,
|
|
@@ -23,7 +23,7 @@ export class RequestContext extends S.ExtendedClass<
|
|
|
23
23
|
/** @deprecated */
|
|
24
24
|
userProfile: S.optional(S.Struct({ sub: UserProfileId })) //
|
|
25
25
|
}) {
|
|
26
|
-
// static Tag =
|
|
26
|
+
// static Tag = ServiceMap.Tag<RequestContext>()
|
|
27
27
|
|
|
28
28
|
static toMonitoring(this: void, self: RequestContext) {
|
|
29
29
|
return {
|
package/src/RequestFiberSet.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Fiber, FiberSet, Layer, ServiceMap, type Tracer } from "effect-app"
|
|
3
3
|
import { reportRequestError, reportUnknownRequestError } from "./api/reportError.js"
|
|
4
4
|
import { InfraLogger } from "./logger.js"
|
|
5
5
|
|
|
6
6
|
const getRootParentSpan = Effect.gen(function*() {
|
|
7
7
|
let span: Tracer.AnySpan | null = yield* Effect.currentSpan.pipe(
|
|
8
|
-
Effect.catchTag("
|
|
8
|
+
Effect.catchTag("NoSuchElementError", () => Effect.succeed(null))
|
|
9
9
|
)
|
|
10
10
|
if (!span) return span
|
|
11
|
-
while (span._tag === "Span" &&
|
|
12
|
-
span = span.parent
|
|
11
|
+
while (span._tag === "Span" && span.parent !== undefined) {
|
|
12
|
+
span = span.parent
|
|
13
13
|
}
|
|
14
14
|
return span
|
|
15
15
|
})
|
|
@@ -19,17 +19,17 @@ export const setRootParentSpan = <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
|
19
19
|
|
|
20
20
|
const make = Effect.gen(function*() {
|
|
21
21
|
const set = yield* FiberSet.make<any, any>()
|
|
22
|
-
const add = (...fibers: Fiber.
|
|
23
|
-
Effect.sync(() => fibers.forEach((_) => FiberSet.
|
|
24
|
-
const addAll = (fibers: readonly Fiber.
|
|
25
|
-
Effect.sync(() => fibers.forEach((_) => FiberSet.
|
|
22
|
+
const add = (...fibers: Fiber.Fiber<any, any>[]) =>
|
|
23
|
+
Effect.sync(() => fibers.forEach((_) => FiberSet.addUnsafe(set, _)))
|
|
24
|
+
const addAll = (fibers: readonly Fiber.Fiber<any, any>[]) =>
|
|
25
|
+
Effect.sync(() => fibers.forEach((_) => FiberSet.addUnsafe(set, _)))
|
|
26
26
|
const join = FiberSet.size(set).pipe(
|
|
27
27
|
Effect.andThen((count) => InfraLogger.logInfo(`Joining ${count} current fibers on the RequestFiberSet`)),
|
|
28
28
|
Effect.andThen(FiberSet.join(set))
|
|
29
29
|
)
|
|
30
30
|
const run = FiberSet.run(set)
|
|
31
31
|
const register = <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
32
|
-
self.pipe(Effect.
|
|
32
|
+
self.pipe(Effect.forkChild, Effect.tap(add), Effect.andThen(Fiber.join))
|
|
33
33
|
|
|
34
34
|
// const waitUntilEmpty = Effect.gen(function*() {
|
|
35
35
|
// const currentSize = yield* FiberSet.size(set)
|
|
@@ -92,12 +92,14 @@ const make = Effect.gen(function*() {
|
|
|
92
92
|
* Whenever you fork a fiber for a Request, and you want to prevent dependent services to close prematurely on interruption,
|
|
93
93
|
* like the ServiceBus Sender, you should register these fibers in this FiberSet.
|
|
94
94
|
*/
|
|
95
|
-
export class RequestFiberSet extends
|
|
96
|
-
static readonly Live = this.
|
|
97
|
-
static readonly register = <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
98
|
-
|
|
95
|
+
export class RequestFiberSet extends ServiceMap.Service<RequestFiberSet>()("RequestFiberSet", { make }) {
|
|
96
|
+
static readonly Live = Layer.effect(this, this.make)
|
|
97
|
+
static readonly register = <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
98
|
+
this.asEffect().pipe(Effect.andThen((_) => _.register(self)))
|
|
99
|
+
static readonly run = <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
100
|
+
this.asEffect().pipe(Effect.andThen((_) => _.run(self)))
|
|
99
101
|
static readonly forkDaemonReport = <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
100
|
-
this.
|
|
102
|
+
this.asEffect().pipe(Effect.andThen((_) => _.forkDaemonReport(self)))
|
|
101
103
|
static readonly forkDaemonReportUnexpected = <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
102
|
-
this.
|
|
104
|
+
this.asEffect().pipe(Effect.andThen((_) => _.forkDaemonReportUnexpected(self)))
|
|
103
105
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Data, Effect, Layer, ServiceMap } from "effect-app"
|
|
2
2
|
import { ContextMap } from "./service.js"
|
|
3
3
|
|
|
4
4
|
// TODO: we have to create a new contextmap on every request.
|
|
@@ -7,14 +7,14 @@ import { ContextMap } from "./service.js"
|
|
|
7
7
|
// we can call another start after startup. but it would be even better if we could Die on accessing rootmap
|
|
8
8
|
// we could also make the ContextMap optional, and when missing, issue a warning instead?
|
|
9
9
|
|
|
10
|
-
export class ContextMapContainer extends
|
|
10
|
+
export class ContextMapContainer extends ServiceMap.Reference("ContextMapContainer", {
|
|
11
11
|
defaultValue: (): ContextMap | "root" => "root"
|
|
12
12
|
}) {
|
|
13
|
-
static readonly layer = Layer.effect(this, ContextMap.make)
|
|
13
|
+
static readonly layer = Layer.effect(this, ContextMap.make.pipe(Effect.map(ContextMap.of)))
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export class ContextMapNotStartedError extends Data.TaggedError("ContextMapNotStartedError") {}
|
|
17
17
|
|
|
18
|
-
export const getContextMap = ContextMapContainer.pipe(
|
|
18
|
+
export const getContextMap = ContextMapContainer.asEffect().pipe(
|
|
19
19
|
Effect.filterOrFail((_) => _ !== "root", () => new ContextMapNotStartedError())
|
|
20
20
|
)
|