presidium-db-client-server 0.3.1 → 0.5.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/DatabaseHandler.js +149 -157
- package/DatabaseServer.js +34 -4
- package/healthCheckHandler.js +18 -0
- package/notFoundHandler.js +18 -0
- package/package.json +4 -2
package/DatabaseHandler.js
CHANGED
|
@@ -111,181 +111,173 @@ function DatabaseHandler(options) {
|
|
|
111
111
|
request.pathSegments = request.path.slice(1).split("/")
|
|
112
112
|
request.pathDepth = request.pathSegments.length
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const ht = hts.get(tableName)
|
|
118
|
-
?? await getHt(tableName, request, manifestHt)
|
|
114
|
+
const tableName = request.pathSegments[0]
|
|
115
|
+
const fname = request.pathSegments[1]
|
|
116
|
+
const ht = hts.get(tableName) ?? await getHt(tableName, request, manifestHt)
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
await switchCase(
|
|
119
|
+
fname,
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
121
|
+
eq("clear"), async () => {
|
|
122
|
+
await ht.clear()
|
|
123
|
+
response.writeHead(200, {
|
|
124
|
+
"Content-Type": "text/plain",
|
|
125
|
+
})
|
|
126
|
+
response.end("OK")
|
|
127
|
+
},
|
|
130
128
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
eq("set"), async () => {
|
|
130
|
+
const { key, value, sortValue } = await Readable.JSON(request)
|
|
131
|
+
await ht.set(key, value, sortValue)
|
|
132
|
+
response.writeHead(200, {
|
|
133
|
+
"Content-Type": "text/plain",
|
|
134
|
+
})
|
|
135
|
+
response.end("OK")
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
eq("get"), async () => {
|
|
139
|
+
const { key } = await Readable.JSON(request)
|
|
140
|
+
const value = await ht.get(key)
|
|
141
|
+
if (value === undefined) {
|
|
142
|
+
response.writeHead(499, {
|
|
135
143
|
"Content-Type": "text/plain",
|
|
136
144
|
})
|
|
137
|
-
response.end("
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
eq("get"), async () => {
|
|
141
|
-
const { key } = await Readable.JSON(request)
|
|
142
|
-
const value = await ht.get(key)
|
|
143
|
-
if (value === undefined) {
|
|
144
|
-
response.writeHead(499, {
|
|
145
|
-
"Content-Type": "text/plain",
|
|
146
|
-
})
|
|
147
|
-
response.end("Resource Not Found")
|
|
148
|
-
} else {
|
|
149
|
-
response.writeHead(200, {
|
|
150
|
-
"Content-Type": "text/plain",
|
|
151
|
-
})
|
|
152
|
-
response.end(value)
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
eq("getBinary"), async () => {
|
|
157
|
-
const { key } = await Readable.JSON(request)
|
|
158
|
-
const value = await ht.getBinary(key)
|
|
145
|
+
response.end("Resource Not Found")
|
|
146
|
+
} else {
|
|
159
147
|
response.writeHead(200, {
|
|
160
|
-
"Content-Type": "
|
|
148
|
+
"Content-Type": "text/plain",
|
|
161
149
|
})
|
|
162
150
|
response.end(value)
|
|
163
|
-
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
164
153
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
154
|
+
eq("getBinary"), async () => {
|
|
155
|
+
const { key } = await Readable.JSON(request)
|
|
156
|
+
const value = await ht.getBinary(key)
|
|
157
|
+
response.writeHead(200, {
|
|
158
|
+
"Content-Type": "application/octet-stream",
|
|
159
|
+
})
|
|
160
|
+
response.end(value)
|
|
161
|
+
},
|
|
173
162
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
163
|
+
eq("delete"), async () => {
|
|
164
|
+
const { key } = await Readable.JSON(request)
|
|
165
|
+
const didDelete = await ht.delete(key)
|
|
166
|
+
response.writeHead(200, {
|
|
167
|
+
"Content-Type": "text/plain",
|
|
168
|
+
})
|
|
169
|
+
response.end(String(didDelete))
|
|
170
|
+
},
|
|
181
171
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
for await (const value of ht.iterator(options)) {
|
|
190
|
-
response.write(value)
|
|
191
|
-
response.write("")
|
|
192
|
-
}
|
|
193
|
-
response.end()
|
|
194
|
-
},
|
|
172
|
+
eq("count"), async () => {
|
|
173
|
+
const count = ht.count()
|
|
174
|
+
response.writeHead(200, {
|
|
175
|
+
"Content-Type": "text/plain",
|
|
176
|
+
})
|
|
177
|
+
response.end(String(count))
|
|
178
|
+
},
|
|
195
179
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
response.end()
|
|
210
|
-
},
|
|
180
|
+
eq("iterator"), async () => {
|
|
181
|
+
const options = await Readable.JSON(request)
|
|
182
|
+
response.writeHead(200, {
|
|
183
|
+
"Content-Type": "application/octet-stream",
|
|
184
|
+
"Content-Encoding": "utf8",
|
|
185
|
+
})
|
|
186
|
+
const count = ht.count()
|
|
187
|
+
for await (const value of ht.iterator(options)) {
|
|
188
|
+
response.write(value)
|
|
189
|
+
response.write("")
|
|
190
|
+
}
|
|
191
|
+
response.end()
|
|
192
|
+
},
|
|
211
193
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
response.
|
|
224
|
-
}
|
|
194
|
+
eq("entriesIterator"), async () => {
|
|
195
|
+
const options = await Readable.JSON(request)
|
|
196
|
+
response.writeHead(200, {
|
|
197
|
+
"Content-Type": "application/octet-stream",
|
|
198
|
+
"Content-Encoding": "utf8",
|
|
199
|
+
})
|
|
200
|
+
const count = ht.count()
|
|
201
|
+
for await (const [key, value] of ht.entriesIterator(options)) {
|
|
202
|
+
response.write(key)
|
|
203
|
+
response.write("")
|
|
204
|
+
response.write(value)
|
|
205
|
+
response.write("")
|
|
206
|
+
}
|
|
207
|
+
response.end()
|
|
208
|
+
},
|
|
225
209
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
210
|
+
eq("forwardIterator"), async () => {
|
|
211
|
+
const options = await Readable.JSON(request)
|
|
212
|
+
response.writeHead(200, {
|
|
213
|
+
"Content-Type": "application/octet-stream",
|
|
214
|
+
"Content-Encoding": "utf8",
|
|
215
|
+
})
|
|
216
|
+
const count = ht.count()
|
|
217
|
+
for await (const value of ht.forwardIterator(options)) {
|
|
218
|
+
response.write(value)
|
|
219
|
+
response.write("")
|
|
220
|
+
}
|
|
221
|
+
response.end()
|
|
222
|
+
},
|
|
239
223
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
response.write("")
|
|
254
|
-
}
|
|
255
|
-
response.end()
|
|
256
|
-
},
|
|
224
|
+
eq("reverseIterator"), async () => {
|
|
225
|
+
const options = await Readable.JSON(request)
|
|
226
|
+
response.writeHead(200, {
|
|
227
|
+
"Content-Type": "application/octet-stream",
|
|
228
|
+
"Content-Encoding": "utf8",
|
|
229
|
+
})
|
|
230
|
+
const count = ht.count()
|
|
231
|
+
for await (const value of ht.reverseIterator(options)) {
|
|
232
|
+
response.write(value)
|
|
233
|
+
response.write("")
|
|
234
|
+
}
|
|
235
|
+
response.end()
|
|
236
|
+
},
|
|
257
237
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
238
|
+
eq("forwardEntriesIterator"), async () => {
|
|
239
|
+
const options = await Readable.JSON(request)
|
|
240
|
+
response.writeHead(200, {
|
|
241
|
+
"Content-Type": "application/octet-stream",
|
|
242
|
+
"Content-Encoding": "utf8",
|
|
243
|
+
})
|
|
244
|
+
const count = ht.count()
|
|
245
|
+
for await (const [key, value, sortValue] of ht.forwardEntriesIterator(options)) {
|
|
246
|
+
response.write(key)
|
|
247
|
+
response.write("")
|
|
248
|
+
response.write(value)
|
|
249
|
+
response.write("")
|
|
250
|
+
response.write(String(sortValue))
|
|
251
|
+
response.write("")
|
|
252
|
+
}
|
|
253
|
+
response.end()
|
|
254
|
+
},
|
|
275
255
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
256
|
+
eq("reverseEntriesIterator"), async () => {
|
|
257
|
+
const options = await Readable.JSON(request)
|
|
258
|
+
response.writeHead(200, {
|
|
259
|
+
"Content-Type": "application/octet-stream",
|
|
260
|
+
"Content-Encoding": "utf8",
|
|
261
|
+
})
|
|
262
|
+
const count = ht.count()
|
|
263
|
+
for await (const [key, value, sortValue] of ht.reverseEntriesIterator(options)) {
|
|
264
|
+
response.write(key)
|
|
265
|
+
response.write("")
|
|
266
|
+
response.write(value)
|
|
267
|
+
response.write("")
|
|
268
|
+
response.write(String(sortValue))
|
|
269
|
+
response.write("")
|
|
270
|
+
}
|
|
271
|
+
response.end()
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
async () => {
|
|
275
|
+
const error = new Error("Forbidden: Hash Table Method")
|
|
276
|
+
error.code = "403"
|
|
277
|
+
throw error
|
|
278
|
+
},
|
|
279
|
+
)
|
|
282
280
|
|
|
283
|
-
} else {
|
|
284
|
-
response.writeHead(404, {
|
|
285
|
-
"Content-Type": "text/plain",
|
|
286
|
-
})
|
|
287
|
-
response.end("Not Found")
|
|
288
|
-
}
|
|
289
281
|
}
|
|
290
282
|
}
|
|
291
283
|
|
package/DatabaseServer.js
CHANGED
|
@@ -7,12 +7,18 @@ const HTTP = require("presidium/HTTP")
|
|
|
7
7
|
const DiskHashTable = require("presidium/DiskHashTable")
|
|
8
8
|
const DiskSortedHashTable = require("presidium/DiskSortedHashTable")
|
|
9
9
|
const DatabaseHandler = require("./DatabaseHandler")
|
|
10
|
+
const healthCheckHandler = require("./healthCheckHandler")
|
|
11
|
+
const notFoundHandler = require("./notFoundHandler")
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* @name DatabaseServer
|
|
13
15
|
*
|
|
14
16
|
* @docs
|
|
15
17
|
* ```specscript
|
|
18
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
19
|
+
*
|
|
20
|
+
* HTTP.Handler(request HTTP.Request, response HTTP.Response) -> ()
|
|
21
|
+
*
|
|
16
22
|
* new DatabaseServer(options {
|
|
17
23
|
* databasePath: string,
|
|
18
24
|
* manifestTableName: string,
|
|
@@ -21,11 +27,11 @@ const DatabaseHandler = require("./DatabaseHandler")
|
|
|
21
27
|
* defaultTableInitialLength: number,
|
|
22
28
|
* defaultTableItemSize: number,
|
|
23
29
|
* defaultDiskSortedHashTableDegree: number,
|
|
24
|
-
* }) -> server DatabaseServer
|
|
30
|
+
* }, ...handlers Arguments<HTTP.Handler>) -> server DatabaseServer
|
|
25
31
|
* ```
|
|
26
32
|
*/
|
|
27
33
|
class DatabaseServer {
|
|
28
|
-
constructor(options) {
|
|
34
|
+
constructor(options, ...handlers) {
|
|
29
35
|
this.databasePath = options.databasePath
|
|
30
36
|
this.manifestTableName = options.manifestTableName ?? "Manifest"
|
|
31
37
|
this.manifestTableInitialLength = options.manifestTableInitialLength ?? 1048576
|
|
@@ -33,6 +39,7 @@ class DatabaseServer {
|
|
|
33
39
|
this.defaultTableInitialLength = options.defaultTableInitialLength ?? 1048576
|
|
34
40
|
this.defaultTableItemSize = options.defaultTableItemSize ?? 1024
|
|
35
41
|
this.defaultDiskSortedHashTableDegree = options.defaultDiskSortedHashTableDegree ?? 2
|
|
42
|
+
this.handlers = handlers
|
|
36
43
|
this.server = null
|
|
37
44
|
this.hts = new Map()
|
|
38
45
|
}
|
|
@@ -67,8 +74,16 @@ class DatabaseServer {
|
|
|
67
74
|
hts: this.hts,
|
|
68
75
|
})
|
|
69
76
|
|
|
70
|
-
const
|
|
71
|
-
|
|
77
|
+
const router = tryCatch(
|
|
78
|
+
switchCase(
|
|
79
|
+
request => request.path == "/health",
|
|
80
|
+
healthCheckHandler,
|
|
81
|
+
|
|
82
|
+
request => request.pathDepth == 2,
|
|
83
|
+
databaseHandler,
|
|
84
|
+
|
|
85
|
+
notFoundHandler,
|
|
86
|
+
),
|
|
72
87
|
function errorHandler(error, request, response) {
|
|
73
88
|
response.writeHead(error.code ?? 500, {
|
|
74
89
|
"Content-Type": "text/plain",
|
|
@@ -77,6 +92,21 @@ class DatabaseServer {
|
|
|
77
92
|
}
|
|
78
93
|
)
|
|
79
94
|
|
|
95
|
+
const handler = all.series([
|
|
96
|
+
...this.handlers,
|
|
97
|
+
request => {
|
|
98
|
+
const { pathname } = new URL(`http://*${request.url}`)
|
|
99
|
+
request.path = decodeURIComponent(
|
|
100
|
+
pathname == "/" ? pathname
|
|
101
|
+
: pathname.endsWith("/") ? pathname.slice(0, -1)
|
|
102
|
+
: pathname
|
|
103
|
+
)
|
|
104
|
+
request.pathSegments = request.path.slice(1).split("/")
|
|
105
|
+
request.pathDepth = request.pathSegments.length
|
|
106
|
+
},
|
|
107
|
+
router,
|
|
108
|
+
])
|
|
109
|
+
|
|
80
110
|
this.server = HTTP.Server(handler)
|
|
81
111
|
}
|
|
82
112
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name healthCheckHandler
|
|
3
|
+
*
|
|
4
|
+
* @docs
|
|
5
|
+
* ```specscript
|
|
6
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
7
|
+
*
|
|
8
|
+
* healthCheckHandler(request HTTP.Request, response HTTP.Response) -> ()
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
function healthCheckHandler(request, response) {
|
|
12
|
+
response.writeHead(200, {
|
|
13
|
+
'Content-Type': 'text/plain',
|
|
14
|
+
})
|
|
15
|
+
response.end('OK')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = healthCheckHandler
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @name notFoundHandler
|
|
3
|
+
*
|
|
4
|
+
* @docs
|
|
5
|
+
* ```specscript
|
|
6
|
+
* module HTTP "https://presidium.services/docs/HTTP"
|
|
7
|
+
*
|
|
8
|
+
* notFoundHandler(request HTTP.Request, response HTTP.Response) -> ()
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
function notFoundHandler(request, response) {
|
|
12
|
+
response.writeHead(404, {
|
|
13
|
+
"Content-Type": "text/plain",
|
|
14
|
+
})
|
|
15
|
+
response.end("Not Found")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = notFoundHandler
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "presidium-db-client-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Presidium DB client and server library",
|
|
5
5
|
"author": "Richard Tong",
|
|
6
6
|
"license": "CFOSS",
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"TextEntriesStream.js",
|
|
22
22
|
"TextStream.js",
|
|
23
23
|
"buffersToEntry.js",
|
|
24
|
-
"handleServerResponse.js"
|
|
24
|
+
"handleServerResponse.js",
|
|
25
|
+
"healthCheckHandler.js",
|
|
26
|
+
"notFoundHandler.js"
|
|
25
27
|
],
|
|
26
28
|
"keywords": [
|
|
27
29
|
"presidium",
|