aba664fbdf
* Add new postgate lex, hiddeReplies to threadgate, codegen * Add protobufs * Add to mock dataplane * Add matching postgate method to feed hydration methods * Add to getRecord * Add to HydrationState * Fix typo * Add to mergeStates, fetch embeds in threads * Integrate into embed views * Add test for QPs in threads * Add feed test * Fix naming convention in protos * Add #viewRemoved record view, rename postgate.json * Integrate new view * Filter hidden replies from feeds * Filter out replies at the handler level, do not filter for author feeds * Fix lint * Move hidden reply check to view layer * Reduce, reuse, recycle * Rename to lowercase * Rename layer vars * Add quote gate props to postgate (#2693) * Add quote gate props to postgate * Consistent naming * Fix record structure * Codegen * Show hidden replies in author feed * Allow reposts of hidden replies * Lex and codegen * Add violates_quote_gate to proto * Consistent naming, codegen * Integrate violatesQuotegate and canQuotepost * Remove rules, codegen * Hydrate all postgates for all requested posts * Match other impl * Add test, need to split these out * Format * Hydrate first nested embeds too * Add postgate test suite * Add violatesQuoteGate to dataplane * Ingest and set violatesQuoteGate, return on meta * Return removed embed for quotes that violate gate * Add test * Dedupe URIs before fetching postgates * Update snaps * Snap * Format * Updating naming conventions for postgate-related attributes * Correct naming * Consistency * Proto too * Rename to viewDetached * Codegen * Rename everything * Codegen * Quotes that violate a quote gate can still be quoted themselves * Couple more renames * Snaps * Ensure reply ref is tombstoned for hidden replies * Split out hidden replies tests and create fresh fixture * Hydrate threadgates for reply notifications, filter hidden replies * Remove snap * Add flaky test * Rename violatesEmbeddingRules * Fix flaky test * Only write to db if violatesEmbeddingRules is true * DRY up post uri -> gate uri logic * isThreadgateListRule * Don't share users object between tests * No pascal * Remove default params * Find -> some * canQuotepost -> canEmbed, remove optional * Fix quoteee typo * await follows * Throw in post uri -> gate utils * Ensure fetch threadgates for reply roots * Don't hydrate threadgates twice * DRY up uri -> did parsing * Clean up parsePostgate logic * Format * Revert change * Revert change * Replace a couple more uri->did conversions * Only filter replies from feeds if viewer hid them * Revert, filter out replies that are hidden from feeds * Remove old test * Replace uri->did util * Revert change to unused file * Only validatePostEmbed and check postgates for post records * Ensure notifications aren't generated down a hidden reply chain * Changeset * Cleanup * Fix notification filtering logic * Simplify * Don't notify for invalid embeds * Use new APIs * Add hasPostGate and hasThreadGate flags from dataplane * Only fetch postgates if post has one * Only fetch threadgates if post has one or was deleted * Remove notification filtering * Don't hydrate threadgates for notifications * Move hidden replies in feeds to match block handling * Do no filtering of hidden replies in feeds * Revert "Don't hydrate threadgates for notifications" This reverts commit 1dcec0b239a7b9d6800427b26b8ba3e6a54210f9. * Revert "Remove notification filtering" This reverts commit 1e7069dfd809d1f18e9f05fd1d422e7399aa1bb0. * Filter notifications for OP only * Add additional check to hidden replies test * Move noty filter logic into method handler * Update .changeset/perfect-parrots-appear.md Co-authored-by: devin ivy <devinivy@gmail.com> * Update packages/bsky/tests/seed/postgates.ts Co-authored-by: devin ivy <devinivy@gmail.com> * Another structuredClone * Update packages/bsky/src/hydration/hydrator.ts Co-authored-by: devin ivy <devinivy@gmail.com> * Better comment * Update packages/bsky/src/data-plane/server/indexing/plugins/post.ts Co-authored-by: devin ivy <devinivy@gmail.com> * Regen protos to match dataplane * Update quotes snap to include embeddingDisabled * Clarify usage of post uri -> gate utils --------- Co-authored-by: devin ivy <devinivy@gmail.com>
247 lines
7.1 KiB
TypeScript
247 lines
7.1 KiB
TypeScript
import { TestNetwork, SeedClient } from '@atproto/dev-env'
|
|
import AtpAgent from '@atproto/api'
|
|
|
|
import { ids } from '../../src/lexicon/lexicons'
|
|
import { feedHiddenRepliesSeed, Users } from '../seed/feed-hidden-replies'
|
|
|
|
describe('feed hidden replies', () => {
|
|
let network: TestNetwork
|
|
let agent: AtpAgent
|
|
let pdsAgent: AtpAgent
|
|
let sc: SeedClient
|
|
let users: Users
|
|
|
|
beforeAll(async () => {
|
|
network = await TestNetwork.create({
|
|
dbPostgresSchema: 'bsky_tests_feed_hidden_replies',
|
|
})
|
|
agent = network.bsky.getClient()
|
|
pdsAgent = network.pds.getClient()
|
|
sc = network.getSeedClient()
|
|
|
|
const result = await feedHiddenRepliesSeed(sc)
|
|
users = result.users
|
|
|
|
await network.processAll()
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await network.close()
|
|
})
|
|
|
|
describe(`notifications`, () => {
|
|
it(`[A] -> [B] : B is hidden`, async () => {
|
|
const A = await sc.post(users.poster.did, `A`)
|
|
|
|
await network.processAll()
|
|
|
|
const B = await sc.reply(users.replier.did, A.ref, A.ref, `B`)
|
|
const C = await sc.reply(users.replier.did, A.ref, A.ref, `C`)
|
|
|
|
await pdsAgent.api.app.bsky.feed.threadgate.create(
|
|
{
|
|
repo: A.ref.uri.host,
|
|
rkey: A.ref.uri.rkey,
|
|
},
|
|
{
|
|
post: A.ref.uriStr,
|
|
createdAt: new Date().toISOString(),
|
|
hiddenReplies: [B.ref.uriStr],
|
|
},
|
|
sc.getHeaders(A.ref.uri.host),
|
|
)
|
|
|
|
await network.processAll()
|
|
|
|
const {
|
|
data: { notifications },
|
|
} = await agent.api.app.bsky.notification.listNotifications(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
users.poster.did,
|
|
ids.AppBskyNotificationListNotifications,
|
|
),
|
|
},
|
|
)
|
|
|
|
const notificationB = notifications.find((item) => {
|
|
return item.uri === B.ref.uriStr
|
|
})
|
|
const notificationC = notifications.find((item) => {
|
|
return item.uri === C.ref.uriStr
|
|
})
|
|
|
|
expect(notificationB).toBeUndefined()
|
|
expect(notificationC).toBeDefined()
|
|
})
|
|
|
|
it(`[A] -> [B] -> [C] : B is hidden, C results in no notification for A, notification for B`, async () => {
|
|
const A = await sc.post(users.poster.did, `A`)
|
|
await network.processAll()
|
|
const B = await sc.reply(users.replier.did, A.ref, A.ref, `B`)
|
|
|
|
await pdsAgent.api.app.bsky.feed.threadgate.create(
|
|
{
|
|
repo: A.ref.uri.host,
|
|
rkey: A.ref.uri.rkey,
|
|
},
|
|
{
|
|
post: A.ref.uriStr,
|
|
createdAt: new Date().toISOString(),
|
|
hiddenReplies: [B.ref.uriStr],
|
|
},
|
|
sc.getHeaders(A.ref.uri.host),
|
|
)
|
|
|
|
await network.processAll()
|
|
|
|
const C = await sc.reply(users.viewer.did, A.ref, B.ref, `C`)
|
|
|
|
await network.processAll()
|
|
|
|
const {
|
|
data: { notifications: posterNotifications },
|
|
} = await agent.api.app.bsky.notification.listNotifications(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
users.poster.did,
|
|
ids.AppBskyNotificationListNotifications,
|
|
),
|
|
},
|
|
)
|
|
|
|
const posterNotificationB = posterNotifications.find((item) => {
|
|
return item.uri === B.ref.uriStr
|
|
})
|
|
const posterNotificationC = posterNotifications.find((item) => {
|
|
return item.uri === C.ref.uriStr
|
|
})
|
|
|
|
expect(posterNotificationB).toBeUndefined()
|
|
expect(posterNotificationC).toBeUndefined()
|
|
|
|
const {
|
|
data: { notifications: replierNotifications },
|
|
} = await agent.api.app.bsky.notification.listNotifications(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
users.replier.did,
|
|
ids.AppBskyNotificationListNotifications,
|
|
),
|
|
},
|
|
)
|
|
|
|
const replierNotificationC = replierNotifications.find((item) => {
|
|
return item.uri === C.ref.uriStr
|
|
})
|
|
|
|
expect(replierNotificationC).toBeDefined()
|
|
})
|
|
|
|
it(`[A] -> [B] -> [C] -> [D] : C is hidden, D results in no notification for A or B, notification for C, C exists in B's notifications`, async () => {
|
|
const A = await sc.post(users.poster.did, `A`)
|
|
await network.processAll()
|
|
const B = await sc.reply(users.replier.did, A.ref, A.ref, `B`)
|
|
await network.processAll()
|
|
const C = await sc.reply(users.viewer.did, A.ref, B.ref, `C`)
|
|
await network.processAll()
|
|
|
|
const {
|
|
data: { notifications: posterNotificationsBefore },
|
|
} = await agent.api.app.bsky.notification.listNotifications(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
users.poster.did,
|
|
ids.AppBskyNotificationListNotifications,
|
|
),
|
|
},
|
|
)
|
|
|
|
const posterNotificationCBefore = posterNotificationsBefore.find(
|
|
(item) => {
|
|
return item.uri === C.ref.uriStr
|
|
},
|
|
)
|
|
|
|
expect(posterNotificationCBefore).toBeDefined()
|
|
|
|
await pdsAgent.api.app.bsky.feed.threadgate.create(
|
|
{
|
|
repo: A.ref.uri.host,
|
|
rkey: A.ref.uri.rkey,
|
|
},
|
|
{
|
|
post: A.ref.uriStr,
|
|
createdAt: new Date().toISOString(),
|
|
hiddenReplies: [C.ref.uriStr],
|
|
},
|
|
sc.getHeaders(A.ref.uri.host),
|
|
)
|
|
await network.processAll()
|
|
const D = await sc.reply(users.viewer.did, A.ref, C.ref, `D`)
|
|
await network.processAll()
|
|
|
|
const {
|
|
data: { notifications: posterNotifications },
|
|
} = await agent.api.app.bsky.notification.listNotifications(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
users.poster.did,
|
|
ids.AppBskyNotificationListNotifications,
|
|
),
|
|
},
|
|
)
|
|
|
|
const posterNotificationB = posterNotifications.find((item) => {
|
|
return item.uri === B.ref.uriStr
|
|
})
|
|
const posterNotificationC = posterNotifications.find((item) => {
|
|
return item.uri === C.ref.uriStr
|
|
})
|
|
const posterNotificationD = posterNotifications.find((item) => {
|
|
return item.uri === D.ref.uriStr
|
|
})
|
|
|
|
expect(posterNotificationB).toBeDefined()
|
|
expect(posterNotificationC).toBeUndefined() // hidden bc OP
|
|
expect(posterNotificationD).toBeUndefined() // hidden bc no propogation
|
|
|
|
const {
|
|
data: { notifications: replierNotifications },
|
|
} = await agent.api.app.bsky.notification.listNotifications(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
users.replier.did,
|
|
ids.AppBskyNotificationListNotifications,
|
|
),
|
|
},
|
|
)
|
|
|
|
const replierNotificationC = replierNotifications.find((item) => {
|
|
return item.uri === C.ref.uriStr
|
|
})
|
|
const replierNotificationD = replierNotifications.find((item) => {
|
|
return item.uri === D.ref.uriStr
|
|
})
|
|
|
|
expect(replierNotificationC).toBeDefined() // not hidden bc not OP
|
|
expect(replierNotificationD).toBeUndefined() // hidden bc no propogation
|
|
|
|
await pdsAgent.api.app.bsky.feed.threadgate.delete(
|
|
{
|
|
repo: A.ref.uri.host,
|
|
rkey: A.ref.uri.rkey,
|
|
},
|
|
sc.getHeaders(A.ref.uri.host),
|
|
)
|
|
await network.processAll()
|
|
})
|
|
})
|
|
})
|