@effect-app/infra 0.48.0 → 0.49.1
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 +21 -0
- package/_cjs/_ext/Array.cjs.map +1 -1
- package/_cjs/api/defaultErrorHandler.cjs.map +1 -1
- package/_cjs/api/express/makeJsonSchema.cjs +2 -2
- package/_cjs/api/express/makeJsonSchema.cjs.map +1 -1
- package/_cjs/api/express/schema/jwt.cjs.map +1 -1
- package/_cjs/api/express/schema/requestHandler.cjs.map +1 -1
- package/_cjs/api/express/schema/routing.cjs.map +1 -1
- package/_cjs/api/reportError.cjs.map +1 -1
- package/_cjs/api/routing.cjs.map +1 -1
- package/_cjs/api/setupRequest.cjs.map +1 -1
- package/_cjs/api/writeDocs.cjs.map +1 -1
- package/_cjs/rateLimit.cjs.map +1 -1
- package/_cjs/services/Emailer/Sendgrid.cjs.map +1 -1
- package/_cjs/services/Emailer/fake.cjs.map +1 -1
- package/_cjs/services/Operations/live.cjs.map +1 -1
- package/_cjs/services/Operations/service.cjs.map +1 -1
- package/_cjs/services/QueueMaker/errors.cjs.map +1 -1
- package/_cjs/services/QueueMaker/memQueue.cjs.map +1 -1
- package/_cjs/services/QueueMaker/sbqueue.cjs.map +1 -1
- package/_cjs/services/Repository.cjs +8 -8
- package/_cjs/services/Repository.cjs.map +1 -1
- package/_cjs/services/RepositoryBase.cjs +13 -13
- package/_cjs/services/RepositoryBase.cjs.map +1 -1
- package/_cjs/services/RequestContextContainer.cjs.map +1 -1
- package/_cjs/services/Store/Cosmos.cjs +9 -9
- package/_cjs/services/Store/Cosmos.cjs.map +1 -1
- package/_cjs/services/Store/Cosmos.test.cjs.map +1 -1
- package/_cjs/services/Store/Disk.cjs.map +1 -1
- package/_cjs/services/Store/Memory.cjs +42 -42
- package/_cjs/services/Store/Memory.cjs.map +1 -1
- package/_cjs/services/Store/Redis.cjs +13 -13
- package/_cjs/services/Store/Redis.cjs.map +1 -1
- package/_cjs/services/Store/codeFilter.test.cjs.map +1 -1
- package/_cjs/services/Store/index.cjs.map +1 -1
- package/_cjs/services/Store/service.cjs.map +1 -1
- package/_cjs/services/Store/utils.cjs +2 -3
- package/_cjs/services/Store/utils.cjs.map +1 -1
- package/_cjs/services/simpledb/cosmosdb.cjs +12 -4
- package/_cjs/services/simpledb/cosmosdb.cjs.map +1 -1
- package/_cjs/services/simpledb/diskdb.cjs.map +1 -1
- package/_cjs/services/simpledb/memdb.cjs.map +1 -1
- package/_cjs/services/simpledb/mongodb.cjs +15 -5
- package/_cjs/services/simpledb/mongodb.cjs.map +1 -1
- package/_cjs/services/simpledb/redisdb.cjs +13 -7
- package/_cjs/services/simpledb/redisdb.cjs.map +1 -1
- package/_cjs/services/simpledb/simpledb.cjs.map +1 -1
- package/_src/_ext/Array.ts +3 -2
- package/_src/api/defaultErrorHandler.ts +14 -12
- package/_src/api/express/makeJsonSchema.ts +6 -5
- package/_src/api/express/schema/jwt.ts +1 -1
- package/_src/api/express/schema/requestHandler.ts +112 -100
- package/_src/api/express/schema/routing.ts +61 -55
- package/_src/api/reportError.ts +16 -12
- package/_src/api/routing.ts +157 -135
- package/_src/api/setupRequest.ts +3 -1
- package/_src/api/writeDocs.ts +4 -3
- package/_src/rateLimit.ts +21 -14
- package/_src/services/Emailer/Sendgrid.ts +11 -10
- package/_src/services/Emailer/fake.ts +2 -1
- package/_src/services/Operations/live.ts +18 -12
- package/_src/services/Operations/service.ts +26 -17
- package/_src/services/QueueMaker/errors.ts +7 -6
- package/_src/services/QueueMaker/memQueue.ts +13 -11
- package/_src/services/QueueMaker/sbqueue.ts +16 -14
- package/_src/services/Repository.ts +53 -51
- package/_src/services/RepositoryBase.ts +33 -28
- package/_src/services/RequestContextContainer.ts +4 -3
- package/_src/services/Store/Cosmos.test.ts +31 -29
- package/_src/services/Store/Cosmos.ts +203 -180
- package/_src/services/Store/Disk.ts +19 -18
- package/_src/services/Store/Memory.ts +37 -32
- package/_src/services/Store/Redis.ts +31 -27
- package/_src/services/Store/codeFilter.test.ts +26 -16
- package/_src/services/Store/index.ts +26 -24
- package/_src/services/Store/service.ts +5 -5
- package/_src/services/Store/utils.ts +27 -24
- package/_src/services/simpledb/cosmosdb.ts +61 -51
- package/_src/services/simpledb/diskdb.ts +38 -31
- package/_src/services/simpledb/memdb.ts +25 -21
- package/_src/services/simpledb/mongodb.ts +40 -35
- package/_src/services/simpledb/redisdb.ts +66 -55
- package/_src/services/simpledb/simpledb.ts +32 -30
- package/dist/_ext/Array.d.ts.map +1 -1
- package/dist/_ext/Array.js +2 -2
- package/dist/api/defaultErrorHandler.d.ts.map +1 -1
- package/dist/api/defaultErrorHandler.js +5 -5
- package/dist/api/express/makeJsonSchema.d.ts +0 -1
- package/dist/api/express/makeJsonSchema.d.ts.map +1 -1
- package/dist/api/express/makeJsonSchema.js +4 -4
- package/dist/api/express/schema/jwt.js +2 -2
- package/dist/api/express/schema/requestHandler.d.ts.map +1 -1
- package/dist/api/express/schema/requestHandler.js +17 -17
- package/dist/api/express/schema/routing.d.ts.map +1 -1
- package/dist/api/express/schema/routing.js +13 -11
- package/dist/api/reportError.d.ts.map +1 -1
- package/dist/api/reportError.js +6 -6
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +29 -25
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +2 -2
- package/dist/api/writeDocs.d.ts.map +1 -1
- package/dist/api/writeDocs.js +5 -4
- package/dist/rateLimit.d.ts +4 -4
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +5 -4
- package/dist/services/Emailer/Sendgrid.d.ts.map +1 -1
- package/dist/services/Emailer/Sendgrid.js +9 -9
- package/dist/services/Emailer/fake.d.ts.map +1 -1
- package/dist/services/Emailer/fake.js +1 -1
- package/dist/services/Operations/live.d.ts.map +1 -1
- package/dist/services/Operations/live.js +11 -8
- package/dist/services/Operations/service.d.ts.map +1 -1
- package/dist/services/Operations/service.js +10 -4
- package/dist/services/QueueMaker/errors.d.ts.map +1 -1
- package/dist/services/QueueMaker/errors.js +5 -5
- package/dist/services/QueueMaker/memQueue.d.ts +1 -1
- package/dist/services/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/services/QueueMaker/memQueue.js +5 -4
- package/dist/services/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/services/QueueMaker/sbqueue.js +5 -5
- package/dist/services/Repository.d.ts +34 -35
- package/dist/services/Repository.d.ts.map +1 -1
- package/dist/services/Repository.js +16 -16
- package/dist/services/RepositoryBase.d.ts +4 -4
- package/dist/services/RepositoryBase.d.ts.map +1 -1
- package/dist/services/RepositoryBase.js +25 -22
- package/dist/services/RequestContextContainer.d.ts.map +1 -1
- package/dist/services/RequestContextContainer.js +3 -3
- package/dist/services/Store/Cosmos.d.ts +3 -3
- package/dist/services/Store/Cosmos.d.ts.map +1 -1
- package/dist/services/Store/Cosmos.js +53 -39
- package/dist/services/Store/Disk.d.ts.map +1 -1
- package/dist/services/Store/Disk.js +18 -18
- package/dist/services/Store/Memory.d.ts +2 -3
- package/dist/services/Store/Memory.d.ts.map +1 -1
- package/dist/services/Store/Memory.js +44 -44
- package/dist/services/Store/Redis.d.ts +4 -4
- package/dist/services/Store/Redis.d.ts.map +1 -1
- package/dist/services/Store/Redis.js +19 -17
- package/dist/services/Store/index.d.ts.map +1 -1
- package/dist/services/Store/index.js +4 -4
- package/dist/services/Store/service.d.ts +4 -5
- package/dist/services/Store/service.d.ts.map +1 -1
- package/dist/services/Store/service.js +3 -3
- package/dist/services/Store/utils.d.ts +2 -2
- package/dist/services/Store/utils.d.ts.map +1 -1
- package/dist/services/Store/utils.js +16 -14
- package/dist/services/simpledb/cosmosdb.d.ts.map +1 -1
- package/dist/services/simpledb/cosmosdb.js +16 -12
- package/dist/services/simpledb/diskdb.d.ts.map +1 -1
- package/dist/services/simpledb/diskdb.js +15 -15
- package/dist/services/simpledb/memdb.d.ts.map +1 -1
- package/dist/services/simpledb/memdb.js +6 -6
- package/dist/services/simpledb/mongodb.d.ts +5 -5
- package/dist/services/simpledb/mongodb.d.ts.map +1 -1
- package/dist/services/simpledb/mongodb.js +9 -9
- package/dist/services/simpledb/redisdb.d.ts +4 -4
- package/dist/services/simpledb/redisdb.d.ts.map +1 -1
- package/dist/services/simpledb/redisdb.js +18 -18
- package/dist/services/simpledb/simpledb.d.ts.map +1 -1
- package/dist/services/simpledb/simpledb.js +9 -9
- package/package.json +7 -7
|
@@ -12,11 +12,12 @@ class CosmosDbOperationError {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const setup = (type: string, indexingPolicy: IndexingPolicy) =>
|
|
15
|
-
Cosmos.
|
|
15
|
+
Cosmos.CosmosClient.tap(({ db }) =>
|
|
16
16
|
Effect.tryPromise(() =>
|
|
17
|
-
db
|
|
17
|
+
db
|
|
18
|
+
.containers
|
|
18
19
|
.create({ id: type, indexingPolicy })
|
|
19
|
-
.catch(err => console.warn(err))
|
|
20
|
+
.catch((err) => console.warn(err))
|
|
20
21
|
)
|
|
21
22
|
)
|
|
22
23
|
// TODO: Error if current indexingPolicy does not match
|
|
@@ -36,11 +37,12 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
36
37
|
}))
|
|
37
38
|
|
|
38
39
|
function find(id: string) {
|
|
39
|
-
return Cosmos
|
|
40
|
-
.
|
|
41
|
-
.
|
|
40
|
+
return Cosmos
|
|
41
|
+
.CosmosClient
|
|
42
|
+
.flatMap(({ db }) => Effect.tryPromise(() => db.container(type).item(id).read<{ data: EA }>()))
|
|
43
|
+
.map((i) => Option.fromNullable(i.resource))
|
|
42
44
|
.map(
|
|
43
|
-
_ =>
|
|
45
|
+
(_) =>
|
|
44
46
|
_.map(
|
|
45
47
|
({ _etag, data }) => ({ version: _etag, data } as CachedRecord<EA>)
|
|
46
48
|
)
|
|
@@ -48,24 +50,26 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
function findBy(parameters: Record<string, string>) {
|
|
51
|
-
return Cosmos
|
|
52
|
-
.
|
|
53
|
+
return Cosmos
|
|
54
|
+
.CosmosClient
|
|
55
|
+
.flatMap(({ db }) =>
|
|
53
56
|
Effect.tryPromise(() =>
|
|
54
57
|
db
|
|
55
58
|
.container(type)
|
|
56
|
-
.items
|
|
59
|
+
.items
|
|
60
|
+
.query({
|
|
57
61
|
query: `
|
|
58
62
|
SELECT TOP 1 ${type}.id
|
|
59
63
|
FROM ${type} i
|
|
60
64
|
WHERE (
|
|
61
65
|
${
|
|
62
66
|
typedKeysOf(parameters)
|
|
63
|
-
.map(k => `i.${k} = @${k}`)
|
|
67
|
+
.map((k) => `i.${k} = @${k}`)
|
|
64
68
|
.join(" and ")
|
|
65
69
|
}
|
|
66
70
|
)
|
|
67
71
|
`,
|
|
68
|
-
parameters: typedKeysOf(parameters).map(p => ({
|
|
72
|
+
parameters: typedKeysOf(parameters).map((p) => ({
|
|
69
73
|
name: `@${p}`,
|
|
70
74
|
value: parameters[p]!
|
|
71
75
|
}))
|
|
@@ -73,58 +77,64 @@ WHERE (
|
|
|
73
77
|
.fetchAll()
|
|
74
78
|
)
|
|
75
79
|
)
|
|
76
|
-
.map(x => x.resources.head)
|
|
77
|
-
.map(_ => _.map(_ => _.id))
|
|
80
|
+
.map((x) => x.resources.head)
|
|
81
|
+
.map((_) => _.map((_) => _.id))
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
function store(record: A, currentVersion: Option<Version>) {
|
|
81
85
|
return Effect.gen(function*($) {
|
|
82
86
|
const version = "_etag" // we get this from the etag anyway.
|
|
83
87
|
|
|
84
|
-
const db = yield* $(Cosmos.
|
|
88
|
+
const { db } = yield* $(Cosmos.CosmosClient)
|
|
85
89
|
const data = yield* $(encode(record))
|
|
86
90
|
|
|
87
91
|
yield* $(
|
|
88
92
|
currentVersion.match(
|
|
89
93
|
() =>
|
|
90
|
-
Effect
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
94
|
+
Effect
|
|
95
|
+
.tryPromise(() =>
|
|
96
|
+
db.container(type).items.create({
|
|
97
|
+
id: record.id,
|
|
98
|
+
timestamp: new Date(),
|
|
99
|
+
data
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
.asUnit
|
|
103
|
+
.orDie,
|
|
104
|
+
(currentVersion) =>
|
|
105
|
+
Effect
|
|
106
|
+
.tryPromise(() =>
|
|
107
|
+
db
|
|
108
|
+
.container(type)
|
|
109
|
+
.item(record.id)
|
|
110
|
+
.replace(
|
|
111
|
+
{
|
|
112
|
+
id: record.id,
|
|
113
|
+
timestamp: new Date(),
|
|
114
|
+
data
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
accessCondition: {
|
|
118
|
+
type: "IfMatch",
|
|
119
|
+
condition: currentVersion
|
|
120
|
+
}
|
|
112
121
|
}
|
|
113
|
-
}
|
|
114
|
-
)
|
|
115
|
-
).orDie.flatMap(x => {
|
|
116
|
-
if (x.statusCode === 412) {
|
|
117
|
-
return Effect.fail(new OptimisticLockException(type, record.id))
|
|
118
|
-
}
|
|
119
|
-
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
120
|
-
return Effect.die(
|
|
121
|
-
new CosmosDbOperationError(
|
|
122
|
-
"not able to update record: " + x.statusCode
|
|
123
122
|
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
)
|
|
124
|
+
.orDie
|
|
125
|
+
.flatMap((x) => {
|
|
126
|
+
if (x.statusCode === 412) {
|
|
127
|
+
return Effect.fail(new OptimisticLockException(type, record.id))
|
|
128
|
+
}
|
|
129
|
+
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
130
|
+
return Effect.die(
|
|
131
|
+
new CosmosDbOperationError(
|
|
132
|
+
"not able to update record: " + x.statusCode
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
return Effect.unit
|
|
137
|
+
})
|
|
128
138
|
)
|
|
129
139
|
)
|
|
130
140
|
return { version, data: record } as CachedRecord<A>
|
|
@@ -33,28 +33,31 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
33
33
|
|
|
34
34
|
function store(record: A, currentVersion: Option<Version>) {
|
|
35
35
|
const version = currentVersion
|
|
36
|
-
.map(cv => (parseInt(cv) + 1).toString())
|
|
36
|
+
.map((cv) => (parseInt(cv) + 1).toString())
|
|
37
37
|
.getOrElse(() => "1")
|
|
38
38
|
const getData = flow(
|
|
39
39
|
encode,
|
|
40
|
-
_ => _.map(data => pretty({ version, timestamp: new Date(), data }))
|
|
40
|
+
(_) => _.map((data) => pretty({ version, timestamp: new Date(), data }))
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
const idx = makeIndexKey(record)
|
|
44
44
|
return currentVersion.isSome()
|
|
45
45
|
? lockIndex(record)
|
|
46
46
|
.zipRight(
|
|
47
|
-
readIndex(idx)
|
|
48
|
-
x
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
readIndex(idx)
|
|
48
|
+
.flatMap((x) =>
|
|
49
|
+
x[record.id]
|
|
50
|
+
? Effect.fail(() => new Error("Combination already exists, abort"))
|
|
51
|
+
: getData(record)
|
|
52
|
+
.flatMap((serialised) => fu.writeTextFile(getFilename(type, record.id), serialised))
|
|
53
|
+
.zipRight(writeIndex(idx, { ...x, [idx.key]: record.id }))
|
|
54
|
+
)
|
|
55
|
+
.orDie
|
|
54
56
|
)
|
|
55
|
-
.scoped
|
|
57
|
+
.scoped
|
|
58
|
+
.map(() => ({ version, data: record } as CachedRecord<A>))
|
|
56
59
|
: getData(record)
|
|
57
|
-
.flatMap(serialised => fu.writeTextFile(getFilename(type, record.id), serialised))
|
|
60
|
+
.flatMap((serialised) => fu.writeTextFile(getFilename(type, record.id), serialised))
|
|
58
61
|
.map(() => ({ version, data: record } as CachedRecord<A>))
|
|
59
62
|
}
|
|
60
63
|
|
|
@@ -74,60 +77,64 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
74
77
|
|
|
75
78
|
function lockRecordOnDisk(type: string) {
|
|
76
79
|
return (id: string) =>
|
|
77
|
-
lockFile(getFilename(type, id))
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
lockFile(getFilename(type, id))
|
|
81
|
+
.mapBoth(
|
|
82
|
+
(err) => new CouldNotAquireDbLockException(type, id, err as Error),
|
|
83
|
+
(release) => ({ release })
|
|
84
|
+
)
|
|
85
|
+
.acquireRelease(
|
|
86
|
+
(l) => l.release
|
|
87
|
+
)
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
function lockIndexOnDisk(type: string) {
|
|
86
91
|
return (id: string) =>
|
|
87
|
-
lockFile(getIdxName(type, id))
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
lockFile(getIdxName(type, id))
|
|
93
|
+
.mapBoth(
|
|
94
|
+
(err) => new CouldNotAquireDbLockException(type, id, err as Error),
|
|
95
|
+
(release) => ({ release })
|
|
96
|
+
)
|
|
97
|
+
.acquireRelease(
|
|
98
|
+
(l) => l.release
|
|
99
|
+
)
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
function readFile(filePath: string) {
|
|
96
103
|
return fu
|
|
97
104
|
.readTextFile(filePath)
|
|
98
|
-
.catchAll(err => Effect.die(new ConnectionException(err as Error)))
|
|
105
|
+
.catchAll((err) => Effect.die(new ConnectionException(err as Error)))
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
function find(type: string) {
|
|
102
109
|
return (id: string) => {
|
|
103
110
|
return tryRead(getFilename(type, id)).map(
|
|
104
|
-
_ => _.map(s => JSON.parse(s) as CachedRecord<EA>)
|
|
111
|
+
(_) => _.map((s) => JSON.parse(s) as CachedRecord<EA>)
|
|
105
112
|
)
|
|
106
113
|
}
|
|
107
114
|
}
|
|
108
115
|
|
|
109
116
|
function getIdx(index: Index) {
|
|
110
|
-
return readIndex(index).map(idx => Option.fromNullable(idx[index.key]))
|
|
117
|
+
return readIndex(index).map((idx) => Option.fromNullable(idx[index.key]))
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
function readIndex(index: Index) {
|
|
114
121
|
return tryRead(getIdxName(type, index.doc)).map(
|
|
115
|
-
_ =>
|
|
122
|
+
(_) =>
|
|
116
123
|
_.match(
|
|
117
124
|
() => ({} as Record<string, TKey>),
|
|
118
|
-
x => JSON.parse(x) as Record<string, TKey>
|
|
125
|
+
(x) => JSON.parse(x) as Record<string, TKey>
|
|
119
126
|
)
|
|
120
127
|
)
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
function writeIndex(index: Index, content: Record<string, TKey>) {
|
|
124
|
-
return pipe(JSON.stringify(content), serialised => fu.writeTextFile(getIdxName(type, index.doc), serialised))
|
|
131
|
+
return pipe(JSON.stringify(content), (serialised) => fu.writeTextFile(getIdxName(type, index.doc), serialised))
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
function tryRead(filePath: string) {
|
|
128
135
|
return fu
|
|
129
136
|
.fileExists(filePath)
|
|
130
|
-
.flatMap(exists => !exists ? Effect(Option.none) : readFile(filePath).map(Option.some))
|
|
137
|
+
.flatMap((exists) => !exists ? Effect(Option.none) : readFile(filePath).map(Option.some))
|
|
131
138
|
}
|
|
132
139
|
|
|
133
140
|
function getFilename(type: string, id: string) {
|
|
@@ -141,7 +148,7 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
function lockFile(fileName: string) {
|
|
144
|
-
return Effect.tryPromise(() => PLF.lock(fileName).then(flow(Effect.tryPromise, _ => _.orDie)))
|
|
151
|
+
return Effect.tryPromise(() => PLF.lock(fileName).then(flow(Effect.tryPromise, (_) => _.orDie)))
|
|
145
152
|
}
|
|
146
153
|
|
|
147
154
|
// TODO: ugh.
|
|
@@ -27,7 +27,7 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
27
27
|
function find(id: string) {
|
|
28
28
|
return storage
|
|
29
29
|
.find(getRecordName(type, id))
|
|
30
|
-
.map(_ => _.map(s => JSON.parse(s)))
|
|
30
|
+
.map((_) => _.map((s) => JSON.parse(s)))
|
|
31
31
|
.flatMapOpt(parseSDB)
|
|
32
32
|
.mapOpt(({ data, version }) => ({
|
|
33
33
|
data: JSON.parse(data) as EA,
|
|
@@ -37,37 +37,41 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
37
37
|
|
|
38
38
|
function findBy<V extends Partial<A>>(keys: V, eq: Equivalence<V>) {
|
|
39
39
|
// Naive implementation, fine for in memory testing purposes.
|
|
40
|
-
return Effect
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
40
|
+
return Effect
|
|
41
|
+
.gen(function*($) {
|
|
42
|
+
for (const [, value] of storage) {
|
|
43
|
+
const sdb_ = JSON.parse(value)
|
|
44
|
+
const sdb = yield* $(parseSDB(sdb_))
|
|
45
|
+
const cr = { data: JSON.parse(sdb.data) as EA, version: sdb.version }
|
|
46
|
+
const r = yield* $(
|
|
47
|
+
decode(cr.data)
|
|
48
|
+
.flatMap((d) =>
|
|
49
|
+
eq(keys, d as unknown as V)
|
|
50
|
+
? Effect(d)
|
|
51
|
+
: Effect.fail("not equals")
|
|
52
|
+
)
|
|
53
|
+
.exit
|
|
54
|
+
)
|
|
55
|
+
if (r.isSuccess()) {
|
|
56
|
+
return r.value
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
return null
|
|
60
|
+
})
|
|
61
|
+
.map(Option.fromNullable)
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
function store(record: A, currentVersion: Option<Version>) {
|
|
61
65
|
const version = currentVersion
|
|
62
|
-
.map(cv => (parseInt(cv) + 1).toString())
|
|
66
|
+
.map((cv) => (parseInt(cv) + 1).toString())
|
|
63
67
|
.getOrElse(() => "1")
|
|
64
68
|
|
|
65
69
|
const getData = flow(
|
|
66
70
|
encode,
|
|
67
|
-
_ => _.map(JSON.stringify).map(data => JSON.stringify({ version, timestamp: new Date(), data }))
|
|
71
|
+
(_) => _.map(JSON.stringify).map((data) => JSON.stringify({ version, timestamp: new Date(), data }))
|
|
68
72
|
)
|
|
69
73
|
return getData(record)
|
|
70
|
-
.flatMap(serialised => storage.set(getRecordName(type, record.id), serialised))
|
|
74
|
+
.flatMap((serialised) => storage.set(getRecordName(type, record.id), serialised))
|
|
71
75
|
.map(() => ({ version, data: record } as CachedRecord<A>))
|
|
72
76
|
}
|
|
73
77
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { MongoClient } from "@effect-app/infra-adapters/mongo-client"
|
|
1
2
|
import type { IndexDescription, InsertOneOptions } from "mongodb"
|
|
2
|
-
|
|
3
|
-
import * as Mongo from "@effect-app/infra-adapters/mongo-client"
|
|
4
3
|
import type { CachedRecord, DBRecord } from "./shared.js"
|
|
5
4
|
import { OptimisticLockException } from "./shared.js"
|
|
6
5
|
import * as simpledb from "./simpledb.js"
|
|
@@ -12,9 +11,9 @@ import type { Version } from "./simpledb.js"
|
|
|
12
11
|
// }, {} as Record<string, number>)
|
|
13
12
|
|
|
14
13
|
const setup = (type: string, indexes: IndexDescription[]) =>
|
|
15
|
-
|
|
16
|
-
.tap(db => Effect.tryPromise(() => db.createCollection(type).catch(err => console.warn(err))))
|
|
17
|
-
.flatMap(db => Effect.tryPromise(() => db.collection(type).createIndexes(indexes)))
|
|
14
|
+
MongoClient
|
|
15
|
+
.tap(({ db }) => Effect.tryPromise(() => db.createCollection(type).catch((err) => console.warn(err))))
|
|
16
|
+
.flatMap(({ db }) => Effect.tryPromise(() => db.collection(type).createIndexes(indexes)))
|
|
18
17
|
|
|
19
18
|
export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>() {
|
|
20
19
|
return <REncode, RDecode, EDecode>(
|
|
@@ -31,8 +30,8 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
31
30
|
}))
|
|
32
31
|
|
|
33
32
|
function find(id: string) {
|
|
34
|
-
return
|
|
35
|
-
.flatMap(db =>
|
|
33
|
+
return MongoClient
|
|
34
|
+
.flatMap(({ db }) =>
|
|
36
35
|
Effect.tryPromise(() =>
|
|
37
36
|
db
|
|
38
37
|
.collection(type)
|
|
@@ -44,8 +43,8 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
function findBy(keys: Record<string, string>) {
|
|
47
|
-
return
|
|
48
|
-
.flatMap(db =>
|
|
46
|
+
return MongoClient
|
|
47
|
+
.flatMap(({ db }) =>
|
|
49
48
|
Effect.tryPromise(() => db.collection(type).findOne<{ _id: TKey }>(keys, { projection: { _id: 1 } }))
|
|
50
49
|
)
|
|
51
50
|
.map(Option.fromNullable)
|
|
@@ -55,41 +54,47 @@ export function createContext<TKey extends string, EA, A extends DBRecord<TKey>>
|
|
|
55
54
|
function store(record: A, currentVersion: Option<Version>) {
|
|
56
55
|
return Effect.gen(function*($) {
|
|
57
56
|
const version = currentVersion
|
|
58
|
-
.map(cv => (parseInt(cv) + 1).toString())
|
|
57
|
+
.map((cv) => (parseInt(cv) + 1).toString())
|
|
59
58
|
.getOrElse(() => "1")
|
|
60
59
|
|
|
61
|
-
const db = yield* $(
|
|
60
|
+
const { db } = yield* $(MongoClient)
|
|
62
61
|
const data = yield* $(encode(record))
|
|
63
62
|
yield* $(
|
|
64
63
|
currentVersion.match(
|
|
65
64
|
() =>
|
|
66
|
-
Effect
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
Effect
|
|
66
|
+
.tryPromise(() =>
|
|
67
|
+
db
|
|
68
|
+
.collection(type)
|
|
69
|
+
.insertOne(
|
|
70
|
+
{ _id: record.id as any, version, timestamp: new Date(), data },
|
|
71
|
+
{
|
|
72
|
+
checkKeys: false // support for keys with `.` and `$`. NOTE: you can write them, read them, but NOT query for them.
|
|
73
|
+
} as InsertOneOptions
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
.asUnit
|
|
77
|
+
.orDie,
|
|
78
|
+
(currentVersion) =>
|
|
79
|
+
Effect
|
|
80
|
+
.tryPromise(() =>
|
|
81
|
+
db.collection(type).replaceOne(
|
|
82
|
+
{ _id: record.id, version: currentVersion },
|
|
71
83
|
{
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
version,
|
|
85
|
+
timestamp: new Date(),
|
|
86
|
+
data
|
|
87
|
+
},
|
|
88
|
+
{ upsert: false }
|
|
74
89
|
)
|
|
75
|
-
).asUnit.orDie,
|
|
76
|
-
currentVersion =>
|
|
77
|
-
Effect.tryPromise(() =>
|
|
78
|
-
db.collection(type).replaceOne(
|
|
79
|
-
{ _id: record.id, version: currentVersion },
|
|
80
|
-
{
|
|
81
|
-
version,
|
|
82
|
-
timestamp: new Date(),
|
|
83
|
-
data
|
|
84
|
-
},
|
|
85
|
-
{ upsert: false }
|
|
86
90
|
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
.orDie
|
|
92
|
+
.flatMap((x) => {
|
|
93
|
+
if (!x.modifiedCount) {
|
|
94
|
+
return Effect.fail(new OptimisticLockException(type, record.id))
|
|
95
|
+
}
|
|
96
|
+
return Effect.unit
|
|
97
|
+
})
|
|
93
98
|
)
|
|
94
99
|
)
|
|
95
100
|
return { version, data: record } as CachedRecord<A>
|