@atproto/bsky 0.0.39 → 0.0.40
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 +7 -0
- package/dist/hydration/hydrator.d.ts +12 -2
- package/dist/hydration/label.d.ts +7 -3
- package/dist/hydration/util.d.ts +5 -2
- package/dist/index.js +187 -103
- package/dist/index.js.map +2 -2
- package/package.json +5 -5
- package/src/api/app/bsky/actor/getProfile.ts +10 -6
- package/src/api/app/bsky/actor/getProfiles.ts +2 -2
- package/src/api/app/bsky/actor/getSuggestions.ts +2 -2
- package/src/api/app/bsky/actor/searchActors.ts +7 -3
- package/src/api/app/bsky/actor/searchActorsTypeahead.ts +2 -2
- package/src/api/app/bsky/feed/getActorFeeds.ts +2 -2
- package/src/api/app/bsky/feed/getActorLikes.ts +2 -2
- package/src/api/app/bsky/feed/getAuthorFeed.ts +6 -2
- package/src/api/app/bsky/feed/getFeed.ts +2 -2
- package/src/api/app/bsky/feed/getFeedGenerator.ts +3 -6
- package/src/api/app/bsky/feed/getFeedGenerators.ts +2 -2
- package/src/api/app/bsky/feed/getLikes.ts +2 -2
- package/src/api/app/bsky/feed/getListFeed.ts +2 -2
- package/src/api/app/bsky/feed/getPostThread.ts +2 -2
- package/src/api/app/bsky/feed/getPosts.ts +2 -2
- package/src/api/app/bsky/feed/getRepostedBy.ts +2 -2
- package/src/api/app/bsky/feed/getSuggestedFeeds.ts +3 -5
- package/src/api/app/bsky/feed/getTimeline.ts +6 -3
- package/src/api/app/bsky/feed/searchPosts.ts +2 -2
- package/src/api/app/bsky/graph/getBlocks.ts +6 -3
- package/src/api/app/bsky/graph/getFollowers.ts +6 -2
- package/src/api/app/bsky/graph/getFollows.ts +6 -2
- package/src/api/app/bsky/graph/getList.ts +2 -2
- package/src/api/app/bsky/graph/getListBlocks.ts +6 -3
- package/src/api/app/bsky/graph/getListMutes.ts +6 -3
- package/src/api/app/bsky/graph/getLists.ts +2 -2
- package/src/api/app/bsky/graph/getMutes.ts +6 -3
- package/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +3 -3
- package/src/api/app/bsky/labeler/getServices.ts +3 -3
- package/src/api/app/bsky/notification/listNotifications.ts +6 -3
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +2 -2
- package/src/data-plane/server/routes/labels.ts +24 -8
- package/src/hydration/hydrator.ts +55 -10
- package/src/hydration/label.ts +33 -7
- package/src/hydration/util.ts +6 -2
- package/src/index.ts +1 -1
- package/src/views/index.ts +7 -7
- package/tests/label-hydration.test.ts +31 -0
- package/tests/views/labeler-service.test.ts +2 -7
- package/tests/views/takedown-labels.test.ts +52 -0
|
@@ -3,28 +3,44 @@ import { noUndefinedVals } from '@atproto/common'
|
|
|
3
3
|
import { ServiceImpl } from '@connectrpc/connect'
|
|
4
4
|
import { Service } from '../../../proto/bsky_connect'
|
|
5
5
|
import { Database } from '../db'
|
|
6
|
+
import { Selectable } from 'kysely'
|
|
7
|
+
import { Label } from '../db/tables/label'
|
|
8
|
+
|
|
9
|
+
type LabelRow = Selectable<Label>
|
|
6
10
|
|
|
7
11
|
export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
8
12
|
async getLabels(req) {
|
|
9
13
|
const { subjects, issuers } = req
|
|
10
14
|
if (subjects.length === 0 || issuers.length === 0) {
|
|
11
|
-
return {
|
|
15
|
+
return { labels: [] }
|
|
12
16
|
}
|
|
13
|
-
const res = await db.db
|
|
17
|
+
const res: LabelRow[] = await db.db
|
|
14
18
|
.selectFrom('label')
|
|
15
19
|
.where('uri', 'in', subjects)
|
|
16
20
|
.where('src', 'in', issuers)
|
|
17
21
|
.selectAll()
|
|
18
22
|
.execute()
|
|
19
23
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const labelsBySubject = new Map<string, LabelRow[]>()
|
|
25
|
+
res.forEach((l) => {
|
|
26
|
+
const labels = labelsBySubject.get(l.uri) ?? []
|
|
27
|
+
labels.push(l)
|
|
28
|
+
labelsBySubject.set(l.uri, labels)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// intentionally duplicate label results, appview frontend should be defensive to this
|
|
32
|
+
const labels = subjects.flatMap((sub) => {
|
|
33
|
+
const labelsForSub = labelsBySubject.get(sub) ?? []
|
|
34
|
+
return labelsForSub.map((l) => {
|
|
35
|
+
const formatted = noUndefinedVals({
|
|
36
|
+
...l,
|
|
37
|
+
cid: l.cid === '' ? undefined : l.cid,
|
|
38
|
+
neg: l.neg === true ? true : undefined,
|
|
39
|
+
})
|
|
40
|
+
return ui8.fromString(JSON.stringify(formatted), 'utf8')
|
|
25
41
|
})
|
|
26
|
-
return ui8.fromString(JSON.stringify(formatted), 'utf8')
|
|
27
42
|
})
|
|
43
|
+
|
|
28
44
|
return { labels }
|
|
29
45
|
},
|
|
30
46
|
})
|
|
@@ -29,7 +29,13 @@ import {
|
|
|
29
29
|
Labelers,
|
|
30
30
|
Labels,
|
|
31
31
|
} from './label'
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
HydrationMap,
|
|
34
|
+
Merges,
|
|
35
|
+
RecordInfo,
|
|
36
|
+
didFromUri,
|
|
37
|
+
urisByCollection,
|
|
38
|
+
} from './util'
|
|
33
39
|
import {
|
|
34
40
|
FeedGenAggs,
|
|
35
41
|
FeedGens,
|
|
@@ -47,7 +53,17 @@ import {
|
|
|
47
53
|
} from './feed'
|
|
48
54
|
import { ParsedLabelers } from '../util'
|
|
49
55
|
|
|
50
|
-
export
|
|
56
|
+
export class HydrateCtx {
|
|
57
|
+
labelers = this.vals.labelers
|
|
58
|
+
viewer = this.vals.viewer
|
|
59
|
+
includeTakedowns = this.vals.includeTakedowns
|
|
60
|
+
constructor(private vals: HydrateCtxVals) {}
|
|
61
|
+
copy<V extends Partial<HydrateCtxVals>>(vals?: V): HydrateCtx & V {
|
|
62
|
+
return new HydrateCtx({ ...this.vals, ...vals }) as HydrateCtx & V
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type HydrateCtxVals = {
|
|
51
67
|
labelers: ParsedLabelers
|
|
52
68
|
viewer: string | null
|
|
53
69
|
includeTakedowns?: boolean
|
|
@@ -91,12 +107,17 @@ export class Hydrator {
|
|
|
91
107
|
feed: FeedHydrator
|
|
92
108
|
graph: GraphHydrator
|
|
93
109
|
label: LabelHydrator
|
|
110
|
+
serviceLabelers: Set<string>
|
|
94
111
|
|
|
95
|
-
constructor(
|
|
112
|
+
constructor(
|
|
113
|
+
public dataplane: DataPlaneClient,
|
|
114
|
+
serviceLabelers: string[] = [],
|
|
115
|
+
) {
|
|
96
116
|
this.actor = new ActorHydrator(dataplane)
|
|
97
117
|
this.feed = new FeedHydrator(dataplane)
|
|
98
118
|
this.graph = new GraphHydrator(dataplane)
|
|
99
119
|
this.label = new LabelHydrator(dataplane)
|
|
120
|
+
this.serviceLabelers = new Set(serviceLabelers)
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
// app.bsky.actor.defs#profileView
|
|
@@ -549,13 +570,14 @@ export class Hydrator {
|
|
|
549
570
|
): Promise<HydrationState> {
|
|
550
571
|
const [labelers, labelerAggs, labelerViewers, profileState] =
|
|
551
572
|
await Promise.all([
|
|
552
|
-
this.label.getLabelers(dids),
|
|
573
|
+
this.label.getLabelers(dids, ctx.includeTakedowns),
|
|
553
574
|
this.label.getLabelerAggregates(dids),
|
|
554
575
|
ctx.viewer
|
|
555
576
|
? this.label.getLabelerViewerStates(dids, ctx.viewer)
|
|
556
577
|
: undefined,
|
|
557
|
-
this.hydrateProfiles(dids
|
|
578
|
+
this.hydrateProfiles(dids, ctx),
|
|
558
579
|
])
|
|
580
|
+
actionTakedownLabels(dids, labelers, profileState.labels ?? new Labels())
|
|
559
581
|
return mergeStates(profileState, {
|
|
560
582
|
labelers,
|
|
561
583
|
labelerAggs,
|
|
@@ -613,8 +635,10 @@ export class Hydrator {
|
|
|
613
635
|
undefined
|
|
614
636
|
)
|
|
615
637
|
} else if (collection === ids.AppBskyLabelerService) {
|
|
638
|
+
if (parsed.rkey !== 'self') return
|
|
639
|
+
const did = parsed.hostname
|
|
616
640
|
return (
|
|
617
|
-
(await this.label.getLabelers([
|
|
641
|
+
(await this.label.getLabelers([did], includeTakedowns)).get(did) ??
|
|
618
642
|
undefined
|
|
619
643
|
)
|
|
620
644
|
} else if (collection === ids.AppBskyActorProfile) {
|
|
@@ -631,6 +655,30 @@ export class Hydrator {
|
|
|
631
655
|
}
|
|
632
656
|
}
|
|
633
657
|
}
|
|
658
|
+
|
|
659
|
+
async createContext(vals: HydrateCtxVals) {
|
|
660
|
+
// ensures we're only apply labelers that exist and are not taken down
|
|
661
|
+
const labelers = vals.labelers.dids
|
|
662
|
+
const nonServiceLabelers = labelers.filter(
|
|
663
|
+
(did) => !this.serviceLabelers.has(did),
|
|
664
|
+
)
|
|
665
|
+
const labelerActors = await this.actor.getActors(
|
|
666
|
+
nonServiceLabelers,
|
|
667
|
+
vals.includeTakedowns,
|
|
668
|
+
)
|
|
669
|
+
const availableDids = labelers.filter(
|
|
670
|
+
(did) => this.serviceLabelers.has(did) || !!labelerActors.get(did),
|
|
671
|
+
)
|
|
672
|
+
const availableLabelers = {
|
|
673
|
+
dids: availableDids,
|
|
674
|
+
redact: vals.labelers.redact,
|
|
675
|
+
}
|
|
676
|
+
return new HydrateCtx({
|
|
677
|
+
labelers: availableLabelers,
|
|
678
|
+
viewer: vals.viewer,
|
|
679
|
+
includeTakedowns: vals.includeTakedowns,
|
|
680
|
+
})
|
|
681
|
+
}
|
|
634
682
|
}
|
|
635
683
|
|
|
636
684
|
const listUrisFromProfileViewer = (item: ProfileViewerState | null) => {
|
|
@@ -770,10 +818,7 @@ export const mergeStates = (
|
|
|
770
818
|
}
|
|
771
819
|
}
|
|
772
820
|
|
|
773
|
-
const mergeMaps = <
|
|
774
|
-
mapA?: HydrationMap<T>,
|
|
775
|
-
mapB?: HydrationMap<T>,
|
|
776
|
-
): HydrationMap<T> | undefined => {
|
|
821
|
+
const mergeMaps = <M extends Merges>(mapA?: M, mapB?: M): M | undefined => {
|
|
777
822
|
if (!mapA) return mapB
|
|
778
823
|
if (!mapB) return mapA
|
|
779
824
|
return mapA.merge(mapB)
|
package/src/hydration/label.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Label } from '../lexicon/types/com/atproto/label/defs'
|
|
|
3
3
|
import { Record as LabelerRecord } from '../lexicon/types/app/bsky/labeler/service'
|
|
4
4
|
import {
|
|
5
5
|
HydrationMap,
|
|
6
|
+
Merges,
|
|
6
7
|
RecordInfo,
|
|
7
8
|
parseJsonBytes,
|
|
8
9
|
parseRecord,
|
|
@@ -16,10 +17,36 @@ export type { Label } from '../lexicon/types/com/atproto/label/defs'
|
|
|
16
17
|
|
|
17
18
|
export type SubjectLabels = {
|
|
18
19
|
isTakendown: boolean
|
|
19
|
-
labels: Label
|
|
20
|
+
labels: HydrationMap<Label> // src + val -> label
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export
|
|
23
|
+
export class Labels extends HydrationMap<SubjectLabels> implements Merges {
|
|
24
|
+
static key(label: Label) {
|
|
25
|
+
return `${label.src}::${label.val}`
|
|
26
|
+
}
|
|
27
|
+
merge(map: Labels): this {
|
|
28
|
+
map.forEach((theirs, key) => {
|
|
29
|
+
if (!theirs) return
|
|
30
|
+
const mine = this.get(key)
|
|
31
|
+
if (mine) {
|
|
32
|
+
mine.isTakendown = mine.isTakendown || theirs.isTakendown
|
|
33
|
+
mine.labels = mine.labels.merge(theirs.labels)
|
|
34
|
+
} else {
|
|
35
|
+
this.set(key, theirs)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
getBySubject(sub: string): Label[] {
|
|
41
|
+
const it = this.get(sub)?.labels.values()
|
|
42
|
+
if (!it) return []
|
|
43
|
+
const labels: Label[] = []
|
|
44
|
+
for (const label of it) {
|
|
45
|
+
if (label) labels.push(label)
|
|
46
|
+
}
|
|
47
|
+
return labels
|
|
48
|
+
}
|
|
49
|
+
}
|
|
23
50
|
|
|
24
51
|
export type LabelerAgg = {
|
|
25
52
|
likes: number
|
|
@@ -43,8 +70,7 @@ export class LabelHydrator {
|
|
|
43
70
|
subjects: string[],
|
|
44
71
|
labelers: ParsedLabelers,
|
|
45
72
|
): Promise<Labels> {
|
|
46
|
-
if (!subjects.length || !labelers.dids.length)
|
|
47
|
-
return new HydrationMap<SubjectLabels>()
|
|
73
|
+
if (!subjects.length || !labelers.dids.length) return new Labels()
|
|
48
74
|
const res = await this.dataplane.getLabels({
|
|
49
75
|
subjects,
|
|
50
76
|
issuers: labelers.dids,
|
|
@@ -57,11 +83,11 @@ export class LabelHydrator {
|
|
|
57
83
|
if (!entry) {
|
|
58
84
|
entry = {
|
|
59
85
|
isTakendown: false,
|
|
60
|
-
labels:
|
|
86
|
+
labels: new HydrationMap(),
|
|
61
87
|
}
|
|
62
88
|
acc.set(label.uri, entry)
|
|
63
89
|
}
|
|
64
|
-
entry.labels.
|
|
90
|
+
entry.labels.set(Labels.key(label), label)
|
|
65
91
|
if (
|
|
66
92
|
TAKEDOWN_LABELS.includes(label.val) &&
|
|
67
93
|
!label.neg &&
|
|
@@ -70,7 +96,7 @@ export class LabelHydrator {
|
|
|
70
96
|
entry.isTakendown = true
|
|
71
97
|
}
|
|
72
98
|
return acc
|
|
73
|
-
}, new
|
|
99
|
+
}, new Labels())
|
|
74
100
|
}
|
|
75
101
|
|
|
76
102
|
async getLabelers(
|
package/src/hydration/util.ts
CHANGED
|
@@ -5,8 +5,8 @@ import * as ui8 from 'uint8arrays'
|
|
|
5
5
|
import { lexicons } from '../lexicon/lexicons'
|
|
6
6
|
import { Record } from '../proto/bsky_pb'
|
|
7
7
|
|
|
8
|
-
export class HydrationMap<T> extends Map<string, T | null> {
|
|
9
|
-
merge(map: HydrationMap<T>):
|
|
8
|
+
export class HydrationMap<T> extends Map<string, T | null> implements Merges {
|
|
9
|
+
merge(map: HydrationMap<T>): this {
|
|
10
10
|
map.forEach((val, key) => {
|
|
11
11
|
this.set(key, val)
|
|
12
12
|
})
|
|
@@ -14,6 +14,10 @@ export class HydrationMap<T> extends Map<string, T | null> {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export interface Merges {
|
|
18
|
+
merge<T extends this>(map: T): this
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
export type RecordInfo<T> = {
|
|
18
22
|
record: T
|
|
19
23
|
cid: string
|
package/src/index.ts
CHANGED
|
@@ -77,7 +77,7 @@ export class BskyAppView {
|
|
|
77
77
|
httpVersion: config.dataplaneHttpVersion,
|
|
78
78
|
rejectUnauthorized: !config.dataplaneIgnoreBadTls,
|
|
79
79
|
})
|
|
80
|
-
const hydrator = new Hydrator(dataplane)
|
|
80
|
+
const hydrator = new Hydrator(dataplane, config.labelsFromIssuerDids)
|
|
81
81
|
const views = new Views(imgUriBuilder)
|
|
82
82
|
|
|
83
83
|
const bsyncClient = createBsyncClient({
|
package/src/views/index.ts
CHANGED
|
@@ -136,8 +136,8 @@ export class Views {
|
|
|
136
136
|
'self',
|
|
137
137
|
).toString()
|
|
138
138
|
const labels = [
|
|
139
|
-
...(state.labels?.
|
|
140
|
-
...(state.labels?.
|
|
139
|
+
...(state.labels?.getBySubject(did) ?? []),
|
|
140
|
+
...(state.labels?.getBySubject(profileUri) ?? []),
|
|
141
141
|
...this.selfLabels({
|
|
142
142
|
uri: profileUri,
|
|
143
143
|
cid: actor.profileCid?.toString(),
|
|
@@ -225,7 +225,7 @@ export class Views {
|
|
|
225
225
|
return undefined
|
|
226
226
|
}
|
|
227
227
|
const listViewer = state.listViewers?.get(uri)
|
|
228
|
-
const labels = state.labels?.
|
|
228
|
+
const labels = state.labels?.getBySubject(uri) ?? []
|
|
229
229
|
const creator = new AtUri(uri).hostname
|
|
230
230
|
return {
|
|
231
231
|
uri,
|
|
@@ -281,7 +281,7 @@ export class Views {
|
|
|
281
281
|
|
|
282
282
|
const uri = AtUri.make(did, ids.AppBskyLabelerService, 'self').toString()
|
|
283
283
|
const labels = [
|
|
284
|
-
...(state.labels?.
|
|
284
|
+
...(state.labels?.getBySubject(uri) ?? []),
|
|
285
285
|
...this.selfLabels({
|
|
286
286
|
uri,
|
|
287
287
|
cid: labeler.cid.toString(),
|
|
@@ -351,7 +351,7 @@ export class Views {
|
|
|
351
351
|
if (!creator) return
|
|
352
352
|
const viewer = state.feedgenViewers?.get(uri)
|
|
353
353
|
const aggs = state.feedgenAggs?.get(uri)
|
|
354
|
-
const labels = state.labels?.
|
|
354
|
+
const labels = state.labels?.getBySubject(uri) ?? []
|
|
355
355
|
|
|
356
356
|
return {
|
|
357
357
|
uri,
|
|
@@ -408,7 +408,7 @@ export class Views {
|
|
|
408
408
|
parsedUri.rkey,
|
|
409
409
|
).toString()
|
|
410
410
|
const labels = [
|
|
411
|
-
...(state.labels?.
|
|
411
|
+
...(state.labels?.getBySubject(uri) ?? []),
|
|
412
412
|
...this.selfLabels({
|
|
413
413
|
uri,
|
|
414
414
|
cid: post.cid,
|
|
@@ -886,7 +886,7 @@ export class Views {
|
|
|
886
886
|
recordInfo = state.follows?.get(notif.uri)
|
|
887
887
|
}
|
|
888
888
|
if (!recordInfo) return
|
|
889
|
-
const labels = state.labels?.
|
|
889
|
+
const labels = state.labels?.getBySubject(notif.uri) ?? []
|
|
890
890
|
const selfLabels = this.selfLabels({
|
|
891
891
|
uri: notif.uri,
|
|
892
892
|
cid: recordInfo.cid,
|
|
@@ -4,6 +4,7 @@ import axios from 'axios'
|
|
|
4
4
|
|
|
5
5
|
describe('label hydration', () => {
|
|
6
6
|
let network: TestNetwork
|
|
7
|
+
let agent: AtpAgent
|
|
7
8
|
let pdsAgent: AtpAgent
|
|
8
9
|
let sc: SeedClient
|
|
9
10
|
|
|
@@ -16,6 +17,7 @@ describe('label hydration', () => {
|
|
|
16
17
|
network = await TestNetwork.create({
|
|
17
18
|
dbPostgresSchema: 'bsky_label_hydration',
|
|
18
19
|
})
|
|
20
|
+
agent = network.bsky.getClient()
|
|
19
21
|
pdsAgent = network.pds.getClient()
|
|
20
22
|
sc = network.getSeedClient()
|
|
21
23
|
await basicSeed(sc)
|
|
@@ -93,6 +95,35 @@ describe('label hydration', () => {
|
|
|
93
95
|
)
|
|
94
96
|
})
|
|
95
97
|
|
|
98
|
+
it('hydrates labels without duplication', async () => {
|
|
99
|
+
AtpAgent.configure({ appLabelers: [alice] })
|
|
100
|
+
pdsAgent.configureLabelersHeader([])
|
|
101
|
+
const res = await pdsAgent.api.app.bsky.actor.getProfiles(
|
|
102
|
+
{ actors: [carol, carol] },
|
|
103
|
+
{ headers: sc.getHeaders(bob) },
|
|
104
|
+
)
|
|
105
|
+
const { labels = [] } = res.data.profiles[0]
|
|
106
|
+
expect(labels.map((l) => ({ val: l.val, src: l.src }))).toEqual([
|
|
107
|
+
{ src: alice, val: 'spam' },
|
|
108
|
+
])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('does not hydrate labels from takendown labeler', async () => {
|
|
112
|
+
AtpAgent.configure({ appLabelers: [alice, sc.dids.dan] })
|
|
113
|
+
pdsAgent.configureLabelersHeader([])
|
|
114
|
+
await network.bsky.ctx.dataplane.takedownActor({ did: alice })
|
|
115
|
+
const res = await pdsAgent.api.app.bsky.actor.getProfile(
|
|
116
|
+
{ actor: carol },
|
|
117
|
+
{ headers: sc.getHeaders(bob) },
|
|
118
|
+
)
|
|
119
|
+
const { labels = [] } = res.data
|
|
120
|
+
expect(labels).toEqual([])
|
|
121
|
+
expect(res.headers['atproto-content-labelers']).toEqual(
|
|
122
|
+
`${sc.dids.dan};redact`, // does not include alice
|
|
123
|
+
)
|
|
124
|
+
await network.bsky.ctx.dataplane.untakedownActor({ did: alice })
|
|
125
|
+
})
|
|
126
|
+
|
|
96
127
|
it('hydrates labels onto list views.', async () => {
|
|
97
128
|
AtpAgent.configure({ appLabelers: [labelerDid] })
|
|
98
129
|
pdsAgent.configureLabelersHeader([])
|
|
@@ -136,10 +136,7 @@ describe('labeler service views', () => {
|
|
|
136
136
|
})
|
|
137
137
|
|
|
138
138
|
it('blocked by labeler takedown', async () => {
|
|
139
|
-
await network.bsky.ctx.dataplane.
|
|
140
|
-
recordUri: aliceService.uriStr,
|
|
141
|
-
})
|
|
142
|
-
|
|
139
|
+
await network.bsky.ctx.dataplane.takedownActor({ did: alice })
|
|
143
140
|
const res = await agent.api.app.bsky.labeler.getServices(
|
|
144
141
|
{ dids: [alice, bob] },
|
|
145
142
|
{ headers: await network.serviceHeaders(bob) },
|
|
@@ -149,8 +146,6 @@ describe('labeler service views', () => {
|
|
|
149
146
|
expect(res.data.views[0].creator.did).toEqual(bob)
|
|
150
147
|
|
|
151
148
|
// Cleanup
|
|
152
|
-
await network.bsky.ctx.dataplane.
|
|
153
|
-
recordUri: aliceService.uriStr,
|
|
154
|
-
})
|
|
149
|
+
await network.bsky.ctx.dataplane.untakedownActor({ did: alice })
|
|
155
150
|
})
|
|
156
151
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import AtpAgent from '@atproto/api'
|
|
2
2
|
import { TestNetwork, SeedClient, basicSeed, RecordRef } from '@atproto/dev-env'
|
|
3
|
+
import { ids } from '../../src/lexicon/lexicons'
|
|
3
4
|
|
|
4
5
|
describe('bsky takedown labels', () => {
|
|
5
6
|
let network: TestNetwork
|
|
@@ -40,6 +41,48 @@ describe('bsky takedown labels', () => {
|
|
|
40
41
|
'carol generator',
|
|
41
42
|
)
|
|
42
43
|
|
|
44
|
+
// labelers
|
|
45
|
+
await sc.createAccount('labeler1', {
|
|
46
|
+
email: 'lab1@test.com',
|
|
47
|
+
handle: 'lab1.test',
|
|
48
|
+
password: 'lab1',
|
|
49
|
+
})
|
|
50
|
+
await sc.agent.api.com.atproto.repo.createRecord(
|
|
51
|
+
{
|
|
52
|
+
repo: sc.dids.labeler1,
|
|
53
|
+
collection: ids.AppBskyLabelerService,
|
|
54
|
+
rkey: 'self',
|
|
55
|
+
record: {
|
|
56
|
+
policies: { labelValues: ['spam'] },
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
headers: sc.getHeaders(sc.dids.labeler1),
|
|
62
|
+
encoding: 'application/json',
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
await sc.createAccount('labeler2', {
|
|
66
|
+
email: 'lab2@test.com',
|
|
67
|
+
handle: 'lab2.test',
|
|
68
|
+
password: 'lab2',
|
|
69
|
+
})
|
|
70
|
+
await sc.agent.api.com.atproto.repo.createRecord(
|
|
71
|
+
{
|
|
72
|
+
repo: sc.dids.labeler2,
|
|
73
|
+
collection: ids.AppBskyLabelerService,
|
|
74
|
+
rkey: 'self',
|
|
75
|
+
record: {
|
|
76
|
+
policies: { labelValues: ['spam'] },
|
|
77
|
+
createdAt: new Date().toISOString(),
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
headers: sc.getHeaders(sc.dids.labeler2),
|
|
82
|
+
encoding: 'application/json',
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
|
|
43
86
|
await network.processAll()
|
|
44
87
|
|
|
45
88
|
takendownSubjects = [
|
|
@@ -47,6 +90,7 @@ describe('bsky takedown labels', () => {
|
|
|
47
90
|
sc.dids.carol,
|
|
48
91
|
aliceListRef.uriStr,
|
|
49
92
|
aliceGenRef.uriStr,
|
|
93
|
+
sc.dids.labeler1,
|
|
50
94
|
]
|
|
51
95
|
const src = network.ozone.ctx.cfg.service.did
|
|
52
96
|
const cts = new Date().toISOString()
|
|
@@ -123,6 +167,14 @@ describe('bsky takedown labels', () => {
|
|
|
123
167
|
expect(res.data.feeds.at(0)?.uri).toEqual(bobGenRef.uriStr)
|
|
124
168
|
})
|
|
125
169
|
|
|
170
|
+
it('takesdown labelers', async () => {
|
|
171
|
+
const res = await agent.api.app.bsky.labeler.getServices({
|
|
172
|
+
dids: [sc.dids.labeler1, sc.dids.labeler2],
|
|
173
|
+
})
|
|
174
|
+
expect(res.data.views.length).toBe(1)
|
|
175
|
+
expect(res.data.views[0].creator?.['did']).toBe(sc.dids.labeler2)
|
|
176
|
+
})
|
|
177
|
+
|
|
126
178
|
it('only applies if the relevant labeler is configured', async () => {
|
|
127
179
|
AtpAgent.configure({ appLabelers: ['did:web:example.com'] })
|
|
128
180
|
const res = await agent.api.app.bsky.actor.getProfile({
|