@atproto/ozone 0.0.7 → 0.0.8

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/db/index.js +15 -1
  4. package/dist/db/index.js.map +3 -3
  5. package/dist/db/migrations/20240201T051104136Z-mod-event-blobs.d.ts +3 -0
  6. package/dist/db/migrations/index.d.ts +1 -0
  7. package/dist/db/schema/moderation_event.d.ts +1 -0
  8. package/dist/db/types.d.ts +1 -0
  9. package/dist/index.js +276 -202
  10. package/dist/index.js.map +3 -3
  11. package/dist/lexicon/index.d.ts +0 -2
  12. package/dist/lexicon/lexicons.d.ts +38 -47
  13. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +2 -2
  14. package/dist/lexicon/types/com/atproto/admin/queryModerationEvents.d.ts +7 -0
  15. package/dist/mod-service/index.d.ts +13 -4
  16. package/dist/mod-service/subject.d.ts +3 -0
  17. package/dist/mod-service/types.d.ts +2 -0
  18. package/package.json +5 -5
  19. package/src/api/admin/emitModerationEvent.ts +9 -6
  20. package/src/api/admin/queryModerationEvents.ts +14 -0
  21. package/src/api/moderation/util.ts +1 -0
  22. package/src/api/temp/fetchLabels.ts +36 -21
  23. package/src/context.ts +1 -0
  24. package/src/daemon/context.ts +1 -0
  25. package/src/db/migrations/20240201T051104136Z-mod-event-blobs.ts +15 -0
  26. package/src/db/migrations/index.ts +1 -0
  27. package/src/db/schema/moderation_event.ts +1 -0
  28. package/src/db/types.ts +6 -1
  29. package/src/lexicon/index.ts +0 -12
  30. package/src/lexicon/lexicons.ts +43 -50
  31. package/src/lexicon/types/com/atproto/admin/defs.ts +2 -0
  32. package/src/lexicon/types/com/atproto/admin/queryModerationEvents.ts +13 -0
  33. package/src/mod-service/index.ts +142 -64
  34. package/src/mod-service/status.ts +3 -2
  35. package/src/mod-service/subject.ts +9 -2
  36. package/src/mod-service/types.ts +4 -0
  37. package/src/mod-service/views.ts +1 -1
  38. package/tests/__snapshots__/get-record.test.ts.snap +16 -0
  39. package/tests/__snapshots__/get-repo.test.ts.snap +9 -1
  40. package/tests/moderation-appeals.test.ts +1 -1
  41. package/tests/moderation-events.test.ts +161 -8
  42. package/tests/moderation-statuses.test.ts +55 -0
  43. package/tests/moderation.test.ts +133 -34
  44. package/dist/lexicon/types/app/bsky/unspecced/getTimelineSkeleton.d.ts +0 -35
  45. package/src/lexicon/types/app/bsky/unspecced/getTimelineSkeleton.ts +0 -49
@@ -1,7 +1,12 @@
1
+ import assert from 'node:assert'
1
2
  import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env'
2
- import AtpAgent, { ComAtprotoAdminDefs } from '@atproto/api'
3
+ import AtpAgent, {
4
+ ComAtprotoAdminDefs,
5
+ ComAtprotoAdminEmitModerationEvent,
6
+ } from '@atproto/api'
3
7
  import { forSnapshot } from './_util'
4
8
  import {
9
+ REASONAPPEAL,
5
10
  REASONMISLEADING,
6
11
  REASONSPAM,
7
12
  } from '../src/lexicon/types/com/atproto/moderation/defs'
