Labeler takedowns (alternate) ()

* build hydration ctx in each handler

* test hydration w/ takendown labeler

* tidy

* handle and test labeler takedowns by label

* appview/ozone: fix admin repo search
This commit is contained in:
devin ivy 2024-03-15 10:59:13 -04:00 committed by GitHub
parent 506614fb7c
commit 011aa22b84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 237 additions and 95 deletions

@ -18,7 +18,11 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ auth, params, req }) => {
const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer, includeTakedowns }
const hydrateCtx = await ctx.hydrator.createContext({
labelers,
viewer,
includeTakedowns,
})
const result = await getProfile({ ...params, hydrateCtx }, ctx)
@ -29,7 +33,7 @@ export default function (server: Server, ctx: AppContext) {
body: result,
headers: resHeaders({
repoRev,
labelers,
labelers: hydrateCtx.labelers,
}),
}
},
@ -54,10 +58,10 @@ const hydration = async (input: {
skeleton: SkeletonState
}) => {
const { ctx, params, skeleton } = input
return ctx.hydrator.hydrateProfilesDetailed([skeleton.did], {
...params.hydrateCtx,
includeTakedowns: true,
})
return ctx.hydrator.hydrateProfilesDetailed(
[skeleton.did],
params.hydrateCtx.copy({ includeTakedowns: true }),
)
}
const presentation = (input: {

@ -18,7 +18,7 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ auth, params, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { viewer, labelers }
const hydrateCtx = await ctx.hydrator.createContext({ viewer, labelers })
const result = await getProfile({ ...params, hydrateCtx }, ctx)
@ -29,7 +29,7 @@ export default function (server: Server, ctx: AppContext) {
body: result,
headers: resHeaders({
repoRev,
labelers,
labelers: hydrateCtx.labelers,
}),
}
},

@ -25,13 +25,13 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { viewer, labelers }
const hydrateCtx = await ctx.hydrator.createContext({ viewer, labelers })
const result = await getSuggestions({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -26,14 +26,18 @@ export default function (server: Server, ctx: AppContext) {
server.app.bsky.actor.searchActors({
auth: ctx.authVerifier.standardOptional,
handler: async ({ auth, params, req }) => {
const viewer = auth.credentials.iss
const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { viewer, labelers }
const hydrateCtx = await ctx.hydrator.createContext({
viewer,
labelers,
includeTakedowns,
})
const results = await searchActors({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: results,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -28,7 +28,7 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const results = await searchActorsTypeahead(
{ ...params, hydrateCtx },
ctx,
@ -36,7 +36,7 @@ export default function (server: Server, ctx: AppContext) {
return {
encoding: 'application/json',
body: results,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -26,12 +26,12 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ auth, params, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getActorFeeds({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -28,7 +28,7 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getActorLikes({ ...params, hydrateCtx }, ctx)
@ -39,7 +39,7 @@ export default function (server: Server, ctx: AppContext) {
body: result,
headers: resHeaders({
repoRev,
labelers,
labelers: hydrateCtx.labelers,
}),
}
},

@ -30,7 +30,11 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer, includeTakedowns }
const hydrateCtx = await ctx.hydrator.createContext({
labelers,
viewer,
includeTakedowns,
})
const result = await getAuthorFeed({ ...params, hydrateCtx }, ctx)
@ -41,7 +45,7 @@ export default function (server: Server, ctx: AppContext) {
body: result,
headers: resHeaders({
repoRev,
labelers,
labelers: hydrateCtx.labelers,
}),
}
},

@ -42,7 +42,7 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const headers = noUndefinedVals({
authorization: req.headers['authorization'],
'accept-language': req.headers['accept-language'],
@ -60,7 +60,7 @@ export default function (server: Server, ctx: AppContext) {
body: result,
headers: {
...(feedResHeaders ?? {}),
...resHeaders({ labelers }),
...resHeaders({ labelers: hydrateCtx.labelers }),
'server-timing': serverTimingHeader([timerSkele, timerHydr]),
},
}

@ -17,11 +17,8 @@ export default function (server: Server, ctx: AppContext) {
const { feed } = params
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydration = await ctx.hydrator.hydrateFeedGens([feed], {
viewer,
labelers,
})
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const hydration = await ctx.hydrator.hydrateFeedGens([feed], hydrateCtx)
const feedInfo = hydration.feedgens?.get(feed)
if (!feedInfo) {
throw new InvalidRequestError('could not find feed')
@ -64,7 +61,7 @@ export default function (server: Server, ctx: AppContext) {
isOnline: true,
isValid: true,
},
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -23,12 +23,12 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const view = await getFeedGenerators({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: view,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -21,13 +21,13 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getLikes({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -26,7 +26,7 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getListFeed({ ...params, hydrateCtx }, ctx)
@ -35,7 +35,7 @@ export default function (server: Server, ctx: AppContext) {
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers, repoRev }),
headers: resHeaders({ labelers: hydrateCtx.labelers, repoRev }),
}
},
})

@ -30,7 +30,7 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req, res }) => {
const { viewer } = ctx.authVerifier.parseCreds(auth)
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
let result: OutputSchema
try {
@ -50,7 +50,7 @@ export default function (server: Server, ctx: AppContext) {
body: result,
headers: resHeaders({
repoRev,
labelers,
labelers: hydrateCtx.labelers,
}),
}
},

@ -19,14 +19,14 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const results = await getPosts({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: results,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -25,13 +25,13 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getRepostedBy({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -18,10 +18,8 @@ export default function (server: Server, ctx: AppContext) {
cursor: params.cursor,
})
const uris = suggestedRes.uris
const hydration = await ctx.hydrator.hydrateFeedGens(uris, {
labelers,
viewer,
})
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const hydration = await ctx.hydrator.hydrateFeedGens(uris, hydrateCtx)
const feedViews = mapDefined(uris, (uri) =>
ctx.views.feedGenerator(uri, hydration),
)
@ -32,7 +30,7 @@ export default function (server: Server, ctx: AppContext) {
feeds: feedViews,
cursor: parseString(suggestedRes.cursor),
},
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -26,16 +26,19 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getTimeline({ ...params, hydrateCtx }, ctx)
const result = await getTimeline(
{ ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
ctx,
)
const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers, repoRev }),
headers: resHeaders({ labelers: hydrateCtx.labelers, repoRev }),
}
},
})

@ -29,12 +29,12 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ auth, params, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const results = await searchPosts({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: results,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -20,12 +20,15 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const result = await getBlocks({ ...params, hydrateCtx }, ctx)
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getBlocks(
{ ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
ctx,
)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -31,14 +31,18 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer, includeTakedowns }
const hydrateCtx = await ctx.hydrator.createContext({
labelers,
viewer,
includeTakedowns,
})
const result = await getFollowers({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -25,7 +25,11 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer, includeTakedowns }
const hydrateCtx = await ctx.hydrator.createContext({
labelers,
viewer,
includeTakedowns,
})
// @TODO ensure canViewTakedowns gets threaded through and applied properly
const result = await getFollows({ ...params, hydrateCtx }, ctx)
@ -33,7 +37,7 @@ export default function (server: Server, ctx: AppContext) {
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -26,12 +26,12 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getList({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -25,12 +25,15 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const result = await getListBlocks({ ...params, hydrateCtx }, ctx)
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getListBlocks(
{ ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
ctx,
)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -25,12 +25,15 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const result = await getListMutes({ ...params, hydrateCtx }, ctx)
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getListMutes(
{ ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
ctx,
)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -20,13 +20,13 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getLists({ ...params, hydrateCtx }, ctx)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -20,12 +20,15 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const result = await getMutes({ ...params, hydrateCtx }, ctx)
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getMutes(
{ ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
ctx,
)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -26,15 +26,15 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ auth, params, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await getSuggestedFollowsByActor(
{ ...params, hydrateCtx },
{ ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
ctx,
)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -10,11 +10,11 @@ export default function (server: Server, ctx: AppContext) {
const { dids, detailed } = params
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydration = await ctx.hydrator.hydrateLabelers(dids, {
const hydrateCtx = await ctx.hydrator.createContext({
viewer,
labelers,
})
const hydration = await ctx.hydrator.hydrateLabelers(dids, hydrateCtx)
const views = mapDefined(dids, (did) => {
if (detailed) {
@ -39,7 +39,7 @@ export default function (server: Server, ctx: AppContext) {
body: {
views,
},
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -28,12 +28,15 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ params, auth, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { labelers, viewer }
const result = await listNotifications({ ...params, hydrateCtx }, ctx)
const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
const result = await listNotifications(
{ ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
ctx,
)
return {
encoding: 'application/json',
body: result,
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -13,7 +13,7 @@ export default function (server: Server, ctx: AppContext) {
handler: async ({ auth, params, req }) => {
const viewer = auth.credentials.iss
const labelers = ctx.reqLabelers(req)
const hydrateCtx = { viewer, labelers }
const hydrateCtx = await ctx.hydrator.createContext({ viewer, labelers })
if (clearlyBadCursor(params.cursor)) {
return {
@ -53,7 +53,7 @@ export default function (server: Server, ctx: AppContext) {
feeds: feedViews,
cursor,
},
headers: resHeaders({ labelers }),
headers: resHeaders({ labelers: hydrateCtx.labelers }),
}
},
})

@ -53,7 +53,17 @@ import {
} from './feed'
import { ParsedLabelers } from '../util'
export type HydrateCtx = {
export class HydrateCtx {
labelers = this.vals.labelers
viewer = this.vals.viewer
includeTakedowns = this.vals.includeTakedowns
constructor(private vals: HydrateCtxVals) {}
copy<V extends Partial<HydrateCtxVals>>(vals?: V): HydrateCtx & V {
return new HydrateCtx({ ...this.vals, ...vals }) as HydrateCtx & V
}
}
export type HydrateCtxVals = {
labelers: ParsedLabelers
viewer: string | null
includeTakedowns?: boolean
@ -97,12 +107,17 @@ export class Hydrator {
feed: FeedHydrator
graph: GraphHydrator
label: LabelHydrator
serviceLabelers: Set<string>
constructor(public dataplane: DataPlaneClient) {
constructor(
public dataplane: DataPlaneClient,
serviceLabelers: string[] = [],
) {
this.actor = new ActorHydrator(dataplane)
this.feed = new FeedHydrator(dataplane)
this.graph = new GraphHydrator(dataplane)
this.label = new LabelHydrator(dataplane)
this.serviceLabelers = new Set(serviceLabelers)
}
// app.bsky.actor.defs#profileView
@ -555,13 +570,14 @@ export class Hydrator {
): Promise<HydrationState> {
const [labelers, labelerAggs, labelerViewers, profileState] =
await Promise.all([
this.label.getLabelers(dids),
this.label.getLabelers(dids, ctx.includeTakedowns),
this.label.getLabelerAggregates(dids),
ctx.viewer
? this.label.getLabelerViewerStates(dids, ctx.viewer)
: undefined,
this.hydrateProfiles(dids.map(didFromUri), ctx),
this.hydrateProfiles(dids, ctx),
])
actionTakedownLabels(dids, labelers, profileState.labels ?? new Labels())
return mergeStates(profileState, {
labelers,
labelerAggs,
@ -639,6 +655,30 @@ export class Hydrator {
}
}
}
async createContext(vals: HydrateCtxVals) {
// ensures we're only apply labelers that exist and are not taken down
const labelers = vals.labelers.dids
const nonServiceLabelers = labelers.filter(
(did) => !this.serviceLabelers.has(did),
)
const labelerActors = await this.actor.getActors(
nonServiceLabelers,
vals.includeTakedowns,
)
const availableDids = labelers.filter(
(did) => this.serviceLabelers.has(did) || !!labelerActors.get(did),
)
const availableLabelers = {
dids: availableDids,
redact: vals.labelers.redact,
}
return new HydrateCtx({
labelers: availableLabelers,
viewer: vals.viewer,
includeTakedowns: vals.includeTakedowns,
})
}
}
const listUrisFromProfileViewer = (item: ProfileViewerState | null) => {

@ -77,7 +77,7 @@ export class BskyAppView {
httpVersion: config.dataplaneHttpVersion,
rejectUnauthorized: !config.dataplaneIgnoreBadTls,
})
const hydrator = new Hydrator(dataplane)
const hydrator = new Hydrator(dataplane, config.labelsFromIssuerDids)
const views = new Views(imgUriBuilder)
const bsyncClient = createBsyncClient({

@ -4,6 +4,7 @@ import axios from 'axios'
describe('label hydration', () => {
let network: TestNetwork
let agent: AtpAgent
let pdsAgent: AtpAgent
let sc: SeedClient
@ -16,6 +17,7 @@ describe('label hydration', () => {
network = await TestNetwork.create({
dbPostgresSchema: 'bsky_label_hydration',
})
agent = network.bsky.getClient()
pdsAgent = network.pds.getClient()
sc = network.getSeedClient()
await basicSeed(sc)
@ -106,6 +108,22 @@ describe('label hydration', () => {
])
})
it('does not hydrate labels from takendown labeler', async () => {
AtpAgent.configure({ appLabelers: [alice, sc.dids.dan] })
pdsAgent.configureLabelersHeader([])
await network.bsky.ctx.dataplane.takedownActor({ did: alice })
const res = await pdsAgent.api.app.bsky.actor.getProfile(
{ actor: carol },
{ headers: sc.getHeaders(bob) },
)
const { labels = [] } = res.data
expect(labels).toEqual([])
expect(res.headers['atproto-content-labelers']).toEqual(
`${sc.dids.dan};redact`, // does not include alice
)
await network.bsky.ctx.dataplane.untakedownActor({ did: alice })
})
it('hydrates labels onto list views.', async () => {
AtpAgent.configure({ appLabelers: [labelerDid] })
pdsAgent.configureLabelersHeader([])

@ -136,10 +136,7 @@ describe('labeler service views', () => {
})
it('blocked by labeler takedown', async () => {
await network.bsky.ctx.dataplane.takedownRecord({
recordUri: aliceService.uriStr,
})
await network.bsky.ctx.dataplane.takedownActor({ did: alice })
const res = await agent.api.app.bsky.labeler.getServices(
{ dids: [alice, bob] },
{ headers: await network.serviceHeaders(bob) },
@ -149,8 +146,6 @@ describe('labeler service views', () => {
expect(res.data.views[0].creator.did).toEqual(bob)
// Cleanup
await network.bsky.ctx.dataplane.untakedownRecord({
recordUri: aliceService.uriStr,
})
await network.bsky.ctx.dataplane.untakedownActor({ did: alice })
})
})

@ -1,5 +1,6 @@
import AtpAgent from '@atproto/api'
import { TestNetwork, SeedClient, basicSeed, RecordRef } from '@atproto/dev-env'
import { ids } from '../../src/lexicon/lexicons'
describe('bsky takedown labels', () => {
let network: TestNetwork
@ -40,6 +41,48 @@ describe('bsky takedown labels', () => {
'carol generator',
)
// labelers
await sc.createAccount('labeler1', {
email: 'lab1@test.com',
handle: 'lab1.test',
password: 'lab1',
})
await sc.agent.api.com.atproto.repo.createRecord(
{
repo: sc.dids.labeler1,
collection: ids.AppBskyLabelerService,
rkey: 'self',
record: {
policies: { labelValues: ['spam'] },
createdAt: new Date().toISOString(),
},
},
{
headers: sc.getHeaders(sc.dids.labeler1),
encoding: 'application/json',
},
)
await sc.createAccount('labeler2', {
email: 'lab2@test.com',
handle: 'lab2.test',
password: 'lab2',
})
await sc.agent.api.com.atproto.repo.createRecord(
{
repo: sc.dids.labeler2,
collection: ids.AppBskyLabelerService,
rkey: 'self',
record: {
policies: { labelValues: ['spam'] },
createdAt: new Date().toISOString(),
},
},
{
headers: sc.getHeaders(sc.dids.labeler2),
encoding: 'application/json',
},
)
await network.processAll()
takendownSubjects = [
@ -47,6 +90,7 @@ describe('bsky takedown labels', () => {
sc.dids.carol,
aliceListRef.uriStr,
aliceGenRef.uriStr,
sc.dids.labeler1,
]
const src = network.ozone.ctx.cfg.service.did
const cts = new Date().toISOString()
@ -123,6 +167,14 @@ describe('bsky takedown labels', () => {
expect(res.data.feeds.at(0)?.uri).toEqual(bobGenRef.uriStr)
})
it('takesdown labelers', async () => {
const res = await agent.api.app.bsky.labeler.getServices({
dids: [sc.dids.labeler1, sc.dids.labeler2],
})
expect(res.data.views.length).toBe(1)
expect(res.data.views[0].creator?.['did']).toBe(sc.dids.labeler2)
})
it('only applies if the relevant labeler is configured', async () => {
AtpAgent.configure({ appLabelers: ['did:web:example.com'] })
const res = await agent.api.app.bsky.actor.getProfile({

@ -23,7 +23,10 @@ export default function (server: Server, ctx: AppContext) {
}
}
const res = await ctx.appviewAgent.api.app.bsky.actor.searchActors(params)
const res = await ctx.appviewAgent.api.app.bsky.actor.searchActors(
params,
await ctx.appviewAuth(),
)
const repoMap = await modService.views.repos(
res.data.actors.map((a) => a.did),
)

@ -38,6 +38,7 @@ describe('admin repo search view', () => {
did: sc.dids['cara-wiegand69.test'],
},
})
await network.ozone.processAll()
})
it('gives relevant results', async () => {