@atproto/bsky 0.0.123 → 0.0.125
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 +23 -0
- package/dist/api/app/bsky/notification/listNotifications.d.ts +7 -0
- package/dist/api/app/bsky/notification/listNotifications.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/listNotifications.js +21 -5
- package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +24 -15
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +6 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +6 -0
- package/dist/context.js.map +1 -1
- package/dist/data-plane/client/hosts.d.ts +37 -0
- package/dist/data-plane/client/hosts.d.ts.map +1 -0
- package/dist/data-plane/client/hosts.js +106 -0
- package/dist/data-plane/client/hosts.js.map +1 -0
- package/dist/data-plane/client/index.d.ts +13 -0
- package/dist/data-plane/client/index.d.ts.map +1 -0
- package/dist/data-plane/client/index.js +133 -0
- package/dist/data-plane/client/index.js.map +1 -0
- package/dist/data-plane/{client.d.ts → client/util.d.ts} +3 -10
- package/dist/data-plane/client/util.d.ts.map +1 -0
- package/dist/data-plane/client/util.js +85 -0
- package/dist/data-plane/client/util.js.map +1 -0
- package/dist/data-plane/server/db/pagination.d.ts +69 -9
- package/dist/data-plane/server/db/pagination.d.ts.map +1 -1
- package/dist/data-plane/server/db/pagination.js +114 -14
- package/dist/data-plane/server/db/pagination.js.map +1 -1
- package/dist/data-plane/server/routes/notifs.d.ts.map +1 -1
- package/dist/data-plane/server/routes/notifs.js +3 -5
- package/dist/data-plane/server/routes/notifs.js.map +1 -1
- package/dist/etcd.d.ts +25 -0
- package/dist/etcd.d.ts.map +1 -0
- package/dist/etcd.js +109 -0
- package/dist/etcd.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +6 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +12 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +412 -158
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +222 -81
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/video.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/embed/video.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/video.js.map +1 -1
- package/dist/lexicon/types/app/bsky/labeler/defs.d.ts +7 -0
- package/dist/lexicon/types/app/bsky/labeler/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/labeler/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/labeler/service.d.ts +7 -0
- package/dist/lexicon/types/app/bsky/labeler/service.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/labeler/service.js.map +1 -1
- package/dist/lexicon/types/com/atproto/identity/defs.d.ts +17 -0
- package/dist/lexicon/types/com/atproto/identity/defs.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/defs.js +16 -0
- package/dist/lexicon/types/com/atproto/identity/defs.js.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.d.ts +39 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.js +7 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.js.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.d.ts +40 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.js +7 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.js.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveHandle.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveHandle.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.d.ts +36 -0
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.js +7 -0
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.js.map +1 -0
- package/dist/lexicon/types/com/atproto/moderation/defs.d.ts +2 -0
- package/dist/lexicon/types/com/atproto/moderation/defs.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/repo/listRecords.d.ts +0 -4
- package/dist/lexicon/types/com/atproto/repo/listRecords.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/repo/listRecords.js.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/getRecord.d.ts +0 -2
- package/dist/lexicon/types/com/atproto/sync/getRecord.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts +1 -30
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js +0 -27
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -1
- package/dist/logger.js.map +1 -1
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +6 -3
- package/dist/views/index.js.map +1 -1
- package/package.json +14 -13
- package/src/api/app/bsky/notification/listNotifications.ts +28 -6
- package/src/config.ts +45 -15
- package/src/context.ts +12 -1
- package/src/data-plane/client/hosts.ts +103 -0
- package/src/data-plane/client/index.ts +123 -0
- package/src/data-plane/client/util.ts +66 -0
- package/src/data-plane/server/db/pagination.ts +158 -35
- package/src/data-plane/server/routes/notifs.ts +4 -9
- package/src/etcd.ts +90 -0
- package/src/index.ts +26 -2
- package/src/lexicon/index.ts +36 -0
- package/src/lexicon/lexicons.ts +243 -84
- package/src/lexicon/types/app/bsky/embed/video.ts +1 -0
- package/src/lexicon/types/app/bsky/labeler/defs.ts +7 -0
- package/src/lexicon/types/app/bsky/labeler/service.ts +7 -0
- package/src/lexicon/types/com/atproto/identity/defs.ts +30 -0
- package/src/lexicon/types/com/atproto/identity/refreshIdentity.ts +52 -0
- package/src/lexicon/types/com/atproto/identity/resolveDid.ts +52 -0
- package/src/lexicon/types/com/atproto/identity/resolveHandle.ts +1 -0
- package/src/lexicon/types/com/atproto/identity/resolveIdentity.ts +48 -0
- package/src/lexicon/types/com/atproto/moderation/defs.ts +3 -0
- package/src/lexicon/types/com/atproto/repo/listRecords.ts +0 -4
- package/src/lexicon/types/com/atproto/sync/getRecord.ts +0 -2
- package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +0 -59
- package/src/logger.ts +2 -0
- package/src/views/index.ts +6 -3
- package/tests/etcd.test.ts +301 -0
- package/tests/views/__snapshots__/labeler-service.test.ts.snap +46 -0
- package/tests/views/__snapshots__/notifications.test.ts.snap +3 -3
- package/tests/views/labeler-service.test.ts +50 -1
- package/tests/views/notifications.test.ts +190 -10
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
- package/dist/data-plane/client.d.ts.map +0 -1
- package/dist/data-plane/client.js +0 -156
- package/dist/data-plane/client.js.map +0 -1
- package/src/data-plane/client.ts +0 -154
|
@@ -20,10 +20,6 @@ export interface QueryParams {
|
|
|
20
20
|
/** The number of records to return. */
|
|
21
21
|
limit: number
|
|
22
22
|
cursor?: string
|
|
23
|
-
/** DEPRECATED: The lowest sort-ordered rkey to start from (exclusive) */
|
|
24
|
-
rkeyStart?: string
|
|
25
|
-
/** DEPRECATED: The highest sort-ordered rkey to stop at (exclusive) */
|
|
26
|
-
rkeyEnd?: string
|
|
27
23
|
/** Flag to reverse the order of the returned records. */
|
|
28
24
|
reverse?: boolean
|
|
29
25
|
}
|
|
@@ -22,9 +22,6 @@ export type OutputSchema =
|
|
|
22
22
|
| $Typed<Sync>
|
|
23
23
|
| $Typed<Identity>
|
|
24
24
|
| $Typed<Account>
|
|
25
|
-
| $Typed<Handle>
|
|
26
|
-
| $Typed<Migrate>
|
|
27
|
-
| $Typed<Tombstone>
|
|
28
25
|
| $Typed<Info>
|
|
29
26
|
| { $type: string }
|
|
30
27
|
export type HandlerError = ErrorFrame<'FutureCursor' | 'ConsumerTooSlow'>
|
|
@@ -150,62 +147,6 @@ export function validateAccount<V>(v: V) {
|
|
|
150
147
|
return validate<Account & V>(v, id, hashAccount)
|
|
151
148
|
}
|
|
152
149
|
|
|
153
|
-
/** DEPRECATED -- Use #identity event instead */
|
|
154
|
-
export interface Handle {
|
|
155
|
-
$type?: 'com.atproto.sync.subscribeRepos#handle'
|
|
156
|
-
seq: number
|
|
157
|
-
did: string
|
|
158
|
-
handle: string
|
|
159
|
-
time: string
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const hashHandle = 'handle'
|
|
163
|
-
|
|
164
|
-
export function isHandle<V>(v: V) {
|
|
165
|
-
return is$typed(v, id, hashHandle)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function validateHandle<V>(v: V) {
|
|
169
|
-
return validate<Handle & V>(v, id, hashHandle)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** DEPRECATED -- Use #account event instead */
|
|
173
|
-
export interface Migrate {
|
|
174
|
-
$type?: 'com.atproto.sync.subscribeRepos#migrate'
|
|
175
|
-
seq: number
|
|
176
|
-
did: string
|
|
177
|
-
migrateTo: string | null
|
|
178
|
-
time: string
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const hashMigrate = 'migrate'
|
|
182
|
-
|
|
183
|
-
export function isMigrate<V>(v: V) {
|
|
184
|
-
return is$typed(v, id, hashMigrate)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function validateMigrate<V>(v: V) {
|
|
188
|
-
return validate<Migrate & V>(v, id, hashMigrate)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** DEPRECATED -- Use #account event instead */
|
|
192
|
-
export interface Tombstone {
|
|
193
|
-
$type?: 'com.atproto.sync.subscribeRepos#tombstone'
|
|
194
|
-
seq: number
|
|
195
|
-
did: string
|
|
196
|
-
time: string
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const hashTombstone = 'tombstone'
|
|
200
|
-
|
|
201
|
-
export function isTombstone<V>(v: V) {
|
|
202
|
-
return is$typed(v, id, hashTombstone)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function validateTombstone<V>(v: V) {
|
|
206
|
-
return validate<Tombstone & V>(v, id, hashTombstone)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
150
|
export interface Info {
|
|
210
151
|
$type?: 'com.atproto.sync.subscribeRepos#info'
|
|
211
152
|
name: 'OutdatedCursor' | (string & {})
|
package/src/logger.ts
CHANGED
|
@@ -15,6 +15,8 @@ export const hydrationLogger: ReturnType<typeof subsystemLogger> =
|
|
|
15
15
|
subsystemLogger('bsky:hydration')
|
|
16
16
|
export const featureGatesLogger: ReturnType<typeof subsystemLogger> =
|
|
17
17
|
subsystemLogger('bsky:featuregates')
|
|
18
|
+
export const dataplaneLogger: ReturnType<typeof subsystemLogger> =
|
|
19
|
+
subsystemLogger('bsky:dp')
|
|
18
20
|
export const httpLogger: ReturnType<typeof subsystemLogger> =
|
|
19
21
|
subsystemLogger('bsky')
|
|
20
22
|
|
package/src/views/index.ts
CHANGED
|
@@ -592,12 +592,15 @@ export class Views {
|
|
|
592
592
|
): Un$Typed<LabelerViewDetailed> | undefined {
|
|
593
593
|
const baseView = this.labeler(did, state)
|
|
594
594
|
if (!baseView) return
|
|
595
|
-
const
|
|
596
|
-
if (!
|
|
595
|
+
const labeler = state.labelers?.get(did)
|
|
596
|
+
if (!labeler) return
|
|
597
597
|
|
|
598
598
|
return {
|
|
599
599
|
...baseView,
|
|
600
|
-
policies:
|
|
600
|
+
policies: labeler.record.policies,
|
|
601
|
+
reasonTypes: labeler.record.reasonTypes,
|
|
602
|
+
subjectTypes: labeler.record.subjectTypes,
|
|
603
|
+
subjectCollections: labeler.record.subjectCollections,
|
|
601
604
|
}
|
|
602
605
|
}
|
|
603
606
|
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import EventEmitter from 'node:events'
|
|
2
|
+
import { Etcd3, IKeyValue } from 'etcd3'
|
|
3
|
+
import { EtcdHostList } from '../src'
|
|
4
|
+
import { EtcdMap } from '../src/etcd'
|
|
5
|
+
|
|
6
|
+
describe('etcd', () => {
|
|
7
|
+
describe('EtcdMap', () => {
|
|
8
|
+
it('initializes values based on current keys', async () => {
|
|
9
|
+
const etcd = new MockEtcd()
|
|
10
|
+
etcd.watcher.set('service/a', { value: '1' })
|
|
11
|
+
etcd.watcher.set('service/b', { value: '2' })
|
|
12
|
+
etcd.watcher.set('service/c', { value: '3' })
|
|
13
|
+
const map = new EtcdMap(etcd as unknown as Etcd3)
|
|
14
|
+
await map.connect()
|
|
15
|
+
expect(map.get('service/a')).toBe('1')
|
|
16
|
+
expect(map.get('service/b')).toBe('2')
|
|
17
|
+
expect(map.get('service/c')).toBe('3')
|
|
18
|
+
expect([...map.values()]).toEqual(['1', '2', '3'])
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('maintains key updates', async () => {
|
|
22
|
+
const etcd = new MockEtcd()
|
|
23
|
+
etcd.watcher.set('service/a', { value: '1' })
|
|
24
|
+
etcd.watcher.set('service/b', { value: '2' })
|
|
25
|
+
etcd.watcher.set('service/c', { value: '3' })
|
|
26
|
+
const map = new EtcdMap(etcd as unknown as Etcd3)
|
|
27
|
+
await map.connect()
|
|
28
|
+
etcd.watcher.set('service/b', { value: '4' })
|
|
29
|
+
expect(map.get('service/a')).toBe('1')
|
|
30
|
+
expect(map.get('service/b')).toBe('4')
|
|
31
|
+
expect(map.get('service/c')).toBe('3')
|
|
32
|
+
expect([...map.values()]).toEqual(['1', '4', '3'])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('maintains key creates', async () => {
|
|
36
|
+
const etcd = new MockEtcd()
|
|
37
|
+
etcd.watcher.set('service/a', { value: '1' })
|
|
38
|
+
const map = new EtcdMap(etcd as unknown as Etcd3)
|
|
39
|
+
await map.connect()
|
|
40
|
+
etcd.watcher.set('service/b', { value: '2' })
|
|
41
|
+
expect(map.get('service/a')).toBe('1')
|
|
42
|
+
expect(map.get('service/b')).toBe('2')
|
|
43
|
+
expect([...map.values()]).toEqual(['1', '2'])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('maintains key deletions', async () => {
|
|
47
|
+
const etcd = new MockEtcd()
|
|
48
|
+
etcd.watcher.set('service/a', { value: '1' })
|
|
49
|
+
etcd.watcher.set('service/b', { value: '2' })
|
|
50
|
+
const map = new EtcdMap(etcd as unknown as Etcd3)
|
|
51
|
+
await map.connect()
|
|
52
|
+
etcd.watcher.del('service/b')
|
|
53
|
+
expect(map.get('service/a')).toBe('1')
|
|
54
|
+
expect(map.get('service/b')).toBe(null)
|
|
55
|
+
expect([...map.values()]).toEqual(['1'])
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('notifies of updates', async () => {
|
|
59
|
+
const etcd = new MockEtcd()
|
|
60
|
+
etcd.watcher.set('service/a', { value: '1' })
|
|
61
|
+
etcd.watcher.set('service/b', { value: '2' })
|
|
62
|
+
const map = new EtcdMap(etcd as unknown as Etcd3)
|
|
63
|
+
await map.connect()
|
|
64
|
+
const states: string[][] = [[...map.values()]]
|
|
65
|
+
map.onUpdate((update) => {
|
|
66
|
+
states.push([...update.values()])
|
|
67
|
+
})
|
|
68
|
+
etcd.watcher.set('service/c', { value: '3' })
|
|
69
|
+
etcd.watcher.del('service/b')
|
|
70
|
+
etcd.watcher.set('service/a', { value: '4' })
|
|
71
|
+
expect(states).toEqual([
|
|
72
|
+
['1', '2'],
|
|
73
|
+
['1', '2', '3'],
|
|
74
|
+
['1', '3'],
|
|
75
|
+
['4', '3'],
|
|
76
|
+
])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('ignores out-of-order updates', async () => {
|
|
80
|
+
const etcd = new MockEtcd()
|
|
81
|
+
etcd.watcher.set('service/a', { value: '1' })
|
|
82
|
+
const map = new EtcdMap(etcd as unknown as Etcd3)
|
|
83
|
+
await map.connect()
|
|
84
|
+
const states: string[][] = [[...map.values()]]
|
|
85
|
+
map.onUpdate((update) => {
|
|
86
|
+
states.push([...update.values()])
|
|
87
|
+
})
|
|
88
|
+
etcd.watcher.set('service/a', { value: '2' })
|
|
89
|
+
etcd.watcher.set('service/a', { value: '3', overrideRev: 1 }) // old rev
|
|
90
|
+
etcd.watcher.set('service/a', { value: '4' })
|
|
91
|
+
expect(map.get('service/a')).toBe('4')
|
|
92
|
+
expect(states).toEqual([['1'], ['2'], ['4']]) // never witnessed 3
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('EtcdHostList', () => {
|
|
97
|
+
it('initializes values based on current keys', async () => {
|
|
98
|
+
const etcd = new MockEtcd()
|
|
99
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.1' })
|
|
100
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.2' })
|
|
101
|
+
etcd.watcher.set('service/c', { value: 'http://192.168.1.3' })
|
|
102
|
+
const hostList = new EtcdHostList(etcd as unknown as Etcd3, '')
|
|
103
|
+
await hostList.connect()
|
|
104
|
+
expect([...hostList.get()]).toEqual([
|
|
105
|
+
'http://192.168.1.1',
|
|
106
|
+
'http://192.168.1.2',
|
|
107
|
+
'http://192.168.1.3',
|
|
108
|
+
])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('maintains key updates', async () => {
|
|
112
|
+
const etcd = new MockEtcd()
|
|
113
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.1' })
|
|
114
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.2' })
|
|
115
|
+
etcd.watcher.set('service/c', { value: 'http://192.168.1.3' })
|
|
116
|
+
const hostList = new EtcdHostList(etcd as unknown as Etcd3, '')
|
|
117
|
+
await hostList.connect()
|
|
118
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.4' })
|
|
119
|
+
expect([...hostList.get()]).toEqual([
|
|
120
|
+
'http://192.168.1.1',
|
|
121
|
+
'http://192.168.1.4',
|
|
122
|
+
'http://192.168.1.3',
|
|
123
|
+
])
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('maintains key creates', async () => {
|
|
127
|
+
const etcd = new MockEtcd()
|
|
128
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.1' })
|
|
129
|
+
const hostList = new EtcdHostList(etcd as unknown as Etcd3, '')
|
|
130
|
+
await hostList.connect()
|
|
131
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.2' })
|
|
132
|
+
expect([...hostList.get()]).toEqual([
|
|
133
|
+
'http://192.168.1.1',
|
|
134
|
+
'http://192.168.1.2',
|
|
135
|
+
])
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('maintains key deletions', async () => {
|
|
139
|
+
const etcd = new MockEtcd()
|
|
140
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.1' })
|
|
141
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.2' })
|
|
142
|
+
const hostList = new EtcdHostList(etcd as unknown as Etcd3, '')
|
|
143
|
+
await hostList.connect()
|
|
144
|
+
etcd.watcher.del('service/b')
|
|
145
|
+
expect([...hostList.get()]).toEqual(['http://192.168.1.1'])
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('notifies of updates', async () => {
|
|
149
|
+
const etcd = new MockEtcd()
|
|
150
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.1' })
|
|
151
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.2' })
|
|
152
|
+
const hostList = new EtcdHostList(etcd as unknown as Etcd3, '')
|
|
153
|
+
await hostList.connect()
|
|
154
|
+
const states: string[][] = [[...hostList.get()]]
|
|
155
|
+
hostList.onUpdate((updated) => {
|
|
156
|
+
expect([...updated]).toEqual([...hostList.get()])
|
|
157
|
+
states.push([...updated])
|
|
158
|
+
})
|
|
159
|
+
etcd.watcher.set('service/c', { value: 'http://192.168.1.3' })
|
|
160
|
+
etcd.watcher.del('service/b')
|
|
161
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.4' })
|
|
162
|
+
expect(states).toEqual([
|
|
163
|
+
['http://192.168.1.1', 'http://192.168.1.2'],
|
|
164
|
+
['http://192.168.1.1', 'http://192.168.1.2', 'http://192.168.1.3'],
|
|
165
|
+
['http://192.168.1.1', 'http://192.168.1.3'],
|
|
166
|
+
['http://192.168.1.4', 'http://192.168.1.3'],
|
|
167
|
+
])
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('ignores bad host values', async () => {
|
|
171
|
+
const etcd = new MockEtcd()
|
|
172
|
+
etcd.watcher.set('service/a', { value: 'not-a-host' })
|
|
173
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.2' })
|
|
174
|
+
const hostList = new EtcdHostList(etcd as unknown as Etcd3, '')
|
|
175
|
+
await hostList.connect()
|
|
176
|
+
expect([...hostList.get()]).toEqual(['http://192.168.1.2'])
|
|
177
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.1' })
|
|
178
|
+
etcd.watcher.set('service/c', { value: 'not-a-host' })
|
|
179
|
+
expect([...hostList.get()]).toEqual([
|
|
180
|
+
'http://192.168.1.1',
|
|
181
|
+
'http://192.168.1.2',
|
|
182
|
+
])
|
|
183
|
+
etcd.watcher.set('service/c', { value: 'http://192.168.1.3' })
|
|
184
|
+
expect([...hostList.get()]).toEqual([
|
|
185
|
+
'http://192.168.1.1',
|
|
186
|
+
'http://192.168.1.2',
|
|
187
|
+
'http://192.168.1.3',
|
|
188
|
+
])
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('falls back to static host list when uninitialized or no keys available', async () => {
|
|
192
|
+
const etcd = new MockEtcd()
|
|
193
|
+
const hostList = new EtcdHostList(etcd as unknown as Etcd3, '', [
|
|
194
|
+
'http://10.0.0.1',
|
|
195
|
+
'http://10.0.0.2',
|
|
196
|
+
])
|
|
197
|
+
etcd.watcher.set('service/a', { value: 'http://192.168.1.1' })
|
|
198
|
+
expect([...hostList.get()]).toEqual([
|
|
199
|
+
'http://10.0.0.1',
|
|
200
|
+
'http://10.0.0.2',
|
|
201
|
+
])
|
|
202
|
+
await hostList.connect()
|
|
203
|
+
const states: string[][] = [[...hostList.get()]]
|
|
204
|
+
hostList.onUpdate((updated) => {
|
|
205
|
+
states.push([...updated])
|
|
206
|
+
})
|
|
207
|
+
etcd.watcher.del('service/a')
|
|
208
|
+
etcd.watcher.set('service/b', { value: 'http://192.168.1.2' })
|
|
209
|
+
expect(states).toEqual([
|
|
210
|
+
['http://192.168.1.1'],
|
|
211
|
+
['http://10.0.0.1', 'http://10.0.0.2'],
|
|
212
|
+
['http://192.168.1.2'],
|
|
213
|
+
])
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
class MockEtcd {
|
|
219
|
+
public watcher = new MockWatcher()
|
|
220
|
+
watch() {
|
|
221
|
+
const watcher = this.watcher
|
|
222
|
+
return {
|
|
223
|
+
prefix() {
|
|
224
|
+
return {
|
|
225
|
+
watcher() {
|
|
226
|
+
return watcher
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
getAll() {
|
|
233
|
+
const watcher = this.watcher
|
|
234
|
+
return {
|
|
235
|
+
prefix() {
|
|
236
|
+
return {
|
|
237
|
+
async exec(): Promise<{ kvs: IKeyValue[] }> {
|
|
238
|
+
return { kvs: watcher.getAll() }
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
class MockWatcher extends EventEmitter {
|
|
247
|
+
rev = 1
|
|
248
|
+
kvs: IKeyValue[] = []
|
|
249
|
+
constructor() {
|
|
250
|
+
super()
|
|
251
|
+
process.nextTick(() => this.emit('connected', {}))
|
|
252
|
+
}
|
|
253
|
+
get(key: string): IKeyValue | null {
|
|
254
|
+
const found = this.kvs.find((kv) => kv.key.toString() === key)
|
|
255
|
+
return found ?? null
|
|
256
|
+
}
|
|
257
|
+
getAll(): IKeyValue[] {
|
|
258
|
+
return [...this.kvs]
|
|
259
|
+
}
|
|
260
|
+
set(
|
|
261
|
+
key: string,
|
|
262
|
+
{ value, overrideRev }: { value: string; overrideRev?: number },
|
|
263
|
+
) {
|
|
264
|
+
const found = this.kvs.find((kv) => kv.key.toString() === key)
|
|
265
|
+
const rev = overrideRev ?? ++this.rev
|
|
266
|
+
if (found) {
|
|
267
|
+
found.value = Buffer.from(value)
|
|
268
|
+
found.mod_revision = rev.toString()
|
|
269
|
+
found.version = (parseInt(found.version, 10) + 1).toString()
|
|
270
|
+
this.emit('put', found)
|
|
271
|
+
} else {
|
|
272
|
+
const created = {
|
|
273
|
+
key: Buffer.from(key),
|
|
274
|
+
value: Buffer.from(value),
|
|
275
|
+
create_revision: rev.toString(),
|
|
276
|
+
mod_revision: rev.toString(),
|
|
277
|
+
version: '1',
|
|
278
|
+
lease: '0',
|
|
279
|
+
}
|
|
280
|
+
this.kvs.push(created)
|
|
281
|
+
this.emit('put', created)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
del(key: string) {
|
|
285
|
+
const foundIdx = this.kvs.findIndex((kv) => kv.key.toString() === key)
|
|
286
|
+
if (foundIdx === -1) return
|
|
287
|
+
const [deleted] = this.kvs.splice(foundIdx, 1)
|
|
288
|
+
const rev = ++this.rev
|
|
289
|
+
deleted.value = Buffer.from('')
|
|
290
|
+
deleted.mod_revision = rev.toString()
|
|
291
|
+
deleted.create_revision = '0'
|
|
292
|
+
deleted.version = '0'
|
|
293
|
+
this.emit('delete', deleted)
|
|
294
|
+
}
|
|
295
|
+
on(evt: 'connected', listener: (res: unknown) => void): any
|
|
296
|
+
on(evt: 'put', listener: (kv: IKeyValue) => void): any
|
|
297
|
+
on(evt: 'delete', listener: (kv: IKeyValue) => void): any
|
|
298
|
+
on(evt: string, listener: (...args: any[]) => void) {
|
|
299
|
+
super.on(evt, listener)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -170,3 +170,49 @@ Object {
|
|
|
170
170
|
],
|
|
171
171
|
}
|
|
172
172
|
`;
|
|
173
|
+
|
|
174
|
+
exports[`labeler service views returns additional labeler data 1`] = `
|
|
175
|
+
Object {
|
|
176
|
+
"views": Array [
|
|
177
|
+
Object {
|
|
178
|
+
"$type": "app.bsky.labeler.defs#labelerViewDetailed",
|
|
179
|
+
"cid": "cids(0)",
|
|
180
|
+
"creator": Object {
|
|
181
|
+
"associated": Object {
|
|
182
|
+
"labeler": true,
|
|
183
|
+
},
|
|
184
|
+
"did": "user(0)",
|
|
185
|
+
"handle": "carol.test",
|
|
186
|
+
"labels": Array [],
|
|
187
|
+
"viewer": Object {
|
|
188
|
+
"blockedBy": false,
|
|
189
|
+
"following": "record(1)",
|
|
190
|
+
"muted": false,
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
"indexedAt": "1970-01-01T00:00:00.000Z",
|
|
194
|
+
"labels": Array [],
|
|
195
|
+
"likeCount": 0,
|
|
196
|
+
"policies": Object {
|
|
197
|
+
"labelValues": Array [
|
|
198
|
+
"spam",
|
|
199
|
+
"!hide",
|
|
200
|
+
"scam",
|
|
201
|
+
"impersonation",
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
"reasonTypes": Array [
|
|
205
|
+
"com.atproto.moderation.defs#reasonOther",
|
|
206
|
+
],
|
|
207
|
+
"subjectCollections": Array [
|
|
208
|
+
"app.bsky.feed.post",
|
|
209
|
+
],
|
|
210
|
+
"subjectTypes": Array [
|
|
211
|
+
"record",
|
|
212
|
+
],
|
|
213
|
+
"uri": "record(0)",
|
|
214
|
+
"viewer": Object {},
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
}
|
|
218
|
+
`;
|
|
@@ -384,7 +384,7 @@ Array [
|
|
|
384
384
|
|
|
385
385
|
exports[`notification views fetches notifications with default priority 1`] = `
|
|
386
386
|
Object {
|
|
387
|
-
"cursor": "
|
|
387
|
+
"cursor": "1970-01-01T00:00:00.000Z",
|
|
388
388
|
"notifications": Array [
|
|
389
389
|
Object {
|
|
390
390
|
"author": Object {
|
|
@@ -486,7 +486,7 @@ Object {
|
|
|
486
486
|
|
|
487
487
|
exports[`notification views fetches notifications with explicit priority 1`] = `
|
|
488
488
|
Object {
|
|
489
|
-
"cursor": "
|
|
489
|
+
"cursor": "1970-01-01T00:00:00.000Z",
|
|
490
490
|
"notifications": Array [
|
|
491
491
|
Object {
|
|
492
492
|
"author": Object {
|
|
@@ -588,7 +588,7 @@ Object {
|
|
|
588
588
|
|
|
589
589
|
exports[`notification views fetches notifications with explicit priority 2`] = `
|
|
590
590
|
Object {
|
|
591
|
-
"cursor": "
|
|
591
|
+
"cursor": "1970-01-01T00:00:00.000Z",
|
|
592
592
|
"notifications": Array [
|
|
593
593
|
Object {
|
|
594
594
|
"author": Object {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AppBskyLabelerDefs,
|
|
4
|
+
AtpAgent,
|
|
5
|
+
ComAtprotoModerationDefs,
|
|
6
|
+
} from '@atproto/api'
|
|
3
7
|
import { RecordRef, SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
4
8
|
import { ids } from '../../src/lexicon/lexicons'
|
|
5
9
|
import { isView as isRecordEmbedView } from '../../src/lexicon/types/app/bsky/embed/record'
|
|
@@ -14,6 +18,7 @@ describe('labeler service views', () => {
|
|
|
14
18
|
// account dids, for convenience
|
|
15
19
|
let alice: string
|
|
16
20
|
let bob: string
|
|
21
|
+
let carol: string
|
|
17
22
|
|
|
18
23
|
let aliceService: RecordRef
|
|
19
24
|
|
|
@@ -27,6 +32,7 @@ describe('labeler service views', () => {
|
|
|
27
32
|
await basicSeed(sc)
|
|
28
33
|
alice = sc.dids.alice
|
|
29
34
|
bob = sc.dids.bob
|
|
35
|
+
carol = sc.dids.carol
|
|
30
36
|
|
|
31
37
|
const aliceRes = await pdsAgent.api.com.atproto.repo.createRecord(
|
|
32
38
|
{
|
|
@@ -190,4 +196,47 @@ describe('labeler service views', () => {
|
|
|
190
196
|
// Cleanup
|
|
191
197
|
await network.bsky.ctx.dataplane.untakedownActor({ did: alice })
|
|
192
198
|
})
|
|
199
|
+
|
|
200
|
+
it(`returns additional labeler data`, async () => {
|
|
201
|
+
await pdsAgent.api.com.atproto.repo.createRecord(
|
|
202
|
+
{
|
|
203
|
+
repo: carol,
|
|
204
|
+
collection: ids.AppBskyLabelerService,
|
|
205
|
+
rkey: 'self',
|
|
206
|
+
record: {
|
|
207
|
+
policies: {
|
|
208
|
+
labelValues: ['spam', '!hide', 'scam', 'impersonation'],
|
|
209
|
+
},
|
|
210
|
+
createdAt: new Date().toISOString(),
|
|
211
|
+
reasonTypes: [ComAtprotoModerationDefs.REASONOTHER],
|
|
212
|
+
subjectTypes: ['record'],
|
|
213
|
+
subjectCollections: ['app.bsky.feed.post'],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{ headers: sc.getHeaders(carol), encoding: 'application/json' },
|
|
217
|
+
)
|
|
218
|
+
await network.processAll()
|
|
219
|
+
|
|
220
|
+
const view = await agent.api.app.bsky.labeler.getServices(
|
|
221
|
+
{ dids: [carol], detailed: true },
|
|
222
|
+
{
|
|
223
|
+
headers: await network.serviceHeaders(
|
|
224
|
+
bob,
|
|
225
|
+
ids.AppBskyLabelerGetServices,
|
|
226
|
+
),
|
|
227
|
+
},
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
const labelerView = view.data.views[0]
|
|
231
|
+
expect(AppBskyLabelerDefs.isLabelerViewDetailed(labelerView)).toBe(true)
|
|
232
|
+
// for TS only
|
|
233
|
+
if (!AppBskyLabelerDefs.isLabelerViewDetailed(labelerView)) return
|
|
234
|
+
expect(labelerView).toBeTruthy()
|
|
235
|
+
expect(labelerView.reasonTypes).toEqual([
|
|
236
|
+
ComAtprotoModerationDefs.REASONOTHER,
|
|
237
|
+
])
|
|
238
|
+
expect(labelerView.subjectTypes).toEqual(['record'])
|
|
239
|
+
expect(labelerView.subjectCollections).toEqual(['app.bsky.feed.post'])
|
|
240
|
+
expect(forSnapshot(view.data)).toMatchSnapshot()
|
|
241
|
+
})
|
|
193
242
|
})
|