@@ -12,7 +17,9 @@ describe('moderation-events', () => {
12
17
  let pdsAgent: AtpAgent
13
18
  let sc: SeedClient
14
19
 
15
- const emitModerationEvent = async (eventData) => {
20
+ const emitModerationEvent = async (
21
+ eventData: ComAtprotoAdminEmitModerationEvent.InputSchema,
22
+ ) => {
16
23
  return pdsAgent.api.com.atproto.admin.emitModerationEvent(eventData, {
17
24
  encoding: 'application/json',
18
25
  headers: network.bsky.adminAuthHeaders('moderator'),
@@ -201,20 +208,166 @@ describe('moderation-events', () => {
201
208
  expect(reversedEvents.length).toEqual(allEvents.data.events.length)
202
209
  expect(reversedEvents[0].id).toEqual(defaultEvents[4].id)
203
210
  })
211
+
212
+ it('returns report events matching reportType filters', async () => {
213
+ const [spamEvents, misleadingEvents] = await Promise.all([
214
+ queryModerationEvents({
215
+ reportTypes: [REASONSPAM],
216
+ }),
217
+ queryModerationEvents({
218
+ reportTypes: [REASONMISLEADING, REASONAPPEAL],
219
+ }),
220
+ ])
221
+
222
+ expect(misleadingEvents.data.events.length).toEqual(2)
223
+ expect(spamEvents.data.events.length).toEqual(6)
224
+ })
225
+
226
+ it('returns events matching keyword in comment', async () => {
227
+ const [eventsWithX, eventsWithTest, eventsWithComment] =
228
+ await Promise.all([
229
+ queryModerationEvents({
230
+ comment: 'X',
231
+ }),
232
+ queryModerationEvents({
233
+ comment: 'test',
234
+ }),
235
+ queryModerationEvents({
236
+ hasComment: true,
237
+ }),
238
+ ])
239
+
240
+ expect(eventsWithX.data.events.length).toEqual(10)
241
+ expect(eventsWithTest.data.events.length).toEqual(0)
242
+ expect(eventsWithComment.data.events.length).toEqual(12)
243
+ })
244
+
245
+ it('returns events matching filter params for columns', async () => {
246
+ const [negatedLabelEvent, createdLabelEvent] = await Promise.all([
247
+ emitModerationEvent({
248
+ event: {
249
+ $type: 'com.atproto.admin.defs#modEventLabel',
250
+ comment: 'X',
251
+ negateLabelVals: ['L1', 'L2'],
252
+ createLabelVals: [],
253
+ },
254
+ // Report bob's account by alice and vice versa
255
+ subject: {
256
+ $type: 'com.atproto.admin.defs#repoRef',
257
+ did: sc.dids.alice,
258
+ },
259
+ createdBy: sc.dids.bob,
260
+ }),
261
+ emitModerationEvent({
262
+ event: {
263
+ $type: 'com.atproto.admin.defs#modEventLabel',
264
+ comment: 'X',
265
+ createLabelVals: ['L1', 'L2'],
266
+ negateLabelVals: [],
267
+ },
268
+ // Report bob's account by alice and vice versa
269
+ subject: {
270
+ $type: 'com.atproto.admin.defs#repoRef',
271
+ did: sc.dids.bob,
272
+ },
273
+ createdBy: sc.dids.alice,
274
+ }),
275
+ ])
276
+ const [withTwoLabels, withoutTwoLabels, withOneLabel, withoutOneLabel] =
277
+ await Promise.all([
278
+ queryModerationEvents({
279
+ addedLabels: ['L1', 'L3'],
280
+ }),
281
+ queryModerationEvents({
282
+ removedLabels: ['L1', 'L2'],
283
+ }),
284
+ queryModerationEvents({
285
+ addedLabels: ['L1'],
286
+ }),
287
+ queryModerationEvents({
288
+ removedLabels: ['L2'],
289
+ }),
290
+ ])
291
+
292
+ // Verify that when querying for events where 2 different labels were added
293
+ // events where all of the labels from the list was added are returned
294
+ expect(withTwoLabels.data.events.length).toEqual(0)
295
+ expect(negatedLabelEvent.data.id).toEqual(
296
+ withoutTwoLabels.data.events[0].id,
297
+ )
298
+
299
+ expect(createdLabelEvent.data.id).toEqual(withOneLabel.data.events[0].id)
300
+ expect(negatedLabelEvent.data.id).toEqual(
301
+ withoutOneLabel.data.events[0].id,
302
+ )
303
+ })
204
304
  })
205
305
 
206
306
  describe('get event', () => {
207
307
  it('gets an event by specific id', async () => {
208
308
  const { data } = await pdsAgent.api.com.atproto.admin.getModerationEvent(
209
- {
210
- id: 1,
309
+ { id: 1 },
310
+ { headers: network.bsky.adminAuthHeaders('moderator') },
311
+ )
312
+ expect(forSnapshot(data)).toMatchSnapshot()
313
+ })
314
+ })
315
+
316
+ describe('blobs', () => {
317
+ it('are tracked on takedown event', async () => {
318
+ const post = sc.posts[sc.dids.carol][0]
319
+ assert(post.images.length > 1)
320
+ await emitModerationEvent({
321
+ event: {
322
+ $type: 'com.atproto.admin.defs#modEventTakedown',
211
323
  },
212
- {
213
- headers: network.bsky.adminAuthHeaders('moderator'),
324
+ subject: {
325
+ $type: 'com.atproto.repo.strongRef',
326
+ uri: post.ref.uriStr,
327
+ cid: post.ref.cidStr,
214
328
  },
215
- )
329
+ subjectBlobCids: [post.images[0].image.ref.toString()],
330
+ createdBy: sc.dids.alice,
331
+ })
332
+ const { data: result } =
333
+ await pdsAgent.api.com.atproto.admin.queryModerationEvents(
334
+ { subject: post.ref.uriStr },
335
+ { headers: network.ozone.adminAuthHeaders('moderator') },
336
+ )
337
+ expect(result.events[0]).toMatchObject({
338
+ createdBy: sc.dids.alice,
339
+ event: {
340
+ $type: 'com.atproto.admin.defs#modEventTakedown',
341
+ },
342
+ subjectBlobCids: [post.images[0].image.ref.toString()],
343
+ })
344
+ })
216
345
 
217
- expect(forSnapshot(data)).toMatchSnapshot()
346
+ it("are tracked on reverse-takedown event even if they aren't specified", async () => {
347
+ const post = sc.posts[sc.dids.carol][0]
348
+ await emitModerationEvent({
349
+ event: {
350
+ $type: 'com.atproto.admin.defs#modEventReverseTakedown',
351
+ },
352
+ subject: {
353
+ $type: 'com.atproto.repo.strongRef',
354
+ uri: post.ref.uriStr,
355
+ cid: post.ref.cidStr,
356
+ },
357
+ createdBy: sc.dids.alice,
358
+ })
359
+ const { data: result } =
360
+ await pdsAgent.api.com.atproto.admin.queryModerationEvents(
361
+ { subject: post.ref.uriStr },
362
+ { headers: network.ozone.adminAuthHeaders('moderator') },
363
+ )
364
+ expect(result.events[0]).toMatchObject({
365
+ createdBy: sc.dids.alice,
366
+ event: {
367
+ $type: 'com.atproto.admin.defs#modEventReverseTakedown',
368
+ },
369
+ subjectBlobCids: [post.images[0].image.ref.toString()],
370
+ })
218
371
  })
219
372
  })
220
373
  })
@@ -1,3 +1,4 @@
1
+ import assert from 'node:assert'
1
2
  import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env'
2
3
  import AtpAgent, {
3
4
  ComAtprotoAdminDefs,
@@ -141,4 +142,58 @@ describe('moderation-statuses', () => {
141
142
  expect(listReviewedFirst.length).toEqual(list.length)
142
143
  })
143
144
  })
145
+
146
+ describe('blobs', () => {
147
+ it('are tracked on takendown subject', async () => {
148
+ const post = sc.posts[sc.dids.carol][0]
149
+ assert(post.images.length > 1)
150
+ await emitModerationEvent({
151
+ event: {
152
+ $type: 'com.atproto.admin.defs#modEventTakedown',
153
+ },
154
+ subject: {
155
+ $type: 'com.atproto.repo.strongRef',
156
+ uri: post.ref.uriStr,
157
+ cid: post.ref.cidStr,
158
+ },
159
+ subjectBlobCids: [post.images[0].image.ref.toString()],
160
+ createdBy: sc.dids.alice,
161
+ })
162
+ const { data: result } =
163
+ await pdsAgent.api.com.atproto.admin.queryModerationStatuses(
164
+ { subject: post.ref.uriStr },
165
+ { headers: network.ozone.adminAuthHeaders('moderator') },
166
+ )
167
+ expect(result.subjectStatuses.length).toBe(1)
168
+ expect(result.subjectStatuses[0]).toMatchObject({
169
+ takendown: true,
170
+ subjectBlobCids: [post.images[0].image.ref.toString()],
171
+ })
172
+ })
173
+
174
+ it('are tracked on reverse-takendown subject based on previous status', async () => {
175
+ const post = sc.posts[sc.dids.carol][0]
176
+ await emitModerationEvent({
177
+ event: {
178
+ $type: 'com.atproto.admin.defs#modEventReverseTakedown',
179
+ },
180
+ subject: {
181
+ $type: 'com.atproto.repo.strongRef',
182
+ uri: post.ref.uriStr,
183
+ cid: post.ref.cidStr,
184
+ },
185
+ createdBy: sc.dids.alice,
186
+ })
187
+ const { data: result } =
188
+ await pdsAgent.api.com.atproto.admin.queryModerationStatuses(
189
+ { subject: post.ref.uriStr },
190
+ { headers: network.ozone.adminAuthHeaders('moderator') },
191
+ )
192
+ expect(result.subjectStatuses.length).toBe(1)
193
+ expect(result.subjectStatuses[0]).toMatchObject({
194
+ takendown: false,
195
+ subjectBlobCids: [post.images[0].image.ref.toString()],
196
+ })
197
+ })
198
+ })
144
199
  })
