Skip to content

Commit

Permalink
Label expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelbsky committed Feb 8, 2025
1 parent f90eedc commit 49ff8c5
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Kysely } from 'kysely'

export async function up(db: Kysely<unknown>): Promise<void> {
await db.schema.alterTable('label').addColumn('exp', 'varchar').execute()
}

export async function down(db: Kysely<unknown>): Promise<void> {
await db.schema.alterTable('label').dropColumn('exp').execute()
}
1 change: 1 addition & 0 deletions packages/bsky/src/data-plane/server/db/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ export * as _20240829T211238293Z from './20240829T211238293Z-simplify-actor-sync
export * as _20240831T134810923Z from './20240831T134810923Z-pinned-posts'
export * as _20241114T153108102Z from './20241114T153108102Z-add-starter-packs-name'
export * as _20250116T222618297Z from './20250116T222618297Z-post-embed-video'
export * as _20250207T174822012Z from './20250207T174822012Z-add-label-exp'
1 change: 1 addition & 0 deletions packages/bsky/src/data-plane/server/db/tables/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface Label {
val: string
neg: boolean
cts: string
exp: string | null
}

export type PartialDB = { [tableName]: Label }
7 changes: 6 additions & 1 deletion packages/bsky/src/data-plane/server/routes/labels.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ServiceImpl } from '@connectrpc/connect'
import { Selectable } from 'kysely'
import { Selectable, sql } from 'kysely'
import * as ui8 from 'uint8arrays'
import { noUndefinedVals } from '@atproto/common'
import { Service } from '../../../proto/bsky_connect'
Expand All @@ -14,10 +14,14 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
if (subjects.length === 0 || issuers.length === 0) {
return { labels: [] }
}

const res: LabelRow[] = await db.db
.selectFrom('label')
.where('uri', 'in', subjects)
.where('src', 'in', issuers)
.where((qb) =>
qb.where('exp', 'is', null).orWhere(sql`exp::timestamp > now()`),
)
.selectAll()
.execute()

