Labeler takedowns (alternate) (#2322)
* 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:
parent
506614fb7c
commit
011aa22b84
packages
bsky
src
api/app/bsky
actor
feed
getActorFeeds.tsgetActorLikes.tsgetAuthorFeed.tsgetFeed.tsgetFeedGenerator.tsgetFeedGenerators.tsgetLikes.tsgetListFeed.tsgetPostThread.tsgetPosts.tsgetRepostedBy.tsgetSuggestedFeeds.tsgetTimeline.tssearchPosts.ts
graph
getBlocks.tsgetFollowers.tsgetFollows.tsgetList.tsgetListBlocks.tsgetListMutes.tsgetLists.tsgetMutes.tsgetSuggestedFollowsByActor.ts
labeler
notification
unspecced
hydration
index.tstests
ozone
@ -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 () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user