@@ -25,6 +25,10 @@ import {
25
25
  } from '../src/lexicon/types/com/atproto/admin/defs'
26
26
  import { EventReverser } from '../src'
27
27
  import { TestOzone } from '@atproto/dev-env/src/ozone'
28
+ import {
29
+ UNSPECCED_TAKEDOWN_BLOBS_LABEL,
30
+ UNSPECCED_TAKEDOWN_LABEL,
31
+ } from '../src/mod-service/types'
28
32
 
29
33
  type BaseCreateReportParams =
30
34
  | { account: string }
@@ -40,6 +44,7 @@ describe('moderation', () => {
40
44
  let network: TestNetwork
41
45
  let ozone: TestOzone
42
46
  let agent: AtpAgent
47
+ let bskyAgent: AtpAgent
43
48
  let pdsAgent: AtpAgent
44
49
  let sc: SeedClient
45
50
 
@@ -139,12 +144,23 @@ describe('moderation', () => {
139
144
  return data
140
145
  }
141
146
 
147
+ const getLabel = async (uri: string, val: string, neg = false) => {
148
+ return ozone.ctx.db.db
149
+ .selectFrom('label')
150
+ .selectAll()
151
+ .where('uri', '=', uri)
152
+ .where('val', '=', val)
153
+ .where('neg', '=', neg)
154
+ .executeTakeFirst()
155
+ }
156
+
142
157
  beforeAll(async () => {
143
158
  network = await TestNetwork.create({
144
159
  dbPostgresSchema: 'ozone_moderation',
145
160
  })
146
161
  ozone = network.ozone
147
162
  agent = network.ozone.getClient()
163
+ bskyAgent = network.bsky.getClient()
148
164
  pdsAgent = network.pds.getClient()
149
165
  sc = network.getSeedClient()
150
166
  await basicSeed(sc)
@@ -444,12 +460,9 @@ describe('moderation', () => {
444
460
  cid: post.cidStr,
445
461
  }
446
462
  const modService = ctx.modService(ctx.db)
447
- await modService.formatAndCreateLabels(
448
- ctx.cfg.service.did,
449
- post.uriStr,
450
- post.cidStr,
451
- { create: ['kittens'] },
452
- )
463
+ await modService.formatAndCreateLabels(post.uriStr, post.cidStr, {
464
+ create: ['kittens'],
465
+ })
453
466
  await emitLabelEvent({
454
467
  negateLabelVals: ['kittens'],
455
468
  createLabelVals: [],
@@ -464,12 +477,9 @@ describe('moderation', () => {
464
477
  })
465
478
  await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['kittens'])
466
479
  // Cleanup
467
- await modService.formatAndCreateLabels(
468
- ctx.cfg.service.did,
469
- post.uriStr,
470
- post.cidStr,
471
- { negate: ['kittens'] },
472
- )
480
+ await modService.formatAndCreateLabels(post.uriStr, post.cidStr, {
481
+ negate: ['kittens'],
482
+ })
473
483
  })
474
484
 
475
485
  it('no-ops when negating an already-negated label and reverses.', async () => {
@@ -497,12 +507,9 @@ describe('moderation', () => {
497
507
  })
498
508
  await expect(getRecordLabels(post.uriStr)).resolves.toEqual(['bears'])
499
509
  // Cleanup
500
- await modService.formatAndCreateLabels(
501
- ctx.cfg.service.did,
502
- post.uriStr,
503
- post.cidStr,
504
- { negate: ['bears'] },
505
- )
510
+ await modService.formatAndCreateLabels(post.uriStr, post.cidStr, {
511
+ negate: ['bears'],
512
+ })
506
513
  })
507
514
 
508
515
  it('creates non-existing labels and reverses.', async () => {
@@ -559,12 +566,9 @@ describe('moderation', () => {
559
566
  it('creates and negates labels on a repo and reverses.', async () => {
560
567
  const { ctx } = ozone
561
568
  const modService = ctx.modService(ctx.db)
562
- await modService.formatAndCreateLabels(
563
- ctx.cfg.service.did,
564
- sc.dids.bob,
565
- null,
566
- { create: ['kittens'] },
567
- )
569
+ await modService.formatAndCreateLabels(sc.dids.bob, null, {
570
+ create: ['kittens'],
571
+ })
568
572
  await emitLabelEvent({
569
573
  createLabelVals: ['puppies'],
570
574
  negateLabelVals: ['kittens'],
@@ -632,34 +636,62 @@ describe('moderation', () => {
632
636
  ).rejects.toThrow('Subject is not taken down')
633
637
  })
634
638
 
635
- it('fans out repo takedowns to pds', async () => {
639
+ it('fans out repo takedowns', async () => {
636
640
  await performTakedown({
637
641
  account: sc.dids.bob,
638
642
  })
639
643
  await ozone.processAll()
640
644
 
641
- const res1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
645
+ const pdsRes1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
642
646
  {
643
647
  did: sc.dids.bob,
644
648
  },
645
649
  { headers: network.pds.adminAuthHeaders() },
646
650
  )
647
- expect(res1.data.takedown?.applied).toBe(true)
651
+ expect(pdsRes1.data.takedown?.applied).toBe(true)
652
+
653
+ const bskyRes1 = await bskyAgent.api.com.atproto.admin.getSubjectStatus(
654
+ {
655
+ did: sc.dids.bob,
656
+ },
657
+ { headers: network.pds.adminAuthHeaders() },
658
+ )
659
+ expect(bskyRes1.data.takedown?.applied).toBe(true)
660
+
661
+ const takedownLabel1 = await getLabel(
662
+ sc.dids.bob,
663
+ UNSPECCED_TAKEDOWN_LABEL,
664
+ )
665
+ expect(takedownLabel1).toBeDefined()
648
666
 
649
667
  // cleanup
650
668
  await performReverseTakedown({ account: sc.dids.bob })
651
669
  await ozone.processAll()
652
670
 
653
- const res2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
671
+ const pdsRes2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
654
672
  {
655
673
  did: sc.dids.bob,
656
674
  },
657
675
  { headers: network.pds.adminAuthHeaders() },
658
676
  )
659
- expect(res2.data.takedown?.applied).toBe(false)
677
+ expect(pdsRes2.data.takedown?.applied).toBe(false)
678
+
679
+ const bskyRes2 = await bskyAgent.api.com.atproto.admin.getSubjectStatus(
680
+ {
681
+ did: sc.dids.bob,
682
+ },
683
+ { headers: network.bsky.adminAuthHeaders() },
684
+ )
685
+ expect(bskyRes2.data.takedown?.applied).toBe(false)
686
+
687
+ const takedownLabel2 = await getLabel(
688
+ sc.dids.bob,
689
+ UNSPECCED_TAKEDOWN_LABEL,
690
+ )
691
+ expect(takedownLabel2).toBeUndefined()
660
692
  })
661
693
 
662
- it('fans out record takedowns to pds', async () => {
694
+ it('fans out record takedowns', async () => {
663
695
  const post = sc.posts[sc.dids.bob][0]
664
696
  const uri = post.ref.uriStr
665
697
  const cid = post.ref.cidStr
@@ -667,21 +699,39 @@ describe('moderation', () => {
667
699
  content: { uri, cid },
668
700
  })
669
701
  await ozone.processAll()
670
- const res1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
702
+
703
+ const pdsRes1 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
671
704
  { uri },
672
705
  { headers: network.pds.adminAuthHeaders() },
673
706
  )
674
- expect(res1.data.takedown?.applied).toBe(true)
707
+ expect(pdsRes1.data.takedown?.applied).toBe(true)
708
+
709
+ const bskyRes1 = await bskyAgent.api.com.atproto.admin.getSubjectStatus(
710
+ { uri },
711
+ { headers: network.bsky.adminAuthHeaders() },
712
+ )
713
+ expect(bskyRes1.data.takedown?.applied).toBe(true)
714
+
715
+ const takedownLabel1 = await getLabel(uri, UNSPECCED_TAKEDOWN_LABEL)
716
+ expect(takedownLabel1).toBeDefined()
675
717
 
676
718
  // cleanup
677
719
  await performReverseTakedown({ content: { uri, cid } })
678
720
  await ozone.processAll()
679
721
 
680
- const res2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
722
+ const pdsRes2 = await pdsAgent.api.com.atproto.admin.getSubjectStatus(
681
723
  { uri },
682
724
  { headers: network.pds.adminAuthHeaders() },
683
725
  )
684
- expect(res2.data.takedown?.applied).toBe(false)
726
+ expect(pdsRes2.data.takedown?.applied).toBe(false)
727
+ const bskyRes2 = await bskyAgent.api.com.atproto.admin.getSubjectStatus(
728
+ { uri },
729
+ { headers: network.bsky.adminAuthHeaders() },
730
+ )
731
+ expect(bskyRes2.data.takedown?.applied).toBe(false)
732
+
733
+ const takedownLabel2 = await getLabel(uri, UNSPECCED_TAKEDOWN_LABEL)
734
+ expect(takedownLabel2).toBeUndefined()
685
735
  })
686
736
 
687
737
  it('allows full moderators to takedown.', async () => {
@@ -732,6 +782,7 @@ describe('moderation', () => {
732
782
  'Must be a full moderator to perform an account takedown',
733
783
  )
734
784
  })
785
+
735
786
  it('automatically reverses actions marked with duration', async () => {
736
787
  await createReport({
737
788
  reasonType: REASONSPAM,
@@ -791,6 +842,22 @@ describe('moderation', () => {
791
842
  })
792
843
  })
793
844
 
845
+ it('serves label when authed', async () => {
846
+ const { data: unauthed } = await agent.api.com.atproto.temp.fetchLabels(
847
+ {},
848
+ )
849
+ expect(unauthed.labels.map((l) => l.val)).not.toContain(
850
+ UNSPECCED_TAKEDOWN_LABEL,
851
+ )
852
+ const { data: authed } = await agent.api.com.atproto.temp.fetchLabels(
853
+ {},
854
+ { headers: network.bsky.adminAuthHeaders() },
855
+ )
856
+ expect(authed.labels.map((l) => l.val)).toContain(
857
+ UNSPECCED_TAKEDOWN_LABEL,
858
+ )
859
+ })
860
+
794
861
  async function emitLabelEvent(
795
862
  opts: Partial<ComAtprotoAdminEmitModerationEvent.InputSchema> & {
796
863
  subject: ComAtprotoAdminEmitModerationEvent.InputSchema['subject']
@@ -924,6 +991,14 @@ describe('moderation', () => {
924
991
  expect(res.data.takedown?.applied).toBe(true)
925
992
  })
926
993
 
994
+ it('creates a takedown blobs label', async () => {
995
+ const label = await getLabel(
996
+ post.ref.uriStr,
997
+ UNSPECCED_TAKEDOWN_BLOBS_LABEL,
998
+ )
999
+ expect(label).toBeDefined()
1000
+ })
1001
+
927
1002
  it('restores blob when action is reversed.', async () => {
928
1003
  await performReverseTakedown({
929
1004
  content: {
@@ -957,5 +1032,29 @@ describe('moderation', () => {
957
1032
  )
958
1033
  expect(res.data.takedown?.applied).toBe(false)
959
1034
  })
1035
+
1036
+ it('serves label when authed', async () => {
1037
+ const { data: unauthed } = await agent.api.com.atproto.temp.fetchLabels(
1038
+ {},
1039
+ )
1040
+ expect(unauthed.labels.map((l) => l.val)).not.toContain(
1041
+ UNSPECCED_TAKEDOWN_BLOBS_LABEL,
1042
+ )
1043
+ const { data: authed } = await agent.api.com.atproto.temp.fetchLabels(
1044
+ {},
1045
+ { headers: network.bsky.adminAuthHeaders() },
1046
+ )
1047
+ expect(authed.labels.map((l) => l.val)).toContain(
1048
+ UNSPECCED_TAKEDOWN_BLOBS_LABEL,
1049
+ )
1050
+ })
1051
+
1052
+ it('negates takedown blobs label on reversal', async () => {
1053
+ const label = await getLabel(
1054
+ post.ref.uriStr,
1055
+ UNSPECCED_TAKEDOWN_BLOBS_LABEL,
1056
+ )
1057
+ expect(label).toBeUndefined()
1058
+ })
960
1059
  })
961
1060
  })
@@ -1,35 +0,0 @@
1
- import express from 'express';
2
- import { HandlerAuth } from '@atproto/xrpc-server';
3
- import * as AppBskyFeedDefs from '../feed/defs';
4
- export interface QueryParams {
5
- limit: number;
6
- cursor?: string;
7
- }
8
- export type InputSchema = undefined;
9
- export interface OutputSchema {
10
- cursor?: string;
11
- feed: AppBskyFeedDefs.SkeletonFeedPost[];
12
- [k: string]: unknown;
13
- }
14
- export type HandlerInput = undefined;
15
- export interface HandlerSuccess {
16
- encoding: 'application/json';
17
- body: OutputSchema;
18
- headers?: {
19
- [key: string]: string;
20
- };
21
- }
22
- export interface HandlerError {
23
- status: number;
24
- message?: string;
25
- error?: 'UnknownFeed';
26
- }
27
- export type HandlerOutput = HandlerError | HandlerSuccess;
28
- export type HandlerReqCtx<HA extends HandlerAuth = never> = {
29
- auth: HA;
30
- params: QueryParams;
31
- input: HandlerInput;
32
- req: express.Request;
33
- res: express.Response;
34
- };
35
- export type Handler<HA extends HandlerAuth = never> = (ctx: HandlerReqCtx<HA>) => Promise<HandlerOutput> | HandlerOutput;
@@ -1,49 +0,0 @@
1
- /**
2
- * GENERATED CODE - DO NOT MODIFY
3
- */
4
- import express from 'express'
5
- import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
- import { lexicons } from '../../../../lexicons'
7
- import { isObj, hasProp } from '../../../../util'
8
- import { CID } from 'multiformats/cid'
9
- import { HandlerAuth } from '@atproto/xrpc-server'
10
- import * as AppBskyFeedDefs from '../feed/defs'
11
-
12
- export interface QueryParams {
13
- limit: number
14
- cursor?: string
15
- }
16
-
17
- export type InputSchema = undefined
18
-
19
- export interface OutputSchema {
20
- cursor?: string
21
- feed: AppBskyFeedDefs.SkeletonFeedPost[]
22
- [k: string]: unknown
23
- }
24
-
25
- export type HandlerInput = undefined
26
-
27
- export interface HandlerSuccess {
28
- encoding: 'application/json'
29
- body: OutputSchema
30
- headers?: { [key: string]: string }
31
- }
32
-
33
- export interface HandlerError {
34
- status: number
35
- message?: string
36
- error?: 'UnknownFeed'
37
- }
38
-
39
- export type HandlerOutput = HandlerError | HandlerSuccess
40
- export type HandlerReqCtx<HA extends HandlerAuth = never> = {
41
- auth: HA
42
- params: QueryParams
43
- input: HandlerInput
44
- req: express.Request
45
- res: express.Response
46
- }
47
- export type Handler<HA extends HandlerAuth = never> = (
48
- ctx: HandlerReqCtx<HA>,
49
- ) => Promise<HandlerOutput> | HandlerOutput