generated from hyper63/adapter-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
meta.ts
129 lines (119 loc) · 3.53 KB
/
meta.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { crocks, cuid, HyperErr } from './deps.ts'
import type { MongoInstanceClient } from './clients/types.ts'
const { Async } = crocks
type DatabaseMeta = {
_id: string
name: string
type: 'database'
createdAt: string
}
/**
* MongoDB implicitly creates databases whenever data is first inserted
* into the database. So in order to properly respond in the hyper way ie.
* 404 database not found or 409 database already exists, we need to keep track of
* what databases exist. That is what this class is for
*
* For each hyper data store, we will store a document in this "meta" mongo database.
*
* This will then be used as part of validating whether a hyper data service exists or not.
*/
export class MetaDb {
private client: MongoInstanceClient
private metaDbName: string
constructor({
client,
metaDbName,
}: {
client: MongoInstanceClient
metaDbName: string
}) {
this.client = client
this.metaDbName = metaDbName
}
/**
* Retrieve the metadata doc for the hyper data service identified
* by the name. Useful to check whether a database exists
*/
get(name: string) {
return Async.of(name)
.chain((name) =>
name === this.metaDbName
? Async.Rejected(
HyperErr({ status: 422, msg: `${name} is a reserved db name` }),
)
: Async.Resolved(name)
)
.chain(() =>
Async.of(
this.client
.db(this.metaDbName)
.collection<DatabaseMeta>(this.metaDbName),
)
.chain(
Async.fromPromise((collection) => collection.findOne({ _id: name }, {})),
)
.chain<ReturnType<typeof HyperErr>, DatabaseMeta>((doc) =>
// deno-lint-ignore ban-ts-comment
// @ts-ignore
doc ? Async.Resolved(doc) : Async.Rejected(
HyperErr({ status: 404, msg: `database does not exist` }),
)
)
)
}
/**
* Store metadata doc on the hyper data service.
*
* If a data service with that name already exists, then
* this will reject with a HyperErr(409)
*/
create(name: string) {
return this.get(name).bichain(
// Map DNE to creating a db record
() =>
Async.of(
this.client
.db(this.metaDbName)
.collection<DatabaseMeta>(this.metaDbName),
).chain(
Async.fromPromise((collection) => {
const doc = {
_id: name,
// Use a cuid for the actual name of the database
name: cuid(),
type: 'database' as const,
createdAt: new Date().toISOString(),
}
return collection.insertOne(doc).then(() => doc)
}),
),
// DB was found, so produce a conflict
() =>
Async.Rejected(
HyperErr({ status: 409, msg: 'database already exists' }),
),
)
}
/**
* Delete metadata document on the hyper data service.
*
* If a data service with that name does not exist, then
* this will reject with a HyperErr(404)
*/
remove(name: string) {
return Async.of(
this.client.db(this.metaDbName).collection<DatabaseMeta>(this.metaDbName),
)
.chain(
Async.fromPromise((collection) => collection.deleteOne({ _id: name })),
)
.chain(({ deletedCount }) => {
if (deletedCount !== 1) {
return Async.Rejected(
HyperErr({ status: 404, msg: 'database does not exist' }),
)
}
return Async.Resolved({ ok: true })
})
}
}