@atproto/bsync 0.0.31 → 0.0.33
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 +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/logger.js +2 -2
- package/dist/logger.js.map +1 -1
- package/package.json +17 -13
- package/bin/migration-create.ts +0 -38
- package/buf.gen.yaml +0 -12
- package/jest.config.cjs +0 -21
- package/proto/bsync.proto +0 -134
- package/src/client.ts +0 -25
- package/src/config.ts +0 -90
- package/src/context.ts +0 -48
- package/src/db/index.ts +0 -200
- package/src/db/migrations/20240108T220751294Z-init.ts +0 -26
- package/src/db/migrations/20240717T224303472Z-notif-ops.ts +0 -24
- package/src/db/migrations/20250527T022203400Z-add-operation.ts +0 -20
- package/src/db/migrations/20250603T163446567Z-alter-operation.ts +0 -19
- package/src/db/migrations/index.ts +0 -8
- package/src/db/migrations/provider.ts +0 -8
- package/src/db/schema/index.ts +0 -16
- package/src/db/schema/mute_item.ts +0 -13
- package/src/db/schema/mute_op.ts +0 -18
- package/src/db/schema/notif_item.ts +0 -13
- package/src/db/schema/notif_op.ts +0 -16
- package/src/db/schema/operation.ts +0 -20
- package/src/db/types.ts +0 -19
- package/src/index.ts +0 -130
- package/src/logger.ts +0 -26
- package/src/routes/add-mute-operation.ts +0 -154
- package/src/routes/add-notif-operation.ts +0 -80
- package/src/routes/auth.ts +0 -15
- package/src/routes/delete-operations.ts +0 -45
- package/src/routes/index.ts +0 -28
- package/src/routes/put-operation.ts +0 -115
- package/src/routes/scan-mute-operations.ts +0 -65
- package/src/routes/scan-notif-operations.ts +0 -64
- package/src/routes/scan-operations.ts +0 -67
- package/src/routes/util.ts +0 -67
- package/tests/delete-operations.test.ts +0 -108
- package/tests/mutes.test.ts +0 -352
- package/tests/notifications.test.ts +0 -209
- package/tests/operations.test.ts +0 -327
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -7
package/src/routes/util.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { Code, ConnectError } from '@connectrpc/connect'
|
|
2
|
-
import {
|
|
3
|
-
InvalidDidError,
|
|
4
|
-
ensureValidAtUri,
|
|
5
|
-
ensureValidDid,
|
|
6
|
-
ensureValidNsid,
|
|
7
|
-
} from '@atproto/syntax'
|
|
8
|
-
|
|
9
|
-
export const validCursor = (cursor: string): number | null => {
|
|
10
|
-
if (cursor === '') return null
|
|
11
|
-
const int = parseInt(cursor, 10)
|
|
12
|
-
if (isNaN(int) || int < 0) {
|
|
13
|
-
throw new ConnectError('invalid cursor', Code.InvalidArgument)
|
|
14
|
-
}
|
|
15
|
-
return int
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const combineSignals = (a: AbortSignal, b: AbortSignal): AbortSignal => {
|
|
19
|
-
const controller = new AbortController()
|
|
20
|
-
for (const signal of [a, b]) {
|
|
21
|
-
if (signal.aborted) {
|
|
22
|
-
controller.abort()
|
|
23
|
-
return signal
|
|
24
|
-
}
|
|
25
|
-
signal.addEventListener('abort', () => controller.abort(signal.reason), {
|
|
26
|
-
// @ts-ignore https://github.com/DefinitelyTyped/DefinitelyTyped/pull/68625
|
|
27
|
-
signal: controller.signal,
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
return controller.signal
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const isValidDid = (did: string) => {
|
|
34
|
-
try {
|
|
35
|
-
ensureValidDid(did)
|
|
36
|
-
return true
|
|
37
|
-
} catch (err) {
|
|
38
|
-
if (err instanceof InvalidDidError) {
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
throw err
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const isValidAtUri = (uri: string) => {
|
|
46
|
-
try {
|
|
47
|
-
ensureValidAtUri(uri)
|
|
48
|
-
return true
|
|
49
|
-
} catch {
|
|
50
|
-
return false
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const validateNamespace = (namespace: string): void => {
|
|
55
|
-
const parts = namespace.split('#')
|
|
56
|
-
|
|
57
|
-
if (parts.length !== 1 && parts.length !== 2) {
|
|
58
|
-
throw new Error('namespace must be in the format "nsid[#fragment]"')
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const [nsid, fragment] = parts
|
|
62
|
-
|
|
63
|
-
ensureValidNsid(nsid)
|
|
64
|
-
if (fragment && !/^[a-zA-Z][a-zA-Z0-9]*$/.test(fragment)) {
|
|
65
|
-
throw new Error('namespace fragment must be a valid identifier')
|
|
66
|
-
}
|
|
67
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import getPort from 'get-port'
|
|
2
|
-
import {
|
|
3
|
-
BsyncClient,
|
|
4
|
-
BsyncService,
|
|
5
|
-
Database,
|
|
6
|
-
authWithApiKey,
|
|
7
|
-
createClient,
|
|
8
|
-
envToCfg,
|
|
9
|
-
} from '../src/index.js'
|
|
10
|
-
import { Method } from '../src/proto/bsync_pb.js'
|
|
11
|
-
|
|
12
|
-
describe('operations', () => {
|
|
13
|
-
let bsync: BsyncService
|
|
14
|
-
let client: BsyncClient
|
|
15
|
-
|
|
16
|
-
const validPayload0 = Buffer.from(JSON.stringify({ value: 0 }))
|
|
17
|
-
const validPayload1 = Buffer.from(JSON.stringify({ value: 1 }))
|
|
18
|
-
|
|
19
|
-
beforeAll(async () => {
|
|
20
|
-
bsync = await BsyncService.create(
|
|
21
|
-
envToCfg({
|
|
22
|
-
port: await getPort(),
|
|
23
|
-
dbUrl: process.env.DB_POSTGRES_URL,
|
|
24
|
-
dbSchema: 'bsync_delete_operations',
|
|
25
|
-
apiKeys: ['key-1'],
|
|
26
|
-
longPollTimeoutMs: 500,
|
|
27
|
-
}),
|
|
28
|
-
)
|
|
29
|
-
await bsync.ctx.db.migrateToLatestOrThrow()
|
|
30
|
-
await bsync.start()
|
|
31
|
-
client = createClient({
|
|
32
|
-
httpVersion: '1.1',
|
|
33
|
-
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
34
|
-
interceptors: [authWithApiKey('key-1')],
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
afterAll(async () => {
|
|
39
|
-
await bsync.destroy()
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
beforeEach(async () => {
|
|
43
|
-
await clearOps(bsync.ctx.db)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('deletes', async () => {
|
|
47
|
-
const res1 = await client.putOperation({
|
|
48
|
-
actorDid: 'did:example:a',
|
|
49
|
-
namespace: 'app.bsky.some.col',
|
|
50
|
-
key: 'key1',
|
|
51
|
-
method: Method.CREATE,
|
|
52
|
-
payload: validPayload0,
|
|
53
|
-
})
|
|
54
|
-
const res2 = await client.putOperation({
|
|
55
|
-
actorDid: 'did:example:a',
|
|
56
|
-
namespace: 'app.bsky.other.col#id',
|
|
57
|
-
key: 'key1',
|
|
58
|
-
method: Method.UPDATE,
|
|
59
|
-
payload: validPayload1,
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
expect(res1.operation?.id).toBe('1')
|
|
63
|
-
expect(res2.operation?.id).toBe('2')
|
|
64
|
-
expect(await dumpOps(bsync.ctx.db)).toStrictEqual([
|
|
65
|
-
{
|
|
66
|
-
id: 1,
|
|
67
|
-
actorDid: 'did:example:a',
|
|
68
|
-
namespace: 'app.bsky.some.col',
|
|
69
|
-
key: 'key1',
|
|
70
|
-
method: Method.CREATE,
|
|
71
|
-
payload: validPayload0,
|
|
72
|
-
createdAt: expect.any(Date),
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
id: 2,
|
|
76
|
-
actorDid: 'did:example:a',
|
|
77
|
-
namespace: 'app.bsky.other.col#id',
|
|
78
|
-
key: 'key1',
|
|
79
|
-
method: Method.UPDATE,
|
|
80
|
-
payload: validPayload1,
|
|
81
|
-
createdAt: expect.any(Date),
|
|
82
|
-
},
|
|
83
|
-
])
|
|
84
|
-
|
|
85
|
-
await client.deleteOperationsByActorAndNamespace({
|
|
86
|
-
actorDid: 'did:example:a',
|
|
87
|
-
namespace: 'app.bsky.some.col',
|
|
88
|
-
})
|
|
89
|
-
await client.deleteOperationsByActorAndNamespace({
|
|
90
|
-
actorDid: 'did:example:a',
|
|
91
|
-
namespace: 'app.bsky.other.col#id',
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
expect(await dumpOps(bsync.ctx.db)).toStrictEqual([])
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
const dumpOps = async (db: Database) => {
|
|
99
|
-
return db.db
|
|
100
|
-
.selectFrom('operation')
|
|
101
|
-
.selectAll()
|
|
102
|
-
.orderBy('id', 'asc')
|
|
103
|
-
.execute()
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const clearOps = async (db: Database) => {
|
|
107
|
-
await db.db.deleteFrom('operation').execute()
|
|
108
|
-
}
|
package/tests/mutes.test.ts
DELETED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import { Code, ConnectError } from '@connectrpc/connect'
|
|
2
|
-
import getPort from 'get-port'
|
|
3
|
-
import { wait } from '@atproto/common'
|
|
4
|
-
import {
|
|
5
|
-
BsyncClient,
|
|
6
|
-
BsyncService,
|
|
7
|
-
Database,
|
|
8
|
-
authWithApiKey,
|
|
9
|
-
createClient,
|
|
10
|
-
envToCfg,
|
|
11
|
-
} from '../src/index.js'
|
|
12
|
-
import { MuteOperation, MuteOperation_Type } from '../src/proto/bsync_pb.js'
|
|
13
|
-
|
|
14
|
-
describe('mutes', () => {
|
|
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_mutes',
|
|
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 clearMutes(bsync.ctx.db)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
describe('addMuteOperation', () => {
|
|
46
|
-
it('adds mute operations to add mutes.', async () => {
|
|
47
|
-
await client.addMuteOperation({
|
|
48
|
-
type: MuteOperation_Type.ADD,
|
|
49
|
-
actorDid: 'did:example:a',
|
|
50
|
-
subject: 'did:example:b',
|
|
51
|
-
})
|
|
52
|
-
await client.addMuteOperation({
|
|
53
|
-
type: MuteOperation_Type.ADD,
|
|
54
|
-
actorDid: 'did:example:a',
|
|
55
|
-
subject: 'did:example:c',
|
|
56
|
-
})
|
|
57
|
-
// dupe has no effect
|
|
58
|
-
await client.addMuteOperation({
|
|
59
|
-
type: MuteOperation_Type.ADD,
|
|
60
|
-
actorDid: 'did:example:a',
|
|
61
|
-
subject: 'did:example:c',
|
|
62
|
-
})
|
|
63
|
-
await client.addMuteOperation({
|
|
64
|
-
type: MuteOperation_Type.ADD,
|
|
65
|
-
actorDid: 'did:example:b',
|
|
66
|
-
subject: 'did:example:c',
|
|
67
|
-
})
|
|
68
|
-
await client.addMuteOperation({
|
|
69
|
-
type: MuteOperation_Type.ADD,
|
|
70
|
-
actorDid: 'did:example:c',
|
|
71
|
-
subject: 'at://did:example:d/app.bsky.graph.list/rkey1',
|
|
72
|
-
})
|
|
73
|
-
expect(await dumpMuteState(bsync.ctx.db)).toEqual({
|
|
74
|
-
'did:example:a': ['did:example:b', 'did:example:c'],
|
|
75
|
-
'did:example:b': ['did:example:c'],
|
|
76
|
-
'did:example:c': ['at://did:example:d/app.bsky.graph.list/rkey1'],
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('adds mute operations to remove mutes.', async () => {
|
|
81
|
-
await client.addMuteOperation({
|
|
82
|
-
type: MuteOperation_Type.ADD,
|
|
83
|
-
actorDid: 'did:example:a',
|
|
84
|
-
subject: 'did:example:b',
|
|
85
|
-
})
|
|
86
|
-
await client.addMuteOperation({
|
|
87
|
-
type: MuteOperation_Type.ADD,
|
|
88
|
-
actorDid: 'did:example:a',
|
|
89
|
-
subject: 'did:example:c',
|
|
90
|
-
})
|
|
91
|
-
await client.addMuteOperation({
|
|
92
|
-
type: MuteOperation_Type.ADD,
|
|
93
|
-
actorDid: 'did:example:b',
|
|
94
|
-
subject: 'did:example:c',
|
|
95
|
-
})
|
|
96
|
-
await client.addMuteOperation({
|
|
97
|
-
type: MuteOperation_Type.REMOVE,
|
|
98
|
-
actorDid: 'did:example:a',
|
|
99
|
-
subject: 'did:example:c',
|
|
100
|
-
})
|
|
101
|
-
// removes nothing
|
|
102
|
-
await client.addMuteOperation({
|
|
103
|
-
type: MuteOperation_Type.REMOVE,
|
|
104
|
-
actorDid: 'did:example:b',
|
|
105
|
-
subject: 'did:example:d',
|
|
106
|
-
})
|
|
107
|
-
expect(await dumpMuteState(bsync.ctx.db)).toEqual({
|
|
108
|
-
'did:example:a': ['did:example:b'],
|
|
109
|
-
'did:example:b': ['did:example:c'],
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('adds mute operations to clear mutes.', async () => {
|
|
114
|
-
await client.addMuteOperation({
|
|
115
|
-
type: MuteOperation_Type.ADD,
|
|
116
|
-
actorDid: 'did:example:a',
|
|
117
|
-
subject: 'did:example:b',
|
|
118
|
-
})
|
|
119
|
-
await client.addMuteOperation({
|
|
120
|
-
type: MuteOperation_Type.ADD,
|
|
121
|
-
actorDid: 'did:example:a',
|
|
122
|
-
subject: 'did:example:c',
|
|
123
|
-
})
|
|
124
|
-
await client.addMuteOperation({
|
|
125
|
-
type: MuteOperation_Type.ADD,
|
|
126
|
-
actorDid: 'did:example:b',
|
|
127
|
-
subject: 'did:example:c',
|
|
128
|
-
})
|
|
129
|
-
await client.addMuteOperation({
|
|
130
|
-
type: MuteOperation_Type.CLEAR,
|
|
131
|
-
actorDid: 'did:example:a',
|
|
132
|
-
})
|
|
133
|
-
expect(await dumpMuteState(bsync.ctx.db)).toEqual({
|
|
134
|
-
'did:example:b': ['did:example:c'],
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('fails on bad inputs', async () => {
|
|
139
|
-
await expect(
|
|
140
|
-
client.addMuteOperation({
|
|
141
|
-
type: MuteOperation_Type.ADD,
|
|
142
|
-
actorDid: 'did:example:a',
|
|
143
|
-
subject: 'invalid',
|
|
144
|
-
}),
|
|
145
|
-
).rejects.toEqual(
|
|
146
|
-
new ConnectError(
|
|
147
|
-
'subject must be a did or aturi on add or remove op',
|
|
148
|
-
Code.InvalidArgument,
|
|
149
|
-
),
|
|
150
|
-
)
|
|
151
|
-
await expect(
|
|
152
|
-
client.addMuteOperation({
|
|
153
|
-
type: MuteOperation_Type.ADD,
|
|
154
|
-
actorDid: 'did:example:a',
|
|
155
|
-
}),
|
|
156
|
-
).rejects.toEqual(
|
|
157
|
-
new ConnectError(
|
|
158
|
-
'subject must be a did or aturi on add or remove op',
|
|
159
|
-
Code.InvalidArgument,
|
|
160
|
-
),
|
|
161
|
-
)
|
|
162
|
-
await expect(
|
|
163
|
-
client.addMuteOperation({
|
|
164
|
-
type: MuteOperation_Type.ADD,
|
|
165
|
-
actorDid: 'did:example:a',
|
|
166
|
-
subject: 'at://did:example:b/bad.collection/rkey1',
|
|
167
|
-
}),
|
|
168
|
-
).rejects.toEqual(
|
|
169
|
-
new ConnectError(
|
|
170
|
-
'subject must be a did or aturi on add or remove op',
|
|
171
|
-
Code.InvalidArgument,
|
|
172
|
-
),
|
|
173
|
-
)
|
|
174
|
-
await expect(
|
|
175
|
-
client.addMuteOperation({
|
|
176
|
-
type: MuteOperation_Type.ADD,
|
|
177
|
-
actorDid: 'invalid',
|
|
178
|
-
subject: 'did:example:b',
|
|
179
|
-
}),
|
|
180
|
-
).rejects.toEqual(
|
|
181
|
-
new ConnectError('actor_did must be a valid did', Code.InvalidArgument),
|
|
182
|
-
)
|
|
183
|
-
await expect(
|
|
184
|
-
client.addMuteOperation({
|
|
185
|
-
type: MuteOperation_Type.REMOVE,
|
|
186
|
-
actorDid: 'did:example:a',
|
|
187
|
-
subject: 'invalid',
|
|
188
|
-
}),
|
|
189
|
-
).rejects.toEqual(
|
|
190
|
-
new ConnectError(
|
|
191
|
-
'subject must be a did or aturi on add or remove op',
|
|
192
|
-
Code.InvalidArgument,
|
|
193
|
-
),
|
|
194
|
-
)
|
|
195
|
-
await expect(
|
|
196
|
-
client.addMuteOperation({
|
|
197
|
-
type: MuteOperation_Type.CLEAR,
|
|
198
|
-
actorDid: 'did:example:a',
|
|
199
|
-
subject: 'did:example:b',
|
|
200
|
-
}),
|
|
201
|
-
).rejects.toEqual(
|
|
202
|
-
new ConnectError(
|
|
203
|
-
'subject must not be set on a clear op',
|
|
204
|
-
Code.InvalidArgument,
|
|
205
|
-
),
|
|
206
|
-
)
|
|
207
|
-
await expect(
|
|
208
|
-
client.addMuteOperation({
|
|
209
|
-
type: MuteOperation_Type.CLEAR,
|
|
210
|
-
actorDid: 'invalid',
|
|
211
|
-
}),
|
|
212
|
-
).rejects.toEqual(
|
|
213
|
-
new ConnectError('actor_did must be a valid did', Code.InvalidArgument),
|
|
214
|
-
)
|
|
215
|
-
await expect(
|
|
216
|
-
client.addMuteOperation({
|
|
217
|
-
type: 100 as any,
|
|
218
|
-
actorDid: 'did:example:a',
|
|
219
|
-
subject: 'did:example:b',
|
|
220
|
-
}),
|
|
221
|
-
).rejects.toEqual(
|
|
222
|
-
new ConnectError('bad mute operation type', Code.InvalidArgument),
|
|
223
|
-
)
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
it('requires auth', async () => {
|
|
227
|
-
// unauthed
|
|
228
|
-
const unauthedClient = createClient({
|
|
229
|
-
httpVersion: '1.1',
|
|
230
|
-
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
231
|
-
})
|
|
232
|
-
const tryAddMuteOperation1 = unauthedClient.addMuteOperation({
|
|
233
|
-
type: MuteOperation_Type.ADD,
|
|
234
|
-
actorDid: 'did:example:a',
|
|
235
|
-
subject: 'did:example:b',
|
|
236
|
-
})
|
|
237
|
-
await expect(tryAddMuteOperation1).rejects.toEqual(
|
|
238
|
-
new ConnectError('missing auth', Code.Unauthenticated),
|
|
239
|
-
)
|
|
240
|
-
// bad auth
|
|
241
|
-
const badauthedClient = createClient({
|
|
242
|
-
httpVersion: '1.1',
|
|
243
|
-
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
244
|
-
interceptors: [authWithApiKey('key-bad')],
|
|
245
|
-
})
|
|
246
|
-
const tryAddMuteOperation2 = badauthedClient.addMuteOperation({
|
|
247
|
-
type: MuteOperation_Type.ADD,
|
|
248
|
-
actorDid: 'did:example:a',
|
|
249
|
-
subject: 'did:example:b',
|
|
250
|
-
})
|
|
251
|
-
await expect(tryAddMuteOperation2).rejects.toEqual(
|
|
252
|
-
new ConnectError('invalid api key', Code.Unauthenticated),
|
|
253
|
-
)
|
|
254
|
-
})
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
describe('scanMuteOperations', () => {
|
|
258
|
-
it('requires auth', async () => {
|
|
259
|
-
// unauthed
|
|
260
|
-
const unauthedClient = createClient({
|
|
261
|
-
httpVersion: '1.1',
|
|
262
|
-
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
263
|
-
})
|
|
264
|
-
const tryScanMuteOperations1 = unauthedClient.scanMuteOperations({})
|
|
265
|
-
await expect(tryScanMuteOperations1).rejects.toEqual(
|
|
266
|
-
new ConnectError('missing auth', Code.Unauthenticated),
|
|
267
|
-
)
|
|
268
|
-
// bad auth
|
|
269
|
-
const badauthedClient = createClient({
|
|
270
|
-
httpVersion: '1.1',
|
|
271
|
-
baseUrl: `http://localhost:${bsync.ctx.cfg.service.port}`,
|
|
272
|
-
interceptors: [authWithApiKey('key-bad')],
|
|
273
|
-
})
|
|
274
|
-
const tryScanMuteOperations2 = badauthedClient.scanMuteOperations({})
|
|
275
|
-
await expect(tryScanMuteOperations2).rejects.toEqual(
|
|
276
|
-
new ConnectError('invalid api key', Code.Unauthenticated),
|
|
277
|
-
)
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
it('pages over created mute ops.', async () => {
|
|
281
|
-
// add 100 mute ops
|
|
282
|
-
for (let i = 0; i < 10; ++i) {
|
|
283
|
-
for (let j = 0; j < 8; ++j) {
|
|
284
|
-
await client.addMuteOperation({
|
|
285
|
-
type: MuteOperation_Type.ADD,
|
|
286
|
-
actorDid: `did:example:${i}`,
|
|
287
|
-
subject: `did:example:${j}`,
|
|
288
|
-
})
|
|
289
|
-
}
|
|
290
|
-
for (let j = 0; j < 2; ++j) {
|
|
291
|
-
await client.addMuteOperation({
|
|
292
|
-
type: MuteOperation_Type.ADD,
|
|
293
|
-
actorDid: `did:example:${i}`,
|
|
294
|
-
subject: `at://did:example:0/app.bsky.graph.list/rkey${j}`,
|
|
295
|
-
})
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
let cursor: string | undefined
|
|
300
|
-
const operations: MuteOperation[] = []
|
|
301
|
-
do {
|
|
302
|
-
const res = await client.scanMuteOperations({
|
|
303
|
-
cursor,
|
|
304
|
-
limit: 30,
|
|
305
|
-
})
|
|
306
|
-
operations.push(...res.operations)
|
|
307
|
-
cursor = res.operations.length ? res.cursor : undefined
|
|
308
|
-
} while (cursor)
|
|
309
|
-
|
|
310
|
-
expect(operations.length).toEqual(100)
|
|
311
|
-
const operationIds = operations.map((op) => parseInt(op.id, 10))
|
|
312
|
-
const ascending = (a: number, b: number) => a - b
|
|
313
|
-
expect(operationIds).toEqual([...operationIds].sort(ascending))
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
it('supports long-poll, finding an operation.', async () => {
|
|
317
|
-
const scanPromise = client.scanMuteOperations({})
|
|
318
|
-
await wait(100) // would be complete by now if it wasn't long-polling for an item
|
|
319
|
-
const { operation } = await client.addMuteOperation({
|
|
320
|
-
type: MuteOperation_Type.ADD,
|
|
321
|
-
actorDid: 'did:example:a',
|
|
322
|
-
subject: 'did:example:b',
|
|
323
|
-
})
|
|
324
|
-
const res = await scanPromise
|
|
325
|
-
expect(res.operations.length).toEqual(1)
|
|
326
|
-
expect(res.operations[0]).toEqual(operation)
|
|
327
|
-
expect(res.cursor).toEqual(operation?.id)
|
|
328
|
-
})
|
|
329
|
-
|
|
330
|
-
it('supports long-poll, not finding an operation.', async () => {
|
|
331
|
-
const res = await client.scanMuteOperations({})
|
|
332
|
-
expect(res.cursor).toEqual('')
|
|
333
|
-
expect(res.operations).toEqual([])
|
|
334
|
-
})
|
|
335
|
-
})
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
const dumpMuteState = async (db: Database) => {
|
|
339
|
-
const items = await db.db.selectFrom('mute_item').selectAll().execute()
|
|
340
|
-
const result: Record<string, string[]> = {}
|
|
341
|
-
items.forEach((item) => {
|
|
342
|
-
result[item.actorDid] ??= []
|
|
343
|
-
result[item.actorDid].push(item.subject)
|
|
344
|
-
})
|
|
345
|
-
Object.values(result).forEach((subjects) => subjects.sort())
|
|
346
|
-
return result
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const clearMutes = async (db: Database) => {
|
|
350
|
-
await db.db.deleteFrom('mute_item').execute()
|
|
351
|
-
await db.db.deleteFrom('mute_op').execute()
|
|
352
|
-
}
|