Expand All @@ -34,6 +38,7 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
return labelsForSub.map((l) => {
const formatted = noUndefinedVals({
...l,
exp: l.exp === null ? undefined : l.exp,
cid: l.cid === '' ? undefined : l.cid,
neg: l.neg === true ? true : undefined,
})
Expand Down
64 changes: 56 additions & 8 deletions packages/bsky/tests/label-hydration.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import assert from 'node:assert'
import { AtpAgent } from '@atproto/api'
import { MINUTE } from '@atproto/common'
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'

describe('label hydration', () => {
Expand All @@ -10,6 +12,7 @@ describe('label hydration', () => {
let bob: string
let carol: string
let labelerDid: string
let labeler2Did: string

beforeAll(async () => {
network = await TestNetwork.create({
Expand All @@ -22,6 +25,7 @@ describe('label hydration', () => {
bob = sc.dids.bob
carol = sc.dids.carol
labelerDid = network.bsky.ctx.cfg.labelsFromIssuerDids[0]
labeler2Did = network.bsky.ctx.cfg.labelsFromIssuerDids[1]
await createLabel({ src: alice, uri: carol, cid: '', val: 'spam' })
await createLabel({ src: bob, uri: carol, cid: '', val: 'impersonation' })
await createLabel({
Expand All @@ -30,6 +34,20 @@ describe('label hydration', () => {
cid: '',
val: 'misleading',
})
await createLabel({
src: labeler2Did,
uri: carol,
cid: '',
val: 'expired',
exp: new Date(Date.now() - MINUTE).toISOString(),
})
await createLabel({
src: labeler2Did,
uri: carol,
cid: '',
val: 'not-expired',
exp: new Date(Date.now() + MINUTE).toISOString(),
})
await network.processAll()
})

Expand All @@ -39,17 +57,33 @@ describe('label hydration', () => {

it('hydrates labels based on a supplied labeler header', async () => {
AtpAgent.configure({ appLabelers: [alice] })
pdsAgent.configureLabelers([])
pdsAgent.configureLabelers([labeler2Did])
const res = await pdsAgent.api.app.bsky.actor.getProfile(
{ actor: carol },
{
headers: sc.getHeaders(bob),
},
)
expect(res.data.labels?.length).toBe(1)
expect(res.data.labels?.[0].src).toBe(alice)
expect(res.data.labels?.[0].val).toBe('spam')
expect(res.headers['atproto-content-labelers']).toEqual(`${alice};redact`)
expect(res.data.labels?.length).toBe(2)
assert(res.data.labels)

const sortedLabels = res.data.labels.sort((a, b) =>
a.src.localeCompare(b.src),
)
const sortedExpected = [
{ src: labeler2Did, val: 'not-expired' },
{ src: alice, val: 'spam' },
].sort((a, b) => a.src.localeCompare(b.src))

expect(sortedLabels[0].src).toBe(sortedExpected[0].src)
expect(sortedLabels[0].val).toBe(sortedExpected[0].val)

expect(sortedLabels[1].src).toBe(sortedExpected[1].src)
expect(sortedLabels[1].val).toBe(sortedExpected[1].val)

expect(res.headers['atproto-content-labelers']).toEqual(
`${alice};redact,${labeler2Did}`,
)
})

it('hydrates labels based on multiple a supplied labelers', async () => {
Expand Down Expand Up @@ -88,9 +122,21 @@ describe('label hydration', () => {
{ headers: sc.getHeaders(bob) },
)
const data = await res.json()
expect(data.labels?.length).toBe(1)
expect(data.labels?.[0].src).toBe(labelerDid)
expect(data.labels?.[0].val).toBe('misleading')

expect(data.labels?.length).toBe(2)
assert(data.labels)

const sortedLabels = data.labels.sort((a, b) => a.src.localeCompare(b.src))
const sortedExpected = [
{ src: labeler2Did, val: 'not-expired' },
{ src: labelerDid, val: 'misleading' },
].sort((a, b) => a.src.localeCompare(b.src))

expect(sortedLabels[0].src).toBe(sortedExpected[0].src)
expect(sortedLabels[0].val).toBe(sortedExpected[0].val)

expect(sortedLabels[1].src).toBe(sortedExpected[1].src)
expect(sortedLabels[1].val).toBe(sortedExpected[1].val)

expect(res.headers.get('atproto-content-labelers')).toEqual(
network.bsky.ctx.cfg.labelsFromIssuerDids
Expand Down Expand Up @@ -181,6 +227,7 @@ describe('label hydration', () => {
uri: string
cid: string
val: string
exp?: string
}) => {
await network.bsky.db.db
.insertInto('label')
Expand All @@ -189,6 +236,7 @@ describe('label hydration', () => {
cid: opts.cid,
val: opts.val,
cts: new Date().toISOString(),
exp: opts.exp ?? null,
neg: false,
src: opts.src ?? labelerDid,
})
Expand Down
1 change: 1 addition & 0 deletions packages/bsky/tests/query-labels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ describe('label hydration', () => {
cid: opts.cid,
val: opts.val,
cts: new Date().toISOString(),
exp: null,
neg: false,
src: opts.src ?? labelerDid,
})
Expand Down
1 change: 1 addition & 0 deletions packages/bsky/tests/views/labels-needs-review.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe('bsky needs-review labels', () => {
uri: sc.dids.geoff,
cid: '',
val: 'needs-review',
exp: null,
neg: false,
cts: new Date().toISOString(),
})
Expand Down
1 change: 1 addition & 0 deletions packages/bsky/tests/views/labels-takedown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ describe('bsky takedown labels', () => {
uri,
cid: '',
val: '!takedown',
exp: null,
neg: false,
cts,
}))
Expand Down
1 change: 1 addition & 0 deletions packages/bsky/tests/views/timeline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ const createLabel = async (
cid: opts.cid,
val: opts.val,
cts: new Date().toISOString(),
exp: null,
neg: false,
src: EXAMPLE_LABELER,
})
Expand Down

0 comments on commit 49ff8c5

Please sign in to comment.