@atproto/bsync 0.0.3 → 0.0.5
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 +15 -0
- package/buf.gen.yaml +2 -2
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -0
- package/dist/context.js.map +1 -1
- package/dist/db/migrations/20240717T224303472Z-notif-ops.d.ts +4 -0
- package/dist/db/migrations/20240717T224303472Z-notif-ops.d.ts.map +1 -0
- package/dist/db/migrations/20240717T224303472Z-notif-ops.js +26 -0
- package/dist/db/migrations/20240717T224303472Z-notif-ops.js.map +1 -0
- package/dist/db/migrations/index.d.ts +1 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +2 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/schema/index.d.ts +3 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/notif_item.d.ts +12 -0
- package/dist/db/schema/notif_item.d.ts.map +1 -0
- package/dist/db/schema/notif_item.js +5 -0
- package/dist/db/schema/notif_item.js.map +1 -0
- package/dist/db/schema/notif_op.d.ts +14 -0
- package/dist/db/schema/notif_op.d.ts.map +1 -0
- package/dist/db/schema/notif_op.js +6 -0
- package/dist/db/schema/notif_op.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +4 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +31 -8
- package/dist/logger.js.map +1 -1
- package/dist/proto/bsync_connect.d.ts +19 -1
- package/dist/proto/bsync_connect.d.ts.map +1 -1
- package/dist/proto/bsync_connect.js +19 -1
- package/dist/proto/bsync_connect.js.map +1 -1
- package/dist/proto/bsync_pb.d.ts +105 -0
- package/dist/proto/bsync_pb.d.ts.map +1 -1
- package/dist/proto/bsync_pb.js +325 -2
- package/dist/proto/bsync_pb.js.map +1 -1
- package/dist/routes/add-mute-operation.d.ts.map +1 -1
- package/dist/routes/add-mute-operation.js +7 -26
- package/dist/routes/add-mute-operation.js.map +1 -1
- package/dist/routes/add-notif-operation.d.ts +6 -0
- package/dist/routes/add-notif-operation.d.ts.map +1 -0
- package/dist/routes/add-notif-operation.js +63 -0
- package/dist/routes/add-notif-operation.js.map +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +4 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/scan-mute-operations.d.ts.map +1 -1
- package/dist/routes/scan-mute-operations.js +3 -26
- package/dist/routes/scan-mute-operations.js.map +1 -1
- package/dist/routes/scan-notif-operations.d.ts +6 -0
- package/dist/routes/scan-notif-operations.d.ts.map +1 -0
- package/dist/routes/scan-notif-operations.js +56 -0
- package/dist/routes/scan-notif-operations.js.map +1 -0
- package/dist/routes/util.d.ts +6 -0
- package/dist/routes/util.d.ts.map +1 -0
- package/dist/routes/util.js +54 -0
- package/dist/routes/util.js.map +1 -0
- package/package.json +3 -2
- package/proto/bsync.proto +29 -0
- package/src/context.ts +2 -0
- package/src/db/migrations/20240717T224303472Z-notif-ops.ts +24 -0
- package/src/db/migrations/index.ts +1 -0
- package/src/db/schema/index.ts +6 -1
- package/src/db/schema/notif_item.ts +13 -0
- package/src/db/schema/notif_op.ts +16 -0
- package/src/index.ts +8 -2
- package/src/logger.ts +11 -7
- package/src/proto/bsync_connect.ts +23 -1
- package/src/proto/bsync_pb.ts +318 -1
- package/src/routes/add-mute-operation.ts +7 -29
- package/src/routes/add-notif-operation.ts +80 -0
- package/src/routes/index.ts +4 -0
- package/src/routes/scan-mute-operations.ts +2 -25
- package/src/routes/scan-notif-operations.ts +64 -0
- package/src/routes/util.ts +51 -0
- package/tests/mutes.test.ts +2 -0
- package/tests/notifications.test.ts +209 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { wait } from '@atproto/common'
|
|
2
|
+
import getPort from 'get-port'
|
|
3
|
+
import { Code, ConnectError } from '@connectrpc/connect'
|
|
4
|
+
import {
|
|
5
|
+
BsyncClient,
|
|
6
|
+
BsyncService,
|
|
7
|
+
Database,
|
|
8
|
+
authWithApiKey,
|
|
9
|
+
createClient,
|
|
10
|
+
envToCfg,
|
|
11
|
+
} from '../src'
|
|
12
|
+
import { NotifOperation } from '../src/proto/bsync_pb'
|
|
13
|
+
|
|
14
|
+
describe('notifications', () => {
|
|
15
|
+
let bsync: BsyncService
|
|
16
|
+
let client: BsyncClient
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
bsync = await BsyncService.create(
|
|
20
|
+
envToCfg({
|
|
21
|
+
port: await getPort(),
|
|
22
|
+
dbUrl: process.env.DB_POSTGRES_URL,
|
|
23
|
+
dbSchema: 'bsync_notifications',
|
|
24
|
+
apiKeys: ['key-1'],
|
|
25
|
+
longPollTimeoutMs: 500,
|
|
26
|
+
}),
|
|
27
|
+
)
|
|
28
|
+
await bsync.ctx.db.migrateToLatestOrThrow()
|
|
29
|
+
await bsync.start()
|
|
30
|
+
client = createClient({
|
|
31
|
+
httpVersion: '1.1',
|
|
32
|
+
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
33
|
+
interceptors: [authWithApiKey('key-1')],
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
afterAll(async () => {
|
|
38
|
+
await bsync.destroy()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
beforeEach(async () => {
|
|
42
|
+
await clearNotifs(bsync.ctx.db)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('addNotifOperation', () => {
|
|
46
|
+
it('adds notif operations to set priority.', async () => {
|
|
47
|
+
// true + true
|
|
48
|
+
await client.addNotifOperation({
|
|
49
|
+
actorDid: 'did:example:a',
|
|
50
|
+
priority: true,
|
|
51
|
+
})
|
|
52
|
+
await client.addNotifOperation({
|
|
53
|
+
actorDid: 'did:example:a',
|
|
54
|
+
priority: true,
|
|
55
|
+
})
|
|
56
|
+
// true + none
|
|
57
|
+
await client.addNotifOperation({
|
|
58
|
+
actorDid: 'did:example:b',
|
|
59
|
+
priority: true,
|
|
60
|
+
})
|
|
61
|
+
await client.addNotifOperation({
|
|
62
|
+
actorDid: 'did:example:b',
|
|
63
|
+
})
|
|
64
|
+
// true + false
|
|
65
|
+
await client.addNotifOperation({
|
|
66
|
+
actorDid: 'did:example:c',
|
|
67
|
+
priority: true,
|
|
68
|
+
})
|
|
69
|
+
await client.addNotifOperation({
|
|
70
|
+
actorDid: 'did:example:c',
|
|
71
|
+
priority: false,
|
|
72
|
+
})
|
|
73
|
+
// false + true
|
|
74
|
+
await client.addNotifOperation({
|
|
75
|
+
actorDid: 'did:example:d',
|
|
76
|
+
priority: false,
|
|
77
|
+
})
|
|
78
|
+
await client.addNotifOperation({
|
|
79
|
+
actorDid: 'did:example:d',
|
|
80
|
+
priority: true,
|
|
81
|
+
})
|
|
82
|
+
expect(await dumpNotifState(bsync.ctx.db)).toEqual({
|
|
83
|
+
'did:example:a': true,
|
|
84
|
+
'did:example:b': true,
|
|
85
|
+
'did:example:c': false,
|
|
86
|
+
'did:example:d': true,
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('fails on bad inputs', async () => {
|
|
91
|
+
await expect(
|
|
92
|
+
client.addNotifOperation({
|
|
93
|
+
actorDid: 'invalid',
|
|
94
|
+
priority: true,
|
|
95
|
+
}),
|
|
96
|
+
).rejects.toEqual(
|
|
97
|
+
new ConnectError('actor_did must be a valid did', Code.InvalidArgument),
|
|
98
|
+
)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('requires auth', async () => {
|
|
102
|
+
// unauthed
|
|
103
|
+
const unauthedClient = createClient({
|
|
104
|
+
httpVersion: '1.1',
|
|
105
|
+
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
106
|
+
})
|
|
107
|
+
const tryAddNotifOperation1 = unauthedClient.addNotifOperation({
|
|
108
|
+
actorDid: 'did:example:a',
|
|
109
|
+
})
|
|
110
|
+
await expect(tryAddNotifOperation1).rejects.toEqual(
|
|
111
|
+
new ConnectError('missing auth', Code.Unauthenticated),
|
|
112
|
+
)
|
|
113
|
+
// bad auth
|
|
114
|
+
const badauthedClient = createClient({
|
|
115
|
+
httpVersion: '1.1',
|
|
116
|
+
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
117
|
+
interceptors: [authWithApiKey('key-bad')],
|
|
118
|
+
})
|
|
119
|
+
const tryAddNotifOperation2 = badauthedClient.addNotifOperation({
|
|
120
|
+
actorDid: 'did:example:a',
|
|
121
|
+
})
|
|
122
|
+
await expect(tryAddNotifOperation2).rejects.toEqual(
|
|
123
|
+
new ConnectError('invalid api key', Code.Unauthenticated),
|
|
124
|
+
)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
describe('scanNotifOperations', () => {
|
|
129
|
+
it('requires auth', async () => {
|
|
130
|
+
// unauthed
|
|
131
|
+
const unauthedClient = createClient({
|
|
132
|
+
httpVersion: '1.1',
|
|
133
|
+
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
134
|
+
})
|
|
135
|
+
const tryScanNotifOperations1 = unauthedClient.scanNotifOperations({})
|
|
136
|
+
await expect(tryScanNotifOperations1).rejects.toEqual(
|
|
137
|
+
new ConnectError('missing auth', Code.Unauthenticated),
|
|
138
|
+
)
|
|
139
|
+
// bad auth
|
|
140
|
+
const badauthedClient = createClient({
|
|
141
|
+
httpVersion: '1.1',
|
|
142
|
+
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
143
|
+
interceptors: [authWithApiKey('key-bad')],
|
|
144
|
+
})
|
|
145
|
+
const tryScanNotifOperations2 = badauthedClient.scanNotifOperations({})
|
|
146
|
+
await expect(tryScanNotifOperations2).rejects.toEqual(
|
|
147
|
+
new ConnectError('invalid api key', Code.Unauthenticated),
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('pages over created notif ops.', async () => {
|
|
152
|
+
// add 100 notif ops
|
|
153
|
+
for (let i = 0; i < 100; ++i) {
|
|
154
|
+
await client.addNotifOperation({
|
|
155
|
+
actorDid: `did:example:${i}`,
|
|
156
|
+
priority: i % 2 === 0,
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let cursor: string | undefined
|
|
161
|
+
const operations: NotifOperation[] = []
|
|
162
|
+
do {
|
|
163
|
+
const res = await client.scanNotifOperations({
|
|
164
|
+
cursor,
|
|
165
|
+
limit: 30,
|
|
166
|
+
})
|
|
167
|
+
operations.push(...res.operations)
|
|
168
|
+
cursor = res.operations.length ? res.cursor : undefined
|
|
169
|
+
} while (cursor)
|
|
170
|
+
|
|
171
|
+
expect(operations.length).toEqual(100)
|
|
172
|
+
const operationIds = operations.map((op) => parseInt(op.id, 10))
|
|
173
|
+
const ascending = (a: number, b: number) => a - b
|
|
174
|
+
expect(operationIds).toEqual([...operationIds].sort(ascending))
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('supports long-poll, finding an operation.', async () => {
|
|
178
|
+
const scanPromise = client.scanNotifOperations({})
|
|
179
|
+
await wait(100) // would be complete by now if it wasn't long-polling for an item
|
|
180
|
+
const { operation } = await client.addNotifOperation({
|
|
181
|
+
actorDid: 'did:example:a',
|
|
182
|
+
})
|
|
183
|
+
const res = await scanPromise
|
|
184
|
+
expect(res.operations.length).toEqual(1)
|
|
185
|
+
expect(res.operations[0]).toEqual(operation)
|
|
186
|
+
expect(res.cursor).toEqual(operation?.id)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('supports long-poll, not finding an operation.', async () => {
|
|
190
|
+
const res = await client.scanNotifOperations({})
|
|
191
|
+
expect(res.cursor).toEqual('')
|
|
192
|
+
expect(res.operations).toEqual([])
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const dumpNotifState = async (db: Database) => {
|
|
198
|
+
const items = await db.db.selectFrom('notif_item').selectAll().execute()
|
|
199
|
+
const result: Record<string, boolean> = {}
|
|
200
|
+
items.forEach((item) => {
|
|
201
|
+
result[item.actorDid] = item.priority
|
|
202
|
+
})
|
|
203
|
+
return result
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const clearNotifs = async (db: Database) => {
|
|
207
|
+
await db.db.deleteFrom('notif_item').execute()
|
|
208
|
+
await db.db.deleteFrom('notif_op').execute()
|
|
209
|
+
}
|