@effect-app/infra 4.0.0-beta.7 → 4.0.0-beta.70
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 +447 -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/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/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 +5 -5
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +4 -4
- 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 +3 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +3 -3
- package/dist/Store/Cosmos.js +1 -1
- 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/service.d.ts +3 -3
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +4 -4
- 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.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/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.map +1 -1
- package/dist/api/routing.js +1 -1
- 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 +28 -24
- package/package.json +16 -16
- 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 +3 -3
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +2 -2
- package/src/Store/Cosmos.ts +10 -10
- package/src/Store/Disk.ts +2 -3
- package/src/Store/Memory.ts +4 -6
- package/src/Store/service.ts +3 -3
- 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 +5 -4
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/schema/jwt.ts +2 -3
- package/src/api/routing.ts +4 -3
- 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 +9 -7
- 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 +16 -7
- 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/fixtures.ts +10 -8
- package/test/query.test.ts +156 -10
- package/test/rawQuery.test.ts +19 -17
- package/test/requires.test.ts +6 -5
- package/test/rpc-multi-middleware.test.ts +73 -4
- package/tsconfig.json +0 -1
package/test/query.test.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* eslint-disable unused-imports/no-unused-vars */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
-
import {
|
|
4
|
+
import { SchemaTransformation } from "effect"
|
|
5
|
+
import { Context, Effect, flow, Layer, Option, pipe, S, Struct } from "effect-app"
|
|
5
6
|
import { inspect } from "util"
|
|
6
7
|
import { expect, expectTypeOf, it } from "vitest"
|
|
7
8
|
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
@@ -11,7 +12,7 @@ import { memFilter, MemoryStoreLive } from "../src/Store/Memory.js"
|
|
|
11
12
|
import { SomeService } from "./fixtures.js"
|
|
12
13
|
|
|
13
14
|
const str = S.Struct({ _tag: S.Literal("string"), value: S.String })
|
|
14
|
-
const num = S.Struct({ _tag: S.Literal("number"), value: S.
|
|
15
|
+
const num = S.Struct({ _tag: S.Literal("number"), value: S.Finite })
|
|
15
16
|
const someUnion = S.Union([str, num])
|
|
16
17
|
|
|
17
18
|
export class Something extends S.Class<Something>("Something")({
|
|
@@ -99,7 +100,7 @@ it("works", () => {
|
|
|
99
100
|
})
|
|
100
101
|
|
|
101
102
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
102
|
-
class SomethingRepo extends
|
|
103
|
+
class SomethingRepo extends Context.Service<SomethingRepo>()("SomethingRepo", {
|
|
103
104
|
make: Effect.gen(function*() {
|
|
104
105
|
return yield* makeRepo("Something", Something, {})
|
|
105
106
|
})
|
|
@@ -156,8 +157,6 @@ it("works with repo", () =>
|
|
|
156
157
|
|
|
157
158
|
expectTypeOf(smtArr).toEqualTypeOf<readonly Something[]>()
|
|
158
159
|
|
|
159
|
-
console.log(" $$$$$$")
|
|
160
|
-
console.log(Struct.pick(["id", "displayName"]))
|
|
161
160
|
expect(q1).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["id", "displayName"])))
|
|
162
161
|
expect(q2).toEqual(items.slice(0, 2).toReversed().map(Struct.pick(["displayName"])))
|
|
163
162
|
})
|
|
@@ -521,8 +520,8 @@ it(
|
|
|
521
520
|
const schema = S.Struct({
|
|
522
521
|
id: S.String,
|
|
523
522
|
createdAt: S.Date.pipe(
|
|
524
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
525
|
-
S.withConstructorDefault(() =>
|
|
523
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
524
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
526
525
|
)
|
|
527
526
|
})
|
|
528
527
|
const repo = yield* makeRepo(
|
|
@@ -534,8 +533,8 @@ it(
|
|
|
534
533
|
const outputSchema = S.Struct({
|
|
535
534
|
id: S.Literal("123"),
|
|
536
535
|
createdAt: S.Date.pipe(
|
|
537
|
-
S.withDecodingDefault(() => new Date().toISOString()),
|
|
538
|
-
S.withConstructorDefault(() =>
|
|
536
|
+
S.withDecodingDefault(Effect.sync(() => new Date().toISOString())),
|
|
537
|
+
S.withConstructorDefault(Effect.sync(() => new Date()))
|
|
539
538
|
)
|
|
540
539
|
})
|
|
541
540
|
|
|
@@ -674,7 +673,7 @@ it("remove null from one constituent of a tagged union", () =>
|
|
|
674
673
|
|
|
675
674
|
class BB extends S.Class<BB>("BB")({
|
|
676
675
|
id: S.Literal("BB"),
|
|
677
|
-
b: S.NullOr(S.
|
|
676
|
+
b: S.NullOr(S.Finite)
|
|
678
677
|
}) {}
|
|
679
678
|
|
|
680
679
|
type Union = AA | BB
|
|
@@ -984,6 +983,153 @@ it("refine nested union", () =>
|
|
|
984
983
|
})
|
|
985
984
|
.pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
986
985
|
|
|
986
|
+
it("find with transformed id", () =>
|
|
987
|
+
Effect
|
|
988
|
+
.gen(function*() {
|
|
989
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
990
|
+
|
|
991
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
992
|
+
configuratorId: ConfiguratorId,
|
|
993
|
+
label: S.NonEmptyString50
|
|
994
|
+
}) {}
|
|
995
|
+
|
|
996
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
997
|
+
S.decodeTo(
|
|
998
|
+
S.toType(PreconfigurationId),
|
|
999
|
+
SchemaTransformation.transformOrFail({
|
|
1000
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1001
|
+
const values = value.split("_")
|
|
1002
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1003
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(values.join("_"))
|
|
1004
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1005
|
+
}),
|
|
1006
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1007
|
+
})
|
|
1008
|
+
),
|
|
1009
|
+
S.revealCodec
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
const Preconfiguration = S.Struct({
|
|
1013
|
+
id: PreconfigurationIdFromString,
|
|
1014
|
+
name: S.String
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, { idKey: "id" as const })
|
|
1018
|
+
|
|
1019
|
+
const id = new PreconfigurationId({
|
|
1020
|
+
configuratorId: S.NonEmptyString255("myConfigurator"),
|
|
1021
|
+
label: S.NonEmptyString50("myLabel")
|
|
1022
|
+
})
|
|
1023
|
+
const item = { id, name: "test preconfig" }
|
|
1024
|
+
|
|
1025
|
+
yield* repo.saveAndPublish([item])
|
|
1026
|
+
|
|
1027
|
+
const found = yield* repo.find(id)
|
|
1028
|
+
expect(Option.isSome(found)).toBe(true)
|
|
1029
|
+
expect(Option.getOrThrow(found).name).toBe("test preconfig")
|
|
1030
|
+
expect(Option.getOrThrow(found).id).toEqual(id)
|
|
1031
|
+
|
|
1032
|
+
const notFound = yield* repo.find(
|
|
1033
|
+
new PreconfigurationId({
|
|
1034
|
+
configuratorId: S.NonEmptyString255("other"),
|
|
1035
|
+
label: S.NonEmptyString50("nope")
|
|
1036
|
+
})
|
|
1037
|
+
)
|
|
1038
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1039
|
+
})
|
|
1040
|
+
.pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
1041
|
+
|
|
1042
|
+
it("find with transformed id in tagged union", () =>
|
|
1043
|
+
Effect
|
|
1044
|
+
.gen(function*() {
|
|
1045
|
+
const ConfiguratorId = S.NonEmptyString255
|
|
1046
|
+
|
|
1047
|
+
class PreconfigurationId extends S.Class<PreconfigurationId>("PreconfigurationId")({
|
|
1048
|
+
configuratorId: ConfiguratorId,
|
|
1049
|
+
label: S.NonEmptyString50
|
|
1050
|
+
}) {}
|
|
1051
|
+
|
|
1052
|
+
const PreconfigurationIdFromString = S.NonEmptyString255.pipe(
|
|
1053
|
+
S.decodeTo(
|
|
1054
|
+
S.toType(PreconfigurationId),
|
|
1055
|
+
SchemaTransformation.transformOrFail({
|
|
1056
|
+
decode: Effect.fnUntraced(function*(value) {
|
|
1057
|
+
const values = value.split("_")
|
|
1058
|
+
const label = yield* S.SchemaParser.decodeUnknownEffect(S.NonEmptyString50)(values.pop())
|
|
1059
|
+
const configuratorId = yield* S.SchemaParser.decodeUnknownEffect(ConfiguratorId)(values.join("_"))
|
|
1060
|
+
return new PreconfigurationId({ configuratorId, label })
|
|
1061
|
+
}),
|
|
1062
|
+
encode: (id) => Effect.succeed(S.NonEmptyString255(`${id.configuratorId}_${id.label}`))
|
|
1063
|
+
})
|
|
1064
|
+
),
|
|
1065
|
+
S.revealCodec
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
class Draft extends S.TaggedClass<Draft>()("Draft", {
|
|
1069
|
+
id: PreconfigurationIdFromString,
|
|
1070
|
+
name: S.String
|
|
1071
|
+
}) {}
|
|
1072
|
+
|
|
1073
|
+
class Published extends S.TaggedClass<Published>()("Published", {
|
|
1074
|
+
id: PreconfigurationIdFromString,
|
|
1075
|
+
name: S.String,
|
|
1076
|
+
publishedAt: S.String
|
|
1077
|
+
}) {}
|
|
1078
|
+
|
|
1079
|
+
class Archived extends S.TaggedClass<Archived>()("Archived", {
|
|
1080
|
+
id: PreconfigurationIdFromString,
|
|
1081
|
+
name: S.String,
|
|
1082
|
+
archivedAt: S.String
|
|
1083
|
+
}) {}
|
|
1084
|
+
|
|
1085
|
+
const Preconfiguration = S.Union([Draft, Published, Archived])
|
|
1086
|
+
|
|
1087
|
+
const repo = yield* makeRepo("Preconfiguration", Preconfiguration, {})
|
|
1088
|
+
|
|
1089
|
+
const id1 = new PreconfigurationId({
|
|
1090
|
+
configuratorId: S.NonEmptyString255("conf1"),
|
|
1091
|
+
label: S.NonEmptyString50("draft1")
|
|
1092
|
+
})
|
|
1093
|
+
const id2 = new PreconfigurationId({
|
|
1094
|
+
configuratorId: S.NonEmptyString255("conf2"),
|
|
1095
|
+
label: S.NonEmptyString50("pub1")
|
|
1096
|
+
})
|
|
1097
|
+
const id3 = new PreconfigurationId({
|
|
1098
|
+
configuratorId: S.NonEmptyString255("conf3"),
|
|
1099
|
+
label: S.NonEmptyString50("arch1")
|
|
1100
|
+
})
|
|
1101
|
+
|
|
1102
|
+
const draft = new Draft({ id: id1, name: "my draft" })
|
|
1103
|
+
const published = new Published({ id: id2, name: "my published", publishedAt: "2024-01-01" })
|
|
1104
|
+
const archived = new Archived({ id: id3, name: "my archived", archivedAt: "2024-06-01" })
|
|
1105
|
+
|
|
1106
|
+
yield* repo.saveAndPublish([draft, published, archived])
|
|
1107
|
+
|
|
1108
|
+
// find each by their PreconfigurationId instance
|
|
1109
|
+
const foundDraft = yield* repo.find(id1)
|
|
1110
|
+
expect(Option.isSome(foundDraft)).toBe(true)
|
|
1111
|
+
expect(Option.getOrThrow(foundDraft)._tag).toBe("Draft")
|
|
1112
|
+
expect(Option.getOrThrow(foundDraft).name).toBe("my draft")
|
|
1113
|
+
|
|
1114
|
+
const foundPublished = yield* repo.find(id2)
|
|
1115
|
+
expect(Option.isSome(foundPublished)).toBe(true)
|
|
1116
|
+
expect(Option.getOrThrow(foundPublished)._tag).toBe("Published")
|
|
1117
|
+
|
|
1118
|
+
const foundArchived = yield* repo.find(id3)
|
|
1119
|
+
expect(Option.isSome(foundArchived)).toBe(true)
|
|
1120
|
+
expect(Option.getOrThrow(foundArchived)._tag).toBe("Archived")
|
|
1121
|
+
|
|
1122
|
+
// not found
|
|
1123
|
+
const notFound = yield* repo.find(
|
|
1124
|
+
new PreconfigurationId({
|
|
1125
|
+
configuratorId: S.NonEmptyString255("nope"),
|
|
1126
|
+
label: S.NonEmptyString50("nope")
|
|
1127
|
+
})
|
|
1128
|
+
)
|
|
1129
|
+
expect(Option.isNone(notFound)).toBe(true)
|
|
1130
|
+
})
|
|
1131
|
+
.pipe(Effect.provide(MemoryStoreLive), setupRequestContextFromCurrent(), Effect.runPromise))
|
|
1132
|
+
|
|
987
1133
|
it("refine union with nested union", () =>
|
|
988
1134
|
Effect
|
|
989
1135
|
.gen(function*() {
|
package/test/rawQuery.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
-
import { Array, Config, Effect, flow, Layer, ManagedRuntime, Redacted, References, Result, S,
|
|
2
|
+
import { Array, Config, Effect, flow, Layer, ManagedRuntime, Redacted, References, Result, S, Context } from "effect-app"
|
|
3
3
|
import { LogLevels } from "effect-app/utils"
|
|
4
4
|
import { setupRequestContextFromCurrent } from "../src/api/setupRequest.js"
|
|
5
5
|
import { and, or, project, where, whereEvery, whereSome } from "../src/Model/query.js"
|
|
@@ -24,7 +24,7 @@ class Something extends S.Class<Something>("Something")({
|
|
|
24
24
|
id: S.String,
|
|
25
25
|
name: S.String,
|
|
26
26
|
description: S.String,
|
|
27
|
-
items: S.Array(S.Struct({ id: S.String, value: S.
|
|
27
|
+
items: S.Array(S.Struct({ id: S.String, value: S.Finite, description: S.String }))
|
|
28
28
|
}) {}
|
|
29
29
|
|
|
30
30
|
const items = [
|
|
@@ -49,7 +49,7 @@ const items = [
|
|
|
49
49
|
]
|
|
50
50
|
|
|
51
51
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
52
|
-
class SomethingRepo extends
|
|
52
|
+
class SomethingRepo extends Context.Service<SomethingRepo>()(
|
|
53
53
|
"SomethingRepo",
|
|
54
54
|
{
|
|
55
55
|
make: Effect.gen(function*() {
|
|
@@ -83,21 +83,23 @@ class SomethingRepo extends ServiceMap.Service<SomethingRepo>()(
|
|
|
83
83
|
.layer
|
|
84
84
|
.pipe(
|
|
85
85
|
Layer.provide(
|
|
86
|
-
Effect
|
|
87
|
-
|
|
88
|
-
Config.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
Effect
|
|
87
|
+
.gen(function*() {
|
|
88
|
+
const url = yield* Config.redacted("STORAGE_URL").pipe(
|
|
89
|
+
Config.withDefault(
|
|
90
|
+
Redacted.make(
|
|
91
|
+
// the emulator doesn't implement array projections :/ so you need an actual cloud instance!
|
|
92
|
+
"AccountEndpoint=http://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
|
93
|
+
)
|
|
92
94
|
)
|
|
93
95
|
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
return CosmosStoreLayer({
|
|
97
|
+
dbName: "test",
|
|
98
|
+
prefix: "",
|
|
99
|
+
url
|
|
100
|
+
})
|
|
99
101
|
})
|
|
100
|
-
|
|
102
|
+
.pipe(Layer.unwrap)
|
|
101
103
|
)
|
|
102
104
|
)
|
|
103
105
|
}
|
|
@@ -107,7 +109,7 @@ describe("select first-level array fields", () => {
|
|
|
107
109
|
.gen(function*() {
|
|
108
110
|
const repo = yield* SomethingRepo
|
|
109
111
|
|
|
110
|
-
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.
|
|
112
|
+
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
|
|
111
113
|
|
|
112
114
|
// ok crazy lol, "value" is a reserved word in CosmosDB, so we have to use t["value"] as a field name instead of t.value
|
|
113
115
|
const items = yield* repo.queryRaw(projected, {
|
|
@@ -159,7 +161,7 @@ describe("select first-level array fields", () => {
|
|
|
159
161
|
.pipe(Effect.provide(SomethingRepo.Test), rt.runPromise))
|
|
160
162
|
})
|
|
161
163
|
|
|
162
|
-
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.
|
|
164
|
+
const projected = S.Struct({ name: S.String, items: S.Array(S.Struct({ id: S.String, value: S.Finite })) })
|
|
163
165
|
|
|
164
166
|
const expected = [
|
|
165
167
|
{
|
package/test/requires.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, expectTypeOf, it } from "@effect/vitest"
|
|
2
|
-
import { Effect, Layer, Result, S
|
|
2
|
+
import { Context, Effect, Layer, Result, S } from "effect-app"
|
|
3
3
|
import { NotLoggedInError, UnauthorizedError } from "effect-app/client"
|
|
4
4
|
import { HttpHeaders } from "effect-app/http"
|
|
5
5
|
import * as RpcX from "effect-app/rpc"
|
|
@@ -63,11 +63,12 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
63
63
|
"works",
|
|
64
64
|
Effect.fn(function*() {
|
|
65
65
|
const defaultOpts = {
|
|
66
|
+
client: null as any, // TODO?
|
|
66
67
|
headers: HttpHeaders.fromRecordUnsafe({}),
|
|
67
68
|
payload: { _tag: "Test" },
|
|
68
69
|
clientId: 0,
|
|
69
70
|
requestId: "test-id" as any,
|
|
70
|
-
rpc: { ...TestRpc, annotations:
|
|
71
|
+
rpc: { ...TestRpc, annotations: Context.make(_mw.requestContext, {}) }
|
|
71
72
|
}
|
|
72
73
|
const next = Effect.void as unknown as Effect.Effect<SuccessValue, unhandled, never>
|
|
73
74
|
const layer = _mw.layer.pipe(
|
|
@@ -89,7 +90,7 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
89
90
|
headers: HttpHeaders.fromRecordUnsafe({ "x-user": "test-user", "x-is-manager": "true" }),
|
|
90
91
|
rpc: {
|
|
91
92
|
...defaultOpts.rpc,
|
|
92
|
-
annotations:
|
|
93
|
+
annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
|
|
93
94
|
}
|
|
94
95
|
})
|
|
95
96
|
)
|
|
@@ -127,7 +128,7 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
127
128
|
Object.assign({ ...defaultOpts }, {
|
|
128
129
|
rpc: {
|
|
129
130
|
...defaultOpts.rpc,
|
|
130
|
-
annotations:
|
|
131
|
+
annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
|
|
131
132
|
}
|
|
132
133
|
})
|
|
133
134
|
)
|
|
@@ -153,7 +154,7 @@ const testSuite = (_mw: typeof middleware3) =>
|
|
|
153
154
|
{
|
|
154
155
|
rpc: {
|
|
155
156
|
...defaultOpts.rpc,
|
|
156
|
-
annotations:
|
|
157
|
+
annotations: Context.make(_mw.requestContext, { requireRoles: ["manager"] })
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { NodeHttpServer } from "@effect/platform-node"
|
|
2
2
|
import { expect, expectTypeOf, it } from "@effect/vitest"
|
|
3
|
-
import { Console, Effect, Layer, Result } from "effect"
|
|
4
|
-
import { S } from "effect-app"
|
|
3
|
+
import { Console, Effect, Layer, Ref, Result } from "effect"
|
|
4
|
+
import { Context, S } from "effect-app"
|
|
5
5
|
import { NotLoggedInError } from "effect-app/client"
|
|
6
6
|
import { HttpRouter } from "effect-app/http"
|
|
7
7
|
import { DefaultGenericMiddlewares } from "effect-app/middleware"
|
|
8
8
|
import { MiddlewareMaker } from "effect-app/rpc"
|
|
9
9
|
import { middlewareGroup } from "effect-app/rpc/MiddlewareMaker"
|
|
10
10
|
import { FetchHttpClient } from "effect/unstable/http"
|
|
11
|
-
import { RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
|
|
11
|
+
import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
|
|
12
12
|
import { createServer } from "http"
|
|
13
13
|
import { DefaultGenericMiddlewaresLive } from "../src/api/routing.js"
|
|
14
14
|
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive, UserProfile } from "./fixtures.js"
|
|
@@ -109,7 +109,7 @@ export const RpcRealLayer = Layer
|
|
|
109
109
|
Layer.provide(FetchHttpClient.layer)
|
|
110
110
|
)
|
|
111
111
|
)
|
|
112
|
-
.pipe(Layer.provide(RpcSerialization.
|
|
112
|
+
.pipe(Layer.provide(RpcSerialization.layerNdjson))
|
|
113
113
|
|
|
114
114
|
it.live(
|
|
115
115
|
"require login",
|
|
@@ -136,3 +136,72 @@ it.live(
|
|
|
136
136
|
Effect.provide(RpcTestLayer)
|
|
137
137
|
)
|
|
138
138
|
)
|
|
139
|
+
|
|
140
|
+
// Per-request service isolation test
|
|
141
|
+
|
|
142
|
+
class PerRequestCounter extends Context.Service<PerRequestCounter>()(
|
|
143
|
+
"PerRequestCounter",
|
|
144
|
+
{ make: Effect.sync(() => ({ a: 0 })) }
|
|
145
|
+
) {
|
|
146
|
+
static Default = Layer.effect(this, this.make)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
class GlobalCounter extends Context.Service<GlobalCounter, {
|
|
150
|
+
readonly ref: Ref.Ref<number>
|
|
151
|
+
}>()("GlobalCounter") {}
|
|
152
|
+
|
|
153
|
+
const CounterRpcs = RpcGroup.make(
|
|
154
|
+
Rpc.make("incrementA", {
|
|
155
|
+
success: S.Number
|
|
156
|
+
}),
|
|
157
|
+
Rpc.make("incrementB", {
|
|
158
|
+
success: S.Number
|
|
159
|
+
})
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
const counterImpl = CounterRpcs
|
|
163
|
+
.toLayer({
|
|
164
|
+
incrementA: Effect.fn(function*() {
|
|
165
|
+
const counter = yield* PerRequestCounter
|
|
166
|
+
counter.a++
|
|
167
|
+
const global = yield* GlobalCounter
|
|
168
|
+
yield* Ref.update(global.ref, (n) => n + 1)
|
|
169
|
+
return counter.a
|
|
170
|
+
}, Effect.provide(PerRequestCounter.Default)),
|
|
171
|
+
incrementB: Effect.fn(function*() {
|
|
172
|
+
const counter = yield* PerRequestCounter
|
|
173
|
+
counter.a++
|
|
174
|
+
const global = yield* GlobalCounter
|
|
175
|
+
yield* Ref.update(global.ref, (n) => n + 1)
|
|
176
|
+
return counter.a
|
|
177
|
+
}, Effect.provide(PerRequestCounter.Default))
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
const GlobalCounterLive = Layer.effect(
|
|
181
|
+
GlobalCounter,
|
|
182
|
+
Ref.make(0).pipe(Effect.map((ref) => ({ ref })))
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
const CounterTestLayer = counterImpl.pipe(Layer.provideMerge(GlobalCounterLive))
|
|
186
|
+
|
|
187
|
+
it.live(
|
|
188
|
+
"per-request service isolation with shared global counter",
|
|
189
|
+
Effect.fnUntraced(
|
|
190
|
+
function*() {
|
|
191
|
+
const client = yield* RpcTest.makeClient(CounterRpcs)
|
|
192
|
+
const global = yield* GlobalCounter
|
|
193
|
+
|
|
194
|
+
const r1 = yield* client.incrementA()
|
|
195
|
+
const r2 = yield* client.incrementB()
|
|
196
|
+
|
|
197
|
+
// per-request counter is fresh each time → both return 1
|
|
198
|
+
expect(r1).toBe(1)
|
|
199
|
+
expect(r2).toBe(1)
|
|
200
|
+
|
|
201
|
+
// global counter is shared across requests → accumulates to 2
|
|
202
|
+
const globalCount = yield* Ref.get(global.ref)
|
|
203
|
+
expect(globalCount).toBe(2)
|
|
204
|
+
},
|
|
205
|
+
Effect.provide(CounterTestLayer)
|
|
206
|
+
)
|
|
207
|
+
